[
  {
    "path": ".gitignore",
    "content": "test/titanium/build\n"
  },
  {
    "path": "AUTHORS",
    "content": "# Authors ordered by first contribution.\n\nZef Hemel <zef@zef.me>\nFabio Rehm <fgrehm@gmail.com>\nLukas Berns\nRoberto Saccon <rsaccon@gmail.com>\nWilker Lúcio <wilkerlucio@gmail.com>\nBruno Jouhier <bjouhier@gmail.com>\nRobin Wenglewski <robin@wenglewski.de>\nMatthias Hochgatterer <matthias.hochgatterer@gmail.com>\nChris Chua <chris.sirhc@gmail.com>\nMike Smullin <mike@smullindesign.com>\nMasahiro Hayashi <hayashi.masahiro@gmail.com>\nMick Staugaard <mick@staugaard.com>\nShane Tomlinson <set117@gmail.com>\nEugene Ware <eugene.ware@nextharbour.com>\n"
  },
  {
    "path": "CHANGES",
    "content": "Changes\n=======\n\n* Moved all the SQL stuff into persistence.store.sql.js, and WebSQL to\n\tpersistence.store.websql.js. So, to use a WebSQL browser database you need\n\tto include 3 files in your HTML now:\n\n\t\t\t<script src=\"persistence.js\" type=\"application/javascript\"></script>\n\t\t\t<script src=\"persistence.store.sql.js\" type=\"application/javascript\"></script>\n\t\t\t<script src=\"persistence.store.websql.js\" type=\"application/javascript\"></script>\n\n\tThen, instead of using `persistence.connect` use:\n\n\t\t\tpersistence.store.websql.config(persistence, 'dbname', 'My db', 5 * 1024 * 1024);\n\n  For node.js and MySQL:\n\n      var persistence = require('./persistence').persistence;\n      var persistenceStore = require('./persistence.store.mysql');\n\n      persistenceStore.config(persistence, 'localhost', 'somedb', 'user', 'pw');\n      var session = persistenceStore.getSession();\n      ...\n      session.close();\n\n* persistence.db.log is now called persistence.debug\n\nv0.1.1: Last version with only one persistence.js file\n"
  },
  {
    "path": "README.md",
    "content": "persistence.js\n==============\n`persistence.js` is a asynchronous Javascript object-relational\nmapper library. It can be used both in the web browser and on\nthe server using [node.js](http://nodejs.org). It currently\nsupports 4 types of data stores:\n\n* [HTML5 WebSQL database](http://dev.w3.org/html5/webdatabase/), a\n  somewhat controversial part of HTML5 that is supported in Webkit\n  browsers, specifically on mobile devices, including iPhone, Android\n  and Palm's WebOS. \n* [Google Gears](http://gears.google.com), a browser plug-in that adds\n  a number of feature to the browser, including a in-browser database.\n* [MySQL](http://www.mysql.com), using the\n  [node-mysql](http://github.com/felixge/node-mysql), node.js module\n  on the server.\n* In-memory, as a fallback. Keeps the database in memory and is\n  cleaned upon a page refresh (or server restart), unless saved to\n  [localStorage](http://dev.w3.org/html5/webstorage/).\n\nThere is also an experimental support for [Qt 4.7 Declarative UI\nframework\n(QML)](http://doc.trolltech.org/4.7-snapshot/declarativeui.html) which\nis an extension to JavaScript.\n\nFor browser use, `persistence.js` has no dependencies on any other\nframeworks, other than the Google Gears [initialization\nscript](http://code.google.com/apis/gears/gears_init.js), in case you\nwant to enable Gears support.\n\nPlug-ins\n--------\n\nThere are a few `persistence.js` plug-ins available that add functionality:\n\n* `persistence.search.js`, adds simple full-text search capabilities,\n  see `docs/search.md` for more information.\n* `persistence.migrations.js`, supports data migrations (changes to\n  the database schema), see `docs/migrations.md` for more information.\n* `persistence.sync.js`, supports database synchronization with a\n  remote server, see `docs/sync.md` for more information.\n* `jquery.persistence.js`, adds jQuery integration, including \n  jQuery-mobile ajax request interception and re-routing to persistencejs,\n  see `docs/jquery.md` for more information and `demo/jquerymobile` for a \n  simple demo.\n\nA Brief Intro to Async Programming\n----------------------------------\n\nIn browsers, Javascript and the web page's rendering engine share\na single thread. The result of this is that only one thing can happen\nat a time. If a database query would be performed _synchronously_,\nlike in many other programming environments like Java and PHP the\nbrowser would freeze from the moment the query was issued until the\nresults came back. Therefore, many APIs in Javascript are defined as\n_asynchronous_ APIs, which mean that they do not block when an\n\"expensive\" computation is performed, but instead provide the call\nwith a function that will be invoked once the result is known. In the\nmeantime, the browser can perform other duties.\n\nFor instance, a synchronous database call call would look as follows:\n\n    var results = db.query(\"SELECT * FROM Table\");\n    for(...) { ... }\n\nThe execution of the first statement could take half a second, during\nwhich the browser doesn't do anything else. By contrast, the\nasynchronous version looks as follows:\n\n    db.query(\"SELECT * FROM Table\", function(results) {\n      for(...) { ... }\n    });\n\nNote that there will be a delay between the `db.query` call and the\nresult being available and that while the database is processing the\nquery, the execution of the Javascript continues. To make this clear,\nconsider the following program:\n    \n    db.query(\"SELECT * FROM Table\", function(results) {\n      console.log(\"hello\");\n    });\n    console.log(\"world\");\n\nAlthough one could assume this would print \"hello\", followed by\n\"world\", the result will likely be that \"world\" is printed before\n\"hello\", because \"hello\" is only printed when the results from the\nquery are available. This is a tricky thing about asynchronous\nprogramming that a Javascript developer will have to get used to.\n\nUsing persistence.js in the browser\n===================================\n\nBrowser support\n---------------\n\n* Modern webkit browsers (Google Chrome and Safari)\n* Firefox (through Google Gears)\n* Opera\n* Android browser (tested on 1.6 and 2.x)\n* iPhone browser (iPhone OS 3+)\n* Palm WebOS (tested on 1.4.0)\n* Other browsers supporting `localStorage` (e.g. Firefox)\n\n(The following is being worked on:)\nInternet Explorer is likely not supported (untested) because it\nlacks `__defineGetter__` and `__defineSetter__` support, which\n`persistence.js` uses heavily. This may change in IE 9.\n\nSetting up\n----------\n\n* Using `bower`:\n\n```shell\nbower install persistence\n```\n\nAdd a `<script>` to your `index.html`:\n\n`lib/persistence.js` needs to be added, as well as any data stores you want to use. Note that the `mysql` and\n`websql` stores both depend on the `sql` store. A typical setup requires you to add at least\n`lib/persistence.js`, `lib/persistence.store.sql.js` and `lib/persistence.store.websql.js` as follows:\n\n    <script src=\"/bower_components/persistencejs/lib/persistence.js\"></script>\n    <script src=\"/bower_components/persistencejs/lib/persistence.store.sql.js\"></script>\n    <script src=\"/bower_components/persistencejs/lib/persistence.store.websql.js\"></script>\n\nIf you want to use the in-memory store (in combination with\n`localStorage`) you also need the `persistence.store.memory.js`\nincluded.\n\n* Using directly from source:\n\n    git clone git://github.com/zefhemel/persistencejs.git\n\nCopy directories you will need following almost the same instructions above.\n\n\nSetup your database\n-------------------\n\nYou need to explicitly configure the data store you want to use,\nconfiguration of the data store is store-specific. The WebSQL store\n(which includes Google Gears support) is configured as follows:\n\n    persistence.store.websql.config(persistence, 'yourdbname', 'A database description', 5 * 1024 * 1024);\n\nThe first argument is always supposed to be `persistence`. The second\nin your database name (it will create it if it does not already exist,\nthe third is a description for you database, the last argument is the\nmaximum size of your database in bytes (5MB in this example).\n\n## Setting up for Cordova with SQLitePlugin/WebSQL\n\nUse following if you want to use `persistencejs` in a [Cordova](https://cordova.apache.org/) mobile app and you plan to use the [Cordova SQLitePlugin](https://github.com/brodysoft/Cordova-SQLitePlugin):\n\n    persistence.store.cordovasql.config(\n      persistence,\n      'yourdbname',\n      '0.0.1',                // DB version\n      'My database',          // DB display name\n      5 * 1024 * 1024,        // DB size (WebSQL fallback only)\n      0,                      // SQLitePlugin Background processing disabled\n      2                       // DB location (iOS only), 0 (default): Documents, 1: Library, 2: Library/LocalDatabase\n                              //   0: iTunes + iCloud, 1: NO iTunes + iCloud, 2: NO iTunes + NO iCloud\n                              //   More information at https://github.com/litehelpers/Cordova-sqlite-storage#opening-a-database\n    );\n\nFor more information on the SQLitePlugin background processing please refer to the [SQLitePlugin](https://github.com/brodysoft/Cordova-SQLitePlugin) readme.\n\nThe Cordova support in `persistencejs` will try to work with the [SQLitePlugin](https://github.com/brodysoft/Cordova-SQLitePlugin) if it is loaded; if not it will automatically fall back to [WebSQL](http://docs.phonegap.com/en/edge/cordova_storage_storage.md.html#Storage).\n\nPlease note that to use Cordova store, you must use the master branch, because it is not included up to release v0.3.0.\n\nThe in-memory store\n---------------------------------------\n\nThe in-memory store is offered as a fallback for browsers that do not\nsupport any of the other supported stores (e.g. WebSQL or Gears). In\nprincipal, it only keeps data in memory, which means that navigating\naway from the page (including a reload or tab close) will result in\nthe loss of all data.\n\nA way around this is using the `persistence.saveToLocalStorage` and\n`persistence.loadFromLocalStorage` functions that can save the entire\ndatabase to the [localStorage](http://dev.w3.org/html5/webstorage/), which\nis persisted indefinitely (similar to WebSQL).\n\nIf you're going to use the in-memory store, you can configure it as follows:\n\n    persistence.store.memory.config(persistence);\n\nThen, if desired, current data can be loaded from the localStorage using:\n\n    persistence.loadFromLocalStorage(function() {\n      alert(\"All data loaded!\");\n    });\n\nAnd saved using:\n\n    persistence.saveToLocalStorage(function() {\n      alert(\"All data saved!\");\n    });\n\nDrawbacks of the in-memory store:\n\n* Performance: All actions that are typically performed by a database\n  (sorting, filtering), are now all performed in-memory using\n  Javascript.\n* Limited database size: Loading and saving requires serialization of\n  all data from and to JSON, which gets more expensive as your dataset\n  grows. Most browsers have a maximum size of 5MB for `localStorage`.\n* Synchronous behavior: Although the API is asynchronous, all\n  persistence actions will be performed synchronously on the main\n  Javascript thread, which may make the browser less responsive.\n\nSchema definition\n-----------------\n\nA data model is declared using `persistence.define`. The following two\ndefinitions define a `Task` and `Category` entity with a few simple\nproperties. The property types are based on [SQLite\ntypes](http://www.sqlite.org/datatype3.html), specifically supported\ntypes are (but any SQLite type is supported):\n\n* `TEXT`: for textual data \n* `INT`: for numeric values\n* `BOOL`: for boolean values (`true` or `false`)\n* `DATE`: for date/time value (with precision of 1 second)\n* `JSON`: a special type that can be used to store arbitrary\n  [JSON](http://www.json.org) data. Note that this data can not be used\n  to filter or sort in any sensible way. If internal changes are made to a `JSON`\n  property, `persistence.js` may not register them. Therefore, a manual\n  call to `anObj.markDirty('jsonPropertyName')` is required before calling\n  `persistence.flush`.\n\nExample use:\n    \n    var Task = persistence.define('Task', {\n      name: \"TEXT\",\n      description: \"TEXT\",\n      done: \"BOOL\"\n    });\n\n    var Category = persistence.define('Category', {\n      name: \"TEXT\",\n      metaData: \"JSON\"\n    });\n\n    var Tag = persistence.define('Tag', {\n      name: \"TEXT\"\n    });\n\nThe returned values are constructor functions and can be used to\ncreate new instances of these entities later.\n\nIt is possible to create indexes on one or more columns using\n`EntityName.index`, for instance:\n\n    Task.index('done');\n    Task.index(['done', 'name']);\n\nThese indexes can also be used to impose unique constraints :\n\n    Task.index(['done', 'name'],{unique:true});\n\nRelationships between entities are defined using the constructor\nfunction's `hasMany` call:\n\n    // This defines a one-to-many relationship:\n    Category.hasMany('tasks', Task, 'category');\n    // These two definitions define a many-to-many relationship\n    Task.hasMany('tags', Tag, 'tasks');\n    Tag.hasMany('tasks', Task, 'tags');\n        \nThe first statement defines a `tasks` relationship on category objects\ncontaining a `QueryCollection` (see the section on query collections\nlater) of `Task`s, it also defines an inverse relationship on `Task`\nobjects with the name `category`. The last two statements define a\nmany-to-many relationships between `Task` and `Tag`. `Task` gets a\n`tags` property (a `QueryCollection`) containing all its tags and vice\nversa, `Tag` gets a `tasks` property containing all of its tasks.\n\nThe defined entity definitions are synchronized (activated) with the\ndatabase using a `persistence.schemaSync` call, which takes a callback\nfunction (with a newly created transaction as an argument), that is called\nwhen the schema synchronization has completed, the callback is\noptional.\n\n    persistence.schemaSync();\n    // or\n    persistence.schemaSync(function(tx) { \n      // tx is the transaction object of the transaction that was\n      // automatically started\n    });\n\nThere is also a migrations plugin you can check out, documentation can be found\nin [docs/migrations.md](docs/migrations.md) file.\n\nMix-ins\n-------\n\nYou can also define mix-ins and apply them to entities of the model. \n\nA mix-in definition is similar to an entity definition, except using\n`defineMixin` rather than just `define`. For example:\n\n    var Annotatable = persistence.defineMixin('Annotatable', {\n      lastAnnotated: \"DATE\"\n    });\n\nYou can define relationships between mix-in and entities. For example:\n\n    // A normal entity\n    var Note = persistence.define('Note', {\n      text: \"TEXT\"\n    });\n  \n    // relationship between a mix-in and a normal entity\n    Annotatable.hasMany('notes', Note, 'annotated');\n\nOnce you have defined a mix-in, you can apply it to any entity of your model, \nwith the `Entity.is(mixin)` method. For example:\n\n    Project.is(Annotatable);\n    Task.is(Annotatable);\n    \nNow, your `Project` and `Task` entities have an additional `lastAnnotated` property.\nThey also have a one to many relationship called `notes` to the `Note` entity. \nAnd you can also traverse the reverse relationship from a `Note` to its `annotated` object.\n\nNote that `annotated` is a polymorphic relationship as it may yield either a `Project` \nor a `Task` (or any other entity which is `Annotatable').\n\nNote: Prefetch is not allowed (yet) on a relationship that targets a mixin. In the example above\nyou cannot prefetch the `annotated` relationship when querying the `Note` entity.\n    \nNotes: this feature is very experimental at this stage. It needs more testing.\n  Support for \"is a\" relationships (classical inheritance) is also in the works.\n\nCreating and manipulating objects\n---------------------------------\n\nNew objects can be instantiated with the constructor functions.\nOptionally, an object with initial property values can be passed as\nwell, or the properties may be set later:\n\n    var task = new Task();\n    var category = new Category({name: \"My category\"});\n    category.metaData = {rating: 5};\n    var tag = new Tag();\n    tag.name = \"work\";\n\nMany-to-one relationships are accessed using their specified name, e.g.:\n    task.category = category;\n\nOne-to-many and many-to-many relationships are access and manipulated\nthrough the `QueryCollection` API that will be discussed later:\n\n    task.tags.add(tag);\n    tasks.tags.remove(tag);\n    tasks.tags.list(tx, function(allTags) { console.log(allTags); });\n\nPersisting/removing objects\n---------------------------\n\nSimilar to [hibernate](http://www.hibernate.org), `persistence.js`\nuses a tracking mechanism to determine which objects' changes have to\nbe persisted to the database. All objects retrieved from the database\nare automatically tracked for changes. New entities can be tracked to\nbe persisted using the `persistence.add` function:\n        \n    var c = new Category({name: \"Main category\"});\n    persistence.add(c);\n    for ( var i = 0; i < 5; i++) {\n      var t = new Task();\n      t.name = 'Task ' + i;\n      t.done = i % 2 == 0;\n      t.category = c;\n      persistence.add(t);\n    }\n\nObjects can also be removed from the database:\n\n    persistence.remove(c);\n\nAll changes made to tracked objects can be flushed to the database by\nusing `persistence.flush`, which takes a transaction object and\ncallback function as arguments. A new transaction can be started using\n`persistence.transaction`:\n    \n    persistence.transaction(function(tx) {\n      persistence.flush(tx, function() {\n        alert('Done flushing!');\n      });\n    });\n\nFor convenience, it is also possible to not specify a transaction or\ncallback, in that case a new transaction will be started\nautomatically. For instance:\n\n    persistence.flush();\n    // or, with callback\n    persistence.flush(function() {\n      alert('Done flushing');\n    });\n\nNote that when no callback is defined, the flushing still happens\nasynchronously.\n\n__Important__: Changes and new objects will not be persisted until you\nexplicitly call `persistence.flush()`. The exception to this rule is\nusing the `list(...)` method on a database `QueryCollection`, which also\nflushes first, although this behavior may change in the future. \n\nDumping and restoring data\n--------------------------\n\nThe library supports two kinds of dumping and restoring data.\n\n`persistence.dump` can be used to create an object containing a full\ndump of a database. Naturally, it is adviced to only do this with\nsmaller databases. Example:\n\n    persistence.dump(tx, [Task, Category], function(dump) {\n      console.log(dump);\n    });\n\nThe `tx` is left out, a new transaction will be started for the\noperation. If the second argument is left out, `dump` defaults\nto dumping _all_ defined entities.\n\nThe dump format is:\n    \n    {\"entity-name\": [list of instances],\n     ...}\n\n`persistence.load` is used to restore the dump produced by\n`persistence.dump`. Usage:\n\n    persistence.load(tx, dumpObj, function() {\n      alert('Dump restored!');\n    });\n\nThe `tx` argument can be left out to automatically start a new\ntransaction. Note that `persistence.load` does not empty the database\nfirst, it simply attempts to add all objects to the database. If\nobjects with, e.g. the same ID already exist, this will fail.\n\nSimilarly, `persistence.loadFromJson` and `persistence.dumpToJson`\nrespectively load and dump all the database's data as JSON strings.\n\nEntity constructor functions\n----------------------------\n\nThe constructor function returned by a `persistence.define` call\ncannot only be used to instantiate new objects, it also has some\nuseful methods of its own:\n\n* `EntityName.all([session])` returns a query collection containing\nall\n  persisted instances of that object. The `session` argument is\n  optional and only required when `persistence.js` is used in\n  multi-session mode.\n* `EntityName.load([session], [tx], id, callback)` loads an particular\n  object from the database by id or returns `null` if it has not been\n  found.\n* `EntityName.findBy([session], [tx], property, value, callback)` searches\n  for a particular object based on a property value (this is assumed to\n  be unique), the callback function is called with the found object or\n  `null` if it has not been found.\n* `EntityName.index([col1, col2, ..., colN], options)` creates an index on a column\n  of a combination of columns, for faster searching. If options.unique is true,\n  the index will impose a unique constraint on the values of the columns.\n\nAnd of course the methods to define relationships to other entities:\n\n* `EntityName.hasMany(property, Entity, inverseProperty)` defines a\n  1:N or N:M relationship (depending on the inverse property)\n* `EntityName.hasOne(property, Entity)` defines a 1:1 or N:1\n  relationship\n\n\nEntity objects\n--------------\n\nEntity instances also have a few predefined properties and methods you\nshould be aware of:\n\n* `obj.id`, contains the identifier of your entity, this is a\n  automatically generated (approximation of a) UUID. You should\n  never write to this property.\n* `obj.fetch(prop, callback)`, if an object has a `hasOne`\n   relationship to another which has not yet been fetched from the\n   database (e.g. when `prefetch` wasn't used), you can fetch in manually\n   using `fetch`. When the property object is retrieved the callback function\n   is invoked with the result, the result is also cached in the entity\n   object itself.\n* `obj.selectJSON([tx], propertySpec, callback)`, sometime you need to extract\n  a subset of data from an entity. You for instance need to post a\n  JSON representation of your entity, but do not want to include all\n  properties. `selectJSON` allows you to do that. The `propertySpec`\n  arguments expects an array with property names. Some examples:\n   * `['id', 'name']`, will return an object with the id and name property of this entity\n   * `['*']`, will return an object with all the properties of this entity, not recursive\n   * `['project.name']`, will return an object with a project property which has a name \n     property containing the project name (hasOne relationship)\n   * `['project.[id, name]']`, will return an object with a project property which has an\n     id and name property containing the project name (hasOne relationship)\n   * `['tags.name']`, will return an object with an array `tags` property containing \n     objects each with a single property: name\n       \n\nQuery collections\n-----------------\n\nA core concept of `persistence.js` is the `QueryCollection`. A\n`QueryCollection` represents a (sometimes) virtual collection that can\nbe filtered, ordered or paginated. `QueryCollection`s are somewhate\ninspired by [Google AppEngine's Query\nclass](http://code.google.com/appengine/docs/python/datastore/queryclass.html).\nA `QueryCollection` has the following methods:\n\n* `filter(property, operator, value)`  \n  Returns a new `QueryCollection` that adds a filter, filtering a\n  certain property based on an operator and value. Supported operators\n  are '=', '!=', '<', '<=', '>', '>=', 'in' and 'not in'. Example:\n  `.filter('done', '=', true)`\n* `or(filter)`  \n  Returns a new `QueryCollection` that contains items either matching\n  the filters specified before calling `or`, or the filter represented\n  in the argument. The `filter` argument is of a `Filter` type, there\n  are three types of filters:\n  - `persistence.PropertyFilter`, which filters on properties (internally called when `filter(...)` is used.  \n    Example: `new persistence.PropertyFilter('done', '=', true)`\n  - `persistence.AndFilter`, which is passed two filter objects as arguments, both of which should be true.\n    Example: `new persistence.AndFilter(new persistence.PropertyFilter('done', '=', true), new persistence.PropertyFilter('archived', '=', true))`\n  - `persistence.OrFilter`, which is passed two filter objects as arguments, one of which should be true.\n    Example: `new persistence.OrFilter(new persistence.PropertyFilter('done', '=', true), new persistence.PropertyFilter('archived', '=', true))`\n* `and(filter)`  \n  same as `or(filter)` except that both conditions should hold for items to be in the collection. \n* `order(property, ascending)`  \n  Returns a new `QueryCollection` that will order its results by the\n  property specified in either an ascending (ascending === true) or\n  descending (ascending === false) order.\n* `limit(n)`  \n  Returns a new `QueryCollection` that limits the size of the result\n  set to `n` items. Useful for pagination.\n* `skip(n)`  \n  Returns a new `QueryCollection` that skips the first `n` results.\n  Useful for pagination.\n* `prefetch(rel)`  \n  Returns a new `QueryCollection` that prefetches entities linked\n  through relationship `rel`, note that this only works for one-to-one\n  and many-to-one relationships.\n* `add(obj)`  \n  Adds object `obj` to the collection.\n* `remove(obj)`  \n  Removes object `obj` from the collection.\n* `list([tx], callback)`  \n  Asynchronously fetches the results matching the formulated query.\n  Once retrieved, the callback function is invoked with an array of\n  entity objects as argument.\n* `each([tx], eachCallback)`  \n  Asynchronously fetches the results matching the formulated query.\n  Once retrieved, the `eachCallback` function is invoked on each\n  element of the result objects.\n* `forEach([tx], eachCallback)`  \n  Alias for `each`\n* `one([tx], callback)`\n  Asynchronously fetches the first element of the collection, or `null` if none.\n* `destroyAll([tx], callback)`\n  Asynchronously removes all the items in the collection. __Important__: this does\n  not only remove the items from the collection, but removes the items themselves!\n* `count([tx], callback)`\n  Asynchronously counts the number of items in the collection. The arguments passed\n  to the `callback` function is the number of items.\n\nQuery collections are returned by:\n\n* `EntityName.all()`, e.g. `Task.all()`\n* one-to-many and many-to-many relationships, e.g. `task.tags`\n\nExample:\n\n    var allTasks = Task.all().filter(\"done\", '=', true).prefetch(\"category\").order(\"name\", false).limit(10);\n        \n    allTasks.list(null, function (results) {\n        results.forEach(function (r) {\n            console.log(r.name)\n            window.task = r;\n        });\n    });\n\nUsing persistence.js on the server\n==================================\n\nInstalling `persistence.js` on node is easy using [npm](http://npmjs.org):\n\n    npm install persistencejs\n\nSadly the node.js server environment requires slight changes to\n`persistence.js` to make it work with multiple database connections:\n\n* A `Session` object needs to be passed as an extra argument to\n  certain method calls, typically as a first argument.\n* Methods previously called on the `persistence` object itself are now\n  called on the `Session` object.\n\nAn example `node.js` application is included in `test/node-blog.js`. \n\nSetup\n-----\nYou need to `require` two modules, the `persistence.js` library itself\nand the MySQL backend module.\n\n    var persistence = require('persistencejs');\n    var persistenceStore = persistence.StoreConfig.init(persistence, { adaptor: 'mysql' });\n\nThen, you configure the database settings to use:\n\n    persistenceStore.config(persistence, 'localhost', 3306, 'dbname', 'username', 'password');\n\nSubsequently, for every connection you handle (assuming you're\nbuilding a sever), you call the `persistenceStore.getSession()`\nmethod:\n\n    var session = persistenceStore.getSession();\n\nThis session is what you pass around, typically together with a\ntransaction object. Note that currently you can only have one\ntransaction open per session and transactions cannot be nested.\n\n    session.transaction(function(tx) {\n      ...\n    });\n\nCommit and Rollback\n-------------------\n\n`persistence.js` works in autocommit mode by default. \n\nYou can override this behavior and enable explicit commit and rollback \nby passing true as first argument to `persistence.transaction`. \nYou can then use the following two methods to control the transaction:\n\n* `transaction.commit(session, callback)` commits the changes.\n* `transaction.rollback(session, callback)` rollbacks the changes.\n\nTypical code will look like:\n \n    session.transaction(true, function(tx) {\n      // create/update/delete objects\n      modifyThings(session, tx, function(err, result) {\n        if (err) {\n          // something went wrong\n          tx.rollback(session, function() {\n            console.log('changes have been rolled back: ' + ex.message);\n          });\n        }\n        else {\n          // success\n          tx.commit(session, function() {\n            console.log('changes have been committed: ' result);\n        });\n      });\n    });\n\nExplicit commit and rollback is only supported on MySQL (server side) \nfor now.\n\nDefining your data model\n------------------------\n\nDefining your data model is done in exactly the same way as regular `persistence.js`:\n\n    var Task = persistence.define('Task', {\n      name: \"TEXT\",\n      description: \"TEXT\",\n      done: \"BOOL\"\n    });\n\nA `schemaSync` is typically performed as follows:\n\n    session.schemaSync(tx, function() {\n      ...\n    });\n\nCreating and manipulating objects\n---------------------------------\n\nCreating and manipulating objects is done much the same way as with\nregular `persistence.js`, except that in the entity's constructor you\nneed to reference the `Session` again:\n\n    var t = new Task(session);\n    ...\n    session.add(t);\n\n    session.flush(tx, function() {\n      ...\n    });\n\nQuery collections\n-----------------\n\nQuery collections work the same way as in regular `persistence.js`\nwith the exception of the `Entity.all()` method that now also requires\na `Session` to be passed to it:\n\n    Task.all(session).filter('done', '=', true).list(tx, function(tasks) {\n      ...\n    });\n\nClosing the session\n-------------------\n\nAfter usage, you need to close your session:\n\n    session.close();\n\nBugs and Contributions\n======================\n\nIf you find a bug, please [report\nit](https://github.com/zefhemel/persistencejs/issues).  or fork the\nproject, fix the problem and send me a pull request. For a list of\nplanned features and open issues, have a look at the [issue\ntracker](https://github.com/zefhemel/persistencejs/issues).\n\nFor support and discussion, please join the [persistence.js Google\nGroup](http://groups.google.com/group/persistencejs).\n\nThanks goes to the people listed in `AUTHORS` for their contributions.\n\nIf you use [GWT](http://code.google.com/webtoolkit/) (the Google Web\nToolkit), be sure to have a look at [Dennis Z. Jiang's GWT persistence.js\nwrapper](http://github.com/dennisjzh/GwtMobile-Persistence)\n\nLicense\n=======\n\nThis work is licensed under the [MIT license](http://en.wikipedia.org/wiki/MIT_License).\n\nSupport this work\n-----------------\n\nYou can support this project by flattering it:\n\n<a href=\"http://flattr.com/thing/2510/persistence-js\" target=\"_blank\">\n<img src=\"http://api.flattr.com/button/button-static-50x60.png\" title=\"Flattr this\" border=\"0\" /></a>\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"persistence\",\n  \"main\": \"./lib/persistence.js\",\n  \"version\": \"0.3.0\",\n  \"_release\": \"0.3.0\",\n  \"_target\": \"~0.3.0\",\n  \"_source\": \"git://github.com/zefhemel/persistencejs.git\",\n  \"homepage\": \"http://persistencejs.org\",\n  \"authors\": [\n    \"Zef Hemel <zef@zef.me>\",\n    \"Fabio Rehm <fgrehm@gmail.com>\",\n    \"Lukas Berns\",\n    \"Roberto Saccon <rsaccon@gmail.com>\",\n    \"Wilker Lúcio <wilkerlucio@gmail.com>\",\n    \"Bruno Jouhier <bjouhier@gmail.com>\",\n    \"Robin Wenglewski <robin@wenglewski.de>\",\n    \"Matthias Hochgatterer <matthias.hochgatterer@gmail.com>\",\n    \"Chris Chua <chris.sirhc@gmail.com>\",\n    \"Mike Smullin <mike@smullindesign.com>\",\n    \"Masahiro Hayashi <hayashi.masahiro@gmail.com>\",\n    \"Mick Staugaard <mick@staugaard.com>\",\n    \"Shane Tomlinson <set117@gmail.com>\",\n    \"Eugene Ware <eugene.ware@nextharbour.com>\"\n  ],\n  \"description\": \"An asynchronous Javascript database mapper library. You can use it in the browser, as well on the server (and you can share data models between them).\",\n  \"license\": \"MIT\",\n  \"ignore\": [\n    \"**/.*\",\n    \"node_modules\",\n    \"test\"\n  ]\n}\n"
  },
  {
    "path": "demo/jquerymobile/README.md",
    "content": "To try this demo, you need to run it through a web server.\n`index.html` uses relative links to persistence.js (in\n`../../lib/persistence.js` to be exact), so this path needs to be\navailable.\n\nExample images, design and text used in the demo are copy-pasted\nstraight from jquerymobile documentation.\n"
  },
  {
    "path": "demo/jquerymobile/docs/text.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n  <title>Text only</title>\n</head>\n<body>\n\n<div data-role=\"page\">\n\n  <div data-role=\"header\">\n    <h1>Text only</h1>\n  </div><!-- /header -->\n\n  <div data-role=\"content\">\n    <p>jQuery’s mobile strategy can be summarized simply: Delivering top-of-the-line JavaScript in a unified User Interface that works across the most-used smartphone web browsers and tablet form factors.</p>\n\n    <p>The critical difference with our approach is the <a href=\"platforms.html\">wide variety of mobile platforms we’re targeting</a> with jQuery Mobile. We’ve been working hard at bringing jQuery support to all mobile browsers that are sufficiently-capable and have at least a nominal amount of market share. In this way, we’re treating mobile web browsers exactly how we treat desktop web browsers.</p>\n\n    <p>To make this broad support possible, all pages in jQuery Mobile are built on a foundation of <strong>clean, semantic HTML</strong> to ensure compatibility with pretty much any web-enabled device. In devices that interpret CSS and JavaScript, jQuery Mobile applies <strong>progressive enhancement techniques</strong> to unobtrusively transform the semantic page into a rich, interactive experience that leverages the power of jQuery and CSS. <strong>Accessibility features</strong> such as WAI-ARIA are tightly integrated throughout the framework to provide support for screen readers and other assistive technologies.</p>\n  </div><!-- /content -->\n\n</div><!-- /page -->\n\n</body>\n</html>"
  },
  {
    "path": "demo/jquerymobile/docs/text_and_images.html",
    "content": "<!DOCTYPE html> \n<html> \n  <head> \n  <title>Text and image</title> \n</head> \n<body> \n\n<div data-role=\"page\">\n\n  <div data-role=\"header\">\n    <h1>Text and image</h1>\n  </div><!-- /header -->\n\n  <div data-role=\"content\">\n    <p>jQuery’s mobile strategy can be summarized simply: Delivering top-of-the-line JavaScript in a unified User Interface that works across the most-used smartphone web browsers and tablet form factors.</p>\n    <p>The critical difference with our approach is the <a href=\"platforms.html\">wide variety of mobile platforms we’re targeting</a> with jQuery Mobile. We’ve been working hard at bringing jQuery support to all mobile browsers that are sufficiently-capable and have at least a nominal amount of market share. In this way, we’re treating mobile web browsers exactly how we treat desktop web browsers.</p>\n    <p>To make this broad support possible, all pages in jQuery Mobile are built on a foundation of <strong>clean, semantic HTML</strong> to ensure compatibility with pretty much any web-enabled device. In devices that interpret CSS and JavaScript, jQuery Mobile applies <strong>progressive enhancement techniques</strong> to unobtrusively transform the semantic page into a rich, interactive experience that leverages the power of jQuery and CSS. <strong>Accessibility features</strong> such as WAI-ARIA are tightly integrated throughout the framework to provide support for screen readers and other assistive technologies.</p>\n    <img src=\"../assets/ipad-palm.png\" alt=\"Smartphone and tablet designs\" style=\"max-width:100%; margin-top:20px;\">\n  </div><!-- /content -->\n  \n</div><!-- /page -->\n\n</body>\n</html>      "
  },
  {
    "path": "demo/jquerymobile/index.html",
    "content": "<!DOCTYPE html>\n<html lang='en' >\n  <head>\n    <title>jQuery mobile / persistencejs integration</title>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <link rel=\"stylesheet\" href=\"http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.css\" />\n    <style>\n      .ui-mobile #jqm-home {  background: #e5e5e5 url(assets/jqm-sitebg.png) top center repeat-x; }\n      .ui-mobile #jqm-homeheader { padding: 55px 25px 0; text-align: center }\n      .ui-mobile #jqm-homeheader h1 { margin: 0 0 10px; }\n      .ui-mobile #jqm-homeheader p { margin: 0; }\n      .ui-mobile #jqm-version { text-indent: -99999px; background: url(assets/version.png) top right no-repeat; width: 119px; height: 122px; overflow: hidden; position: absolute; top: 0; right: 0; }\n      .ui-mobile .jqm-themeswitcher { clear: both; margin: 20px 0 0; }\n\n      h2 { margin-top:1.5em; }\n      p code { font-size:1.2em; font-weight:bold; }\n\n      dt { font-weight: bold; margin: 2em 0 .5em; }\n      dt code, dd code { font-size:1.3em; line-height:150%; }\n    </style>\n    <script src=\"http://code.jquery.com/jquery-1.4.4.min.js\"></script>\n    <script src=\"http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.js\"></script>\n    <script src=\"http://code.google.com/apis/gears/gears_init.js\"></script>\n    <script src=\"../../lib/persistence.js\"></script>\n    <script src=\"../../lib/persistence.store.sql.js\"></script>\n    <script src=\"../../lib/persistence.store.websql.js\"></script>\n    <script src=\"../../lib/persistence.store.memory.js\"></script>\n    <script src=\"../../lib/persistence.jquery.js\"></script>\n    <script src=\"../../lib/persistence.jquery.mobile.js\"></script>\n    <script type=\"text/javascript\">\n      if (location.protocol == \"file:\") {\n        alert(\"Didn't you read the README ? You need to load this page from a server.\");\n      }\n      if (window.openDatabase) {\n        persistence.store.websql.config(persistence, \"jquerymobile\", 'database', 5 * 1024 * 1024);\n      } else {\n        persistence.store.memory.config(persistence);\n      }\n      persistence.define('Page', {\n        path: \"TEXT\",\n        data: \"TEXT\",\n      });\n      \n      persistence.define('Image', {\n        path: \"TEXT\",\n        data: \"TEXT\",\n      });\n      \n      persistence.define('Order', {\n        shipping: \"TEXT\"\n      });\n      \n      persistence.schemaSync();\n\n      $('#reset').click(function() {\n        persistence.reset();\n        persistence.schemaSync();\n        return false;\n      });\n    </script>\n  </head>\n  <body>\n\n    <div data-role=\"page\" data-theme=\"b\" id=\"jqm-home\">\n      <div id=\"jqm-homeheader\">\n        <h1 id=\"jqm-logo\"><img src=\"assets/jquery-logo.png\" alt=\"jQuery Mobile Framework\" width=\"235\" height=\"61\" /></h1>\n        <p>Touch-Optimized Web Framework for Smartphones &amp; Tablets - now with PersistenceJS integration</p>\n        <p id=\"jqm-version\">Alpha Release</p>\n      </div>\n      <div data-role=\"content\">\n        <ul data-role=\"listview\" data-inset=\"true\" data-theme=\"c\" data-dividertheme=\"b\">\n          <li data-role=\"list-divider\">Local persistence demos</li>\n          <li><a href=\"docs/text.html\">Text only</a></li>\n          <li><a href=\"docs/text_and_images.html\">Text and images</a></li>\n          <li><a href=\"#form_submission\">Form submission</a></li>\n        </ul>\n      </div>\n      <a id=\"reset\" href=\"#\" data-role=\"button\" data-theme=\"c\" class=\"ui-btn-right\">Reset DB</a>\n    </div>\n\n    <div data-role=\"page\" id=\"form_submission\">\n      <div data-role=\"header\">\n        <h1>Form submission</h1>\n      </div><!-- /header -->\n      <div data-role=\"content\">\n        <form action=\"order/form-fake-response.html\" method=\"post\">\n          <fieldset>\n            <div data-role=\"fieldcontain\">\n              <label for=\"shipping\" class=\"select\">Formsubmission (GET):</label>\n              <select name=\"shipping\" id=\"shipping\">\n                <option value=\"Standard shipping\">Standard: 7 day</option>\n                <option value=\"Rush shipping\">Rush: 3 days</option>\n                <option value=\"Express shipping\">Express: next day</option>\n                <option value=\"Overnight shipping\">Overnight</option>\n              </select>\n            </div>\n          <button type=\"submit\" data-theme=\"a\">Submit</button>\n        </fieldset>\n        </form>\n      </div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "demo/jquerymobile/order/form-fake-response.html",
    "content": "<!DOCTYPE html> \n<html> \n  <head> \n  <title>Form submission</title> \n</head> \n<body> \n\n<div data-role=\"page\">\n\n  <div data-role=\"header\">\n    <h1>Sample form response</h1>\n  </div><!-- /header -->\n\n  <div data-role=\"content\">\n    <h1>Fake response</h1> \n    <h2>You choose: (your value here if it would be no fake)</h2> \n  </div><!-- /content -->\n  \n</div><!-- /page -->\n\n</body>\n</html>"
  },
  {
    "path": "docs/DEVELOPMENT.md",
    "content": "Documentation for developers\n============================\n\nConstructor functions\n---------------------\n\n    var Task = persistence.define('Task', {\n      name: \"TEXT\",\n      done: \"BOOL\"\n    });\n    var Category = persistence.define('Category', {\n      name: \"TEXT\"\n    });\n    Task.hasOne('category', Category);\n\n`Task` is a constructor function that is used to create new instances of the `Task` entity, but also can be used to retrieve meta data from, using `Task.meta`. This `meta` field provides the following information:\n\n* `name`, the name of the entity as set as first argument of `define`\n* `fields`, the field object passed to the original `define`,\n  consisting of property names as keys and textual column types as\n  values.\n* `hasOne`, an object with relation names as keys and relationship\n  objects as values. The relationship object currently has one field:\n  `type`: which links to the constructor function of the type of the\n  relation. Example: `Task.hasOne.category.type` will equal the\n  `Category` constructor.\n* `hasMany`, an object with relation anmes as keys and relationship objects as values. The relationship has the following fields:\n   * `type`: the constructor function of the relationship entity\n   * `inverseProperty`: the property name of the inverse relation\n   * `manyToMany`: a boolean value indicating if this is a manyToMany\n     relationship or not (then it's a one-tomany)\n    * `tableName`: name of the utility coupling table used for a\n      many-to-many relationship\n\nExtension hooks\n----------------\n\n* `persistence.entityDecoratorHooks`: a list of functions (with the\n  constructor function as argument) to be called to decorate. Useful to\n  add new functionality to constructo functions, such as `Task.index`.\n* `persistence.flushHooks`: a list of functions to be called before flushing.\n* `persistence.schemaSyncHooks`: a list of functions to be called before syncing the schema.\n\n"
  },
  {
    "path": "docs/jquery.md",
    "content": "# persistence.jquery.js\n\n`persistence.jquery.js` is a jquery plugin for `persistence.js` that\nallows the usage of jquery notation for crossbrowser-access of\npersistencejs entities.\n \nExample\n-------\n\nSimple example:\n\n    var User = persistence.define('User', {\n      firstname: \"TEXT\",\n      lastname: \"TEXT\"\n    });\n\n    var user = new User({firstname: \"Joe\", lastname: \"Doo\"});\n\n    // setter   \n    $(user).data('firstname', \"Mike\") \n\n    // getter\n    console.log($(user).data('firstname')); // => Mike\n\nYou can find more examples in `test/test.persistence-jquery.js`."
  },
  {
    "path": "docs/jquery.mobile.md",
    "content": "# persistence.jquery.mobile.js\n\n`persistence.jquery.mobile.js` is a plugin for `persistence.js` and [jQuery mobile](http://jquerymobile.com) that\nallows ajax request re-routing to persitencejs for:\n\n* Html text: caches ajax-loaded HTML pages in local DB.\n* Images (in `img` tags of ajax-loaded HTML pages): grabs/encodes them via `canvas` and caches them as data-URL strings in local DB.\n* Form submission (only POST requests).\n\nFor ajax-loaded HTML pages and images, the content-providing entities get \ntheir name from user-overwritable default values. For form submissions, the entity \nis matched according to the following URL pattern:\n    \n    entity-name / path1/path2/../pathN\n\nAjax re-routing to persitencejs only takes place if the required entities exist.\n  \nGlobal settings (and it's default values):\n\n    persistence.jquery.mobile.pageEntityName = \"Page\";    // Html page entity name\n    persistence.jquery.mobile.imageEntityName = \"Image\";  // Image entity name\n    persistence.jquery.mobile.pathField = \"path\";         // Entity path-field name\n    persistence.jquery.mobile.dataField = \"data\";         // Entity data-field name\n    \n\nOptional Regular Expression to exclude URLs from re-routing to persistencejs:\n\n    persistence.jquery.mobile.urlExcludeRx\n    \nExample: `persistence.jquery.mobile.urlExcludeRx = /^\\/admin\\//;` \n(all URL paths starting with \"/admin/\" are excluded)\n\n\nAjax page loading example:\n\n    URL: \"about/intro.html\"\n    => entity name: \"Page\"\n    => entity path field: \"about/intro.html\" \n    => entity data field: (the HTML content of the page)\n    Images: (all images contained in the page specified above)\n    => entity name: \"Image\"\n    => entity path field: (src attribute value of IMG tag) \n    => entity data field: (the imgae data as Base64 encoded dataURL)\n\nAjax form submission examples: \n\n    URL (POST): \"order/response.html\"\n    => entity name: \"Order\"\n    => entity fields (other than path): retrieved from POST data\n\nYou can find a demo at `demo/jquerymobile/index.html` (you must load it from a server)."
  },
  {
    "path": "docs/migrations.md",
    "content": "# persistence.migrations.js\n\n`persistence.migrations.js` is a plugin for `persistence.js` that provides\na simple API for altering your databases in a structured and organised manner\ninspired by [Ruby on Rails migrations](http://guides.rubyonrails.org/migrations.html).\n\n## Anatomy of a Migration\n\n    persistence.defineMigration(1, {\n      up: function() {\n        this.createTable('Task', function(t){\n          t.text('name');\n          t.text('description');\n          t.boolean('done');\n        });\n      },\n      down: function() {\n        this.dropTable('Task');\n      }\n    });\n\nThis migration adds a table called `Task` with a string column called `name`,\na text column called `description` and a boolean column called `done`. \nA `id VARCHAR(32) PRIMARY KEY` collumn will also be added, however since\nthis is the default we do not need to ask for this. Reversing this migration\nis as simple as dropping the table. The first argument passed to `defineMigration`\nis the migration version which should be incremented when defining following\nmigrations\n\nMigrations are not limited to changing the schema. You can also use them to\nfix bad data in the database or populate new fields:\n\n    persistence.defineMigration(2, {\n      up: function() {\n        this.addColumn('User', 'email', 'TEXT');\n\n        // You can execute some raw SQL\n        this.executeSql('UPDATE User SET email = username + \"@domain.com\"');\n\n        // OR \n\n        // you can define a custom action to query for objects and manipulate them\n        this.action(function(tx, nextAction){\n          allUsers.list(tx, function(result){\n            result.forEach(function(u){\n              u.email = u.userName + '@domain.com';\n              persistence.add(u);\n            });\n            persistence.flush(tx, function() {\n              // Please remember to call this when you are done with an action,\n              // otherwise the system will hang\n              nextAction();\n            });\n          });\n        });\n      }\n    });\n\nThis migration adds a `email` column to the `User` table and sets all emails\nto `\"{userName}@domain.com\"`.\n\n## API methods\n\n    persistence.defineMigration(3, {\n      up: function() {\n        this.addColumn('TableName', 'columnName', 'COLUMN_TYPE');\n        this.removeColumn('TableName', 'columnName');\n        this.addIndex('TableName', 'columnName');\n        this.removeIndex('TableName', 'columnName');\n        this.executeSql('RAW SQL');\n        this.dropTable('TableName');\n\n        this.createTable('TableName', function(table){\n          table.text('textColumnName');\n          table.integer('integerColumnName');\n          table.boolean('booleanColumnName');\n          table.json('jsonColumnName'); // JSON columns will be mapped to TEXT columns on database\n          table.date('dateColumnName');\n        });\n      }\n    });\n\n## Running Migrations\n\nFirst thing you need to do is initialize migrations plugin:\n\n    persistence.migrations.init(function() {\n      // Optional callback to be executed after initialization\n    });\n\nThen you should load your migrations and run:\n\n* `persistence.migrate()` to run migrations up to the most recent\n* `persistence.migrate(function)` to run migrations up to the most recent and execute some code\n* `persistence.migrate(version, function)` to run migrations up / down to the specified version and execute some code\n\nTo load migrations you should use something like [RequireJS](http://requirejs.org/).\n"
  },
  {
    "path": "docs/search.md",
    "content": "persistence.search.js\n==============\n`persistence.search.js` is a light-weight extension of the\n`persistence.js` library that adds full-text search through a simple\nAPI.\n\nInitialization:\n\n    persistence.search.config(persistence, persistence.store.websql.sqliteDialect);\n\nExample usage:\n\n    var Note = persistence.define('Note', {\n        name: \"TEXT\",\n        text: \"TEXT\",\n        status: \"TEXT\"\n    });\n    Note.textIndex('name');\n    Note.textIndex('text');\n\nThis sample defines a `Note` entity with three properties of which\n`name` and `text` are full-text indexed. For this a new database table\nwill be created that stores the index.\n\nSearching is done as follows:\n\n    Note.search(\"note\").list(tx, function(results) {\n      console.log(results);\n    });\n\nor you can paginate your results using `limit` and `skip` (similar\nto `limit` and `skip` in QueryCollections).\n\n    Note.search(\"note\").limit(10).skip(10).list(null, function(results) {\n      console.log(results);\n    });\n\nQuery language\n--------------\n\nQueries can contain regular words. In addition the `*` wildcard can be\nused anywhere with a word. The `property:` notation can be used to\nsearch only a particular field. Examples:\n\n* `note`\n* `name: note`\n* `interesting` \n* `inter*`\n* `important essential`\n\nNote that currently a result is return when _any_ word matches.\nResults are ranked by number of occurences of one of the words in the\ntext.\n"
  },
  {
    "path": "docs/sync.md",
    "content": "persistence.sync.js\n===================\n\n`persystence.sync.js` is a `persistence.js` plug-in that adds data\nsynchronization with remote servers. It comes with a client-side\ncomponent (`persistence.sync.js`) and a sample server-side component\n(`persistence.sync.server.js`) for use with\n[node.js](http://nodejs.org). It should be fairly easy to implement\nserver-components using other languages, any contributions there\nare welcome.\n\nClient-side usage\n-----------------\n\nAfter including both `persistence.js` and `persistence.sync.js` in\nyour page, you can enable syncing on entities individually:\n\n    var Task = persistence.define(\"Task\", {\n      name: \"TEXT\",\n      done: \"BOOL\"\n    });\n\n    Task.enableSync('/taskChanges');\n\nThe argument passed to `enableSync` is the URI of the sync server\ncomponent.\n\nTo initiate a sync, the `EntityName.syncAll(..)` method is used. The method signature\nis the following:\n\n    EntityName.syncAll(conflictHandler, successCallback, errorCallback)\n\nsuccessCallback and errorCallback are optional. successCallback occurs after a\nsuccessful sync errorCallback occurs on error (I.E. a non-200 response code).\n\nconflictHandler is called in the event of a conflict between local and remote data:\n\n    function conflictHandler(conflicts, updatesToPush, callback) {\n      // Decide what to do with the conflicts here, possibly add to updatesToPush\n      callback();\n    }\n\n    EntityName.syncAll(conflictHandler, function() {\n      alert('Done!');\n    }, errorHandler);\n\nThere are two sample conflict handlers:\n\n1. `persistence.sync.preferLocalConflictHandler`, which in case of a\n   data conflict will always pick the local changes.\n2. `persistence.sync.preferRemoteConflictHandler`, which in case of a\n   data conflict will always pick the remote changes.\n\nFor instance:\n\n    EntityName.syncAll(persistence.sync.preferLocalConflictHandler, function() {\n      alert('Done!');\n    }, errorCallback);\n\nNote that you are responsible for syncing all entities and that there\nare no database consistencies after a sync, e.g. if you only sync `Task`s that\nrefer to a `Project` object and that `Project` object has not (yet) been synced,\nthe database will be (temporarily) inconsistent.\n\nServer-side (Java, Slim3, AppEngine)\n------------------------------------\n\nRoberto Saccon developed a [Java server-side implementation of\npersistence sync using the Slim3\nframework](http://github.com/rsaccon/Slim3PersistenceSync).\n\nServer-side (node.js)\n---------------------\n\nThe server must expose a resource located at the given URI that responds to:\n\n* `GET` requests with a `since=<UNIX MS TIMESTAMP>` GET parameter that\n  will return a JSON object with two properties:\n  * `now`, the timestamp of the current time at the server (in ms since 1/1/1970)\n  * `updates`, an array of objects updated since the timestamp\n    `since`. Each object has at least an `id` and `_lastChange` field\n    (in the same timestamp format).\n\n  For instance:\n      \n      /taskChanges?since=1279888110373\n\n      {\"now\":1279888110421,\n       \"updates\": [\n          {\"id\": \"F89F99F7B887423FB4B9C961C3883C0A\",\n           \"name\": \"Main project\",\n           \"_lastChange\": 1279888110370\n          }\n       ]\n      }\n\n* `POST` requests with as its body a JSON array of new/updated\n  objects. Every object needs to have at least an `id` property.\n\n  Example, posting to:\n\n      /taskChanges\n\n  with body:\n\n      [{\"id\":\"BDDF85807155497490C12D6DA3A833F1\",\n        \"name\":\"Locally created project\"}]\n\n  The server is supposed to persist these changes (if valid).\n  Internally the items must be assigned a `_lastChange` timestamp\n  `TS`. If OK, the server will return a JSON object with \"ok\" as\n  `status` and `TS` as `now`. _Note:_ it is important that the\n  timestamp of all items and the one returned are the same.\n\n      {\"status\": \"ok\", \n       \"now\": 1279888110797}\n\n\nServer-side filtering\n-------------------\n\nIn certain circumstances, it is not necessary or desired to push all records down to a client. A standard GET URI looks like this:\n    \n    app.get('/taskupdates', function(req, res) {\n      persistenceSync.pushUpdates(req.conn, req.tx, Task, req.query.since, function(updates){\n        res.send(updates);\n      });\n    });\n\nThe third parameter in `pushUpdates` is the Entity model. If you wish to filter, simply pass a Query Collection in its place.\n    \n    app.get('/taskupdates', function(req, res) {\n      var taskCollection = Task.all(req.conn).filter('done','=',false);\n      persistenceSync.pushUpdates(req.conn, req.tx, taskCollection, req.query.since, function(updates){\n        res.send(updates);\n      });\n    });\n\n\n\nLimitations\n-----------\n\n* This synchronization library synchronizes on a per-object granularity. It\n  does not keep exact changes on a per-property basis, therefore\n  conflicts may be introduced that need to be resolved.\n* It does not synchronize many-to-many relationships at this point\n* There may still be many bugs, I'm not sure.\n       \n"
  },
  {
    "path": "index.js",
    "content": "module.exports = require('./lib/');\n"
  },
  {
    "path": "lib/index.js",
    "content": "module.exports = require('./persistence').persistence;\nmodule.exports.StoreConfig = require('./persistence.store.config');\n"
  },
  {
    "path": "lib/persistence.jquery.js",
    "content": "/**\n * Copyright (c) 2010 Roberto Saccon <rsaccon@gmail.com>\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\nif (!window.jQuery) {\n  throw new Error(\"jQuery should be loaded before persistence.jquery.js\");\n}\n\nif (!window.persistence) {\n  throw new Error(\"persistence.js should be loaded before persistence.jquery.js\");\n} \n\npersistence.jquery = {};\n\n/**\n * crossbrowser implementation for entity-property\n */\npersistence.defineProp = function(scope, field, setterCallback, getterCallback) {\n    scope[field] = function(value) {\n        if (value === undefined) {\n            return getterCallback();\n        } else {\n            setterCallback(value);\n            return scope;\n        }\n    };\n};\n\n/**\n * crossbrowser implementation for entity-property setter\n */\npersistence.set = function(scope, fieldName, value) {\n    if (persistence.isImmutable(fieldName)) throw new Error(\"immutable field: \"+fieldName);\n    scope[fieldName](value);\n    return scope;\n};\n\n/**\n * crossbrowser implementation for entity-property getter\n */\npersistence.get = function(arg1, arg2) {\n    var val = (arguments.length == 1) ? arg1 : arg1[arg2];\n    return (typeof val === \"function\") ? val() : val;\n};\n\n\n(function($){\n    var originalDataMethod = $.fn.data;\n\n    $.fn.data = function(name, data) {\n        if (this[0] && this[0]._session && (this[0]._session === window.persistence)) {\n            if (data) {\n                this[0][name](data);\n                return this;\n            } else {\n                return this[0][name]();\n            }\n        } else {\n            return originalDataMethod.apply(this, arguments);\n        }\n    };\n\n    if (persistence.sync) {\n        persistence.sync.getJSON = function(url, success) {\n            $.getJSON(url, null, success);\n        };\n\n        persistence.sync.postJSON = function(url, data, success) {\n            $.ajax({\n                url: url,\n                type: 'POST',\n                data: data,\n                dataType: 'json',\n                success: function(response) {\n                    success(JSON.parse(response));\n                }\n            });\n        };\n    }\n})(jQuery);\n"
  },
  {
    "path": "lib/persistence.jquery.mobile.js",
    "content": "/**\n * Copyright (c) 2010 Roberto Saccon <rsaccon@gmail.com>\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\nif (!window.persistence.jquery) {\n  throw new Error(\"persistence.jquery.js should be loaded before persistence.jquery.mobile.js\");\n}\n\npersistence.jquery.mobile = {};\n\n(function($){   \n    var $pjqm = persistence.jquery.mobile;\n    \n    if (window.openDatabase) {\n        $pjqm.pageEntityName = \"Page\";\n        $pjqm.imageEntityName = \"Image\";\n        $pjqm.pathField = \"path\";\n        $pjqm.dataField = \"data\";\n\n        var originalAjaxMethod = $.ajax;\n\n        function expand(docPath, srcPath) {\n            var basePath = (/\\/$/.test(location.pathname) || (location.pathname == \"\")) ?\n                location.pathname :\n                location.pathname.substring(0, location.pathname.lastIndexOf(\"/\"));\n            if (/^\\.\\.\\//.test(srcPath)) {\n                // relative path with upward directory traversal\n                var count = 1, splits = docPath.split(\"/\");\n                while (/^\\.\\.\\//.test(srcPath)) {\n                    srcPath = srcPath.substring(3);\n                    count++;\n                }\n                return basePath + ((count >= splits.length) ?\n                    srcPath :\n                    splits.slice(0, splits.length-count).join(\"/\") + \"/\" + srcPath);\n            } else if (/^\\//.test(srcPath)) {\n                // absolute path\n                return srcPath;\n            } else {\n                // relative path without directory traversal\n                return basePath + docPath + \"/\" + srcPath;\n            }\n        }\n\n        function base64Image(img, type) {\n            var canvas = document.createElement(\"canvas\");\n            canvas.width = img.width;\n            canvas.height = img.height;\n\n            // Copy the image contents to the canvas\n            var ctx = canvas.getContext(\"2d\");\n            ctx.drawImage(img, 0, 0);\n\n            return canvas.toDataURL(\"image/\" + type);\n        }\n\n        // parseUri 1.2.2\n        // (c) Steven Levithan <stevenlevithan.com>\n        // MIT License\n\n        var parseUriOptions = {\n            strictMode: false,\n            key: [\"source\",\"protocol\",\"authority\",\"userInfo\",\"user\",\"password\",\"host\",\"port\",\"relative\",\"path\",\"directory\",\"file\",\"query\",\"anchor\"],\n            q:   {\n                name:   \"queryKey\",\n                parser: /(?:^|&)([^&=]*)=?([^&]*)/g\n            },\n            parser: {\n                strict: /^(?:([^:\\/?#]+):)?(?:\\/\\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?))?((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/,\n                loose:  /^(?:(?![^:@]+:[^:@\\/]*@)([^:\\/?#.]+):)?(?:\\/\\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/\n            }\n        };\n\n        function parseUri (str) {\n            var o   = parseUriOptions,\n                m   = o.parser[o.strictMode ? \"strict\" : \"loose\"].exec(str),\n                uri = {},\n                i   = 14;\n\n            while (i--) uri[o.key[i]] = m[i] || \"\";\n\n            uri[o.q.name] = {};\n            uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {\n                if ($1) uri[o.q.name][$1] = $2;\n            });\n\n            return uri;\n        }\n\n        function getImageType(parsedUri) {\n            if (parsedUri.queryKey.type) {\n                return parsedUri.queryKey.type;\n            }  else {\n                return (/\\.png$/i.test(parsedUri.path)) ? \"png\" : \"jpeg\";\n            }\n        }\n\n        $.ajax = function(settings) {\n            var parsedUrl = parseUri(settings.url);\n            var entities = {}, urlPathSegments = parsedUrl.path.split(\"/\");\n            if ((settings.type == \"post\") && (urlPathSegments.length > 1)) {\n                var entityName = (urlPathSegments[1].charAt(0).toUpperCase() + urlPathSegments[1].substring(1));\n                if (persistence.isDefined(entityName)) {\n                    var Form = persistence.define(entityName);\n\n                    var persistFormData = function() {\n                        var obj = {};\n                        settings.data.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ( $0, $1, $2 ) {\n                            if ($1) {\n                                obj[$1] = $2;\n                            }\n                        });\n\n                        var entity = new Form(obj);\n                        persistence.add(entity);\n                        persistence.flush();\n                    };\n\n                    if (!navigator.hasOwnProperty(\"onLine\") || navigator.onLine) {\n                        originalAjaxMethod({\n                            url: settings.url,\n                            success: function(data) {\n                                settings.success(data);\n                                persistFormData();\n                            },\n                            error: settings.error\n                        });\n                    } else {\n                        persistFormData();\n                    }\n                } else {\n                    originalAjaxMethod(settings);\n                }\n            } else if (persistence.urlExcludeRx && persistence.urlExcludeRx.test(parsedUrl.path)) {\n                originalAjaxMethod(settings);\n            } else {\n                if (persistence.isDefined($pjqm.pageEntityName)) {\n                    var Page = persistence.define($pjqm.pageEntityName);\n                    Page.findBy($pjqm.pathField, settings.url, function(page) {\n                        if (page) {\n                            //\n                            // load page and images from persistencejs\n                            //\n                            if (settings.success) {\n                                var pos = 0, countOuter = 0, countInner = 0;\n                                var inStr = page[$pjqm.dataField](), outStr = \"\";\n                                var regExp = /(<img[^>]+src\\s*=\\s*[\\'\\\"])([^\\'\\\"]+)([\\'\\\"][^>]*>)/ig;\n                                var replaced = inStr.replace(regExp, function($0, $1, $2, $3, offset) {\n                                    countOuter++;\n                                    if (persistence.isDefined($pjqm.imageEntityName)) {\n                                        var Img = persistence.define($pjqm.imageEntityName);\n                                        Img.findBy($pjqm.pathField, expand(settings.url, $2), function(image){\n                                            countInner++;\n                                            if (image) {\n                                                var imgTagStr = $1 + image[$pjqm.dataField]() + $3;\n                                                outStr += inStr.substring(pos, offset) + imgTagStr;\n                                                pos = offset + imgTagStr.length;\n                                            } else {\n                                                outStr += inStr.substring(pos, offset) + imgTagStr;\n                                                pos = offset;\n                                            }\n                                            if (countInner == countOuter) {\n                                                settings.success(outStr);\n                                            }\n                                            return \"\";\n                                        });\n                                    } else {\n                                        outStr += inStr.substring(pos, offset) + imgTagStr;\n                                        pos = offset;\n                                    }\n                                });\n                                if (replaced == inStr) {\n                                    settings.success(inStr);\n                                } else if (!persistence.isDefined($pjqm.imageEntityName)) {\n                                    settings.success(outStr);\n                                };\n                            }\n                        } else {\n                            //\n                            // ajax-load page and persist page and images\n                            //\n                            originalAjaxMethod({\n                                url: settings.url,\n                                success: function(data) {\n                                    settings.success(data);\n                                    if (persistence.isDefined($pjqm.pageEntityName)) {\n                                        var entities = [], crawlImages = false;\n                                        var Page = persistence.define($pjqm.pageEntityName);\n                                        if (persistence.isDefined($pjqm.imageEntityName)) {\n                                            var Img = persistence.define($pjqm.imageEntityName), count = 0;\n                                            $(\"#\"+settings.url.replace(/\\//g,\"\\\\/\").replace(/\\./g,\"\\\\.\")+\" img\").each(function(i, img){\n                                                crawlImages = true;\n                                                count++;\n                                                $(img).load(function() {\n                                                  var obj = {}, parsedImgSrc = parseUri(img.src);\n                                                  obj[$pjqm.pathField] = parsedImgSrc.path;\n                                                  obj[$pjqm.dataField] = base64Image(img, getImageType(parsedImgSrc));\n                                                  entities.push(new Img(obj));\n\n                                                  if (crawlImages && (--count == 0)) {\n                                                      for (var j=0; j<entities.length; j++) {\n                                                          persistence.add(entities[j]);\n                                                      }\n                                                      persistence.flush();\n                                                  }\n                                                });\n                                                $(img).error(function() {\n                                                    crawlImages = false;\n                                                });\n                                            });\n                                        }\n\n                                        var obj = {};\n                                        obj[$pjqm.pathField] = settings.url;\n                                        obj[$pjqm.dataField] = data;\n\n                                        entities.push(new Page(obj));\n\n                                        if (!crawlImages) {\n                                            persistence.add(entities[0]);\n                                            persistence.flush();\n                                        }\n                                    }\n                                },\n                                error: settings.error\n                            });\n                        }\n                    });\n                } else {\n                    originalAjaxMethod(settings);\n                }\n            }\n        };\n    }\n})(jQuery);"
  },
  {
    "path": "lib/persistence.js",
    "content": "/**\n * Copyright (c) 2010 Zef Hemel <zef@zef.me>\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\nif (typeof exports !== 'undefined') {\n\texports.createPersistence = function() {\n\t\treturn initPersistence({})\n\t}\n\tvar singleton;\n\tif (typeof (exports.__defineGetter__) === 'function') {\n\t    exports.__defineGetter__(\"persistence\", function () {\n\t        if (!singleton)\n\t            singleton = exports.createPersistence();\n\t        return singleton;\n\t    });\n\t} else {\n\t    Object.defineProperty(exports, \"persistence\", {\n\t        get: function () {\n\t            if (!singleton)\n\t                singleton = exports.createPersistence();\n\t            return singleton;\n\t        },\n\t        enumerable: true, configurable: true\n\t    });\n\t}\n\n}\nelse {\n\twindow = window || {};\n\twindow.persistence = initPersistence(window.persistence || {});\n}\n\n\nfunction initPersistence(persistence) {\n\tif (persistence.isImmutable) // already initialized\n\t\treturn persistence;\n\n/**\n * Check for immutable fields\n */\npersistence.isImmutable = function(fieldName) {\n  return (fieldName == \"id\");\n};\n\n/**\n * Default implementation for entity-property\n */\npersistence.defineProp = function(scope, field, setterCallback, getterCallback) {\n    if (typeof (scope.__defineSetter__) === 'function' && typeof (scope.__defineGetter__) === 'function') {\n        scope.__defineSetter__(field, function (value) {\n            setterCallback(value);\n        });\n        scope.__defineGetter__(field, function () {\n            return getterCallback();\n        });\n    } else {\n        Object.defineProperty(scope, field, {\n            get: getterCallback,\n            set: function (value) {\n                setterCallback(value);\n            },\n            enumerable: true, configurable: true\n        });\n    }\n};\n\n/**\n * Default implementation for entity-property setter\n */\npersistence.set = function(scope, fieldName, value) {\n    if (persistence.isImmutable(fieldName)) throw new Error(\"immutable field: \"+fieldName);\n    scope[fieldName] = value;\n};\n\n/**\n * Default implementation for entity-property getter\n */\npersistence.get = function(arg1, arg2) {\n  return (arguments.length == 1) ? arg1 : arg1[arg2];\n};\n\n\n(function () {\n    var entityMeta = {};\n    var entityClassCache = {};\n    persistence.getEntityMeta = function() { return entityMeta; }\n\n    // Per-session data\n    persistence.trackedObjects = {};\n    persistence.objectsToRemove = {};\n    persistence.objectsRemoved = []; // {id: ..., type: ...}\n    persistence.globalPropertyListeners = {}; // EntityType__prop -> QueryColleciton obj\n    persistence.queryCollectionCache = {}; // entityName -> uniqueString -> QueryCollection\n\n    persistence.getObjectsToRemove = function() { return this.objectsToRemove; };\n    persistence.getTrackedObjects = function() { return this.trackedObjects; };\n\n    // Public Extension hooks\n    persistence.entityDecoratorHooks = [];\n    persistence.flushHooks = [];\n    persistence.schemaSyncHooks = [];\n\n    // Enable debugging (display queries using console.log etc)\n    persistence.debug = true;\n\n    persistence.subscribeToGlobalPropertyListener = function(coll, entityName, property) {\n      var key = entityName + '__' + property;\n      if(key in this.globalPropertyListeners) {\n        var listeners = this.globalPropertyListeners[key];\n        for(var i = 0; i < listeners.length; i++) {\n          if(listeners[i] === coll) {\n            return;\n          }\n        }\n        this.globalPropertyListeners[key].push(coll);\n      } else {\n        this.globalPropertyListeners[key] = [coll];\n      }\n    }\n\n    persistence.unsubscribeFromGlobalPropertyListener = function(coll, entityName, property) {\n      var key = entityName + '__' + property;\n      var listeners = this.globalPropertyListeners[key];\n      for(var i = 0; i < listeners.length; i++) {\n        if(listeners[i] === coll) {\n          listeners.splice(i, 1);\n          return;\n        }\n      }\n    }\n\n    persistence.propertyChanged = function(obj, property, oldValue, newValue) {\n      if(!this.trackedObjects[obj.id]) return; // not yet added, ignore for now\n\n      var entityName = obj._type;\n      var key = entityName + '__' + property;\n      if(key in this.globalPropertyListeners) {\n        var listeners = this.globalPropertyListeners[key];\n        for(var i = 0; i < listeners.length; i++) {\n          var coll = listeners[i];\n          var dummyObj = obj._data;\n          dummyObj[property] = oldValue;\n          var matchedBefore = coll._filter.match(dummyObj);\n          dummyObj[property] = newValue;\n          var matchedAfter = coll._filter.match(dummyObj);\n          if(matchedBefore != matchedAfter) {\n            coll.triggerEvent('change', coll, obj);\n          }\n        }\n      }\n    }\n\n    persistence.objectRemoved = function(obj) {\n      var entityName = obj._type;\n      if(this.queryCollectionCache[entityName]) {\n        var colls = this.queryCollectionCache[entityName];\n        for(var key in colls) {\n          if(colls.hasOwnProperty(key)) {\n            var coll = colls[key];\n            if(coll._filter.match(obj)) { // matched the filter -> was part of collection\n              coll.triggerEvent('change', coll, obj);\n            }\n          }\n        }\n      }\n    }\n\n    /**\n     * Retrieves metadata about entity, mostly for internal use\n     */\n    function getMeta(entityName) {\n      return entityMeta[entityName];\n    }\n\n    persistence.getMeta = getMeta;\n    \n\n    /**\n     * A database session\n     */\n    function Session(conn) {\n      this.trackedObjects = {};\n      this.objectsToRemove = {};\n      this.objectsRemoved = [];\n      this.globalPropertyListeners = {}; // EntityType__prop -> QueryColleciton obj\n      this.queryCollectionCache = {}; // entityName -> uniqueString -> QueryCollection\n      this.conn = conn;\n    }\n\n    Session.prototype = persistence; // Inherit everything from the root persistence object\n\n    persistence.Session = Session;\n\n    /**\n     * Define an entity\n     *\n     * @param entityName\n     *            the name of the entity (also the table name in the database)\n     * @param fields\n     *            an object with property names as keys and SQLite types as\n     *            values, e.g. {name: \"TEXT\", age: \"INT\"}\n     * @return the entity's constructor\n     */\n    persistence.define = function (entityName, fields) {\n      if (entityMeta[entityName]) { // Already defined, ignore\n        return getEntity(entityName);\n      }\n      var meta = {\n        name: entityName,\n        fields: fields,\n        isMixin: false,\n        indexes: [],\n        hasMany: {},\n        hasOne: {}\n      };\n      entityMeta[entityName] = meta;\n      return getEntity(entityName);\n    };\n\n    /**\n     * Checks whether an entity exists\n     *\n     * @param entityName\n     *            the name of the entity (also the table name in the database)\n     * @return `true` if the entity exists, otherwise `false`\n     */\n    persistence.isDefined = function (entityName) {\n        return !!entityMeta[entityName];\n    }\n\n    /**\n     * Define a mixin\n     *\n     * @param mixinName\n     *            the name of the mixin\n     * @param fields\n     *            an object with property names as keys and SQLite types as\n     *            values, e.g. {name: \"TEXT\", age: \"INT\"}\n     * @return the entity's constructor\n     */\n    persistence.defineMixin = function (mixinName, fields) {\n      var Entity = this.define(mixinName, fields);\n      Entity.meta.isMixin = true;\n      return Entity;\n    };\n\n    persistence.isTransaction = function(obj) {\n      return !obj || (obj && obj.executeSql);\n    };\n\n    persistence.isSession = function(obj) {\n      return !obj || (obj && obj.schemaSync);\n    };\n\n    /**\n     * Adds the object to tracked entities to be persisted\n     *\n     * @param obj\n     *            the object to be tracked\n     */\n    persistence.add = function (obj) {\n      if(!obj) return;\n      if (!this.trackedObjects[obj.id]) {\n        this.trackedObjects[obj.id] = obj;\n        if(obj._new) {\n          for(var p in obj._data) {\n            if(obj._data.hasOwnProperty(p)) {\n              this.propertyChanged(obj, p, undefined, obj._data[p]);\n            }\n          }\n        }\n      }\n      return this;\n    };\n\n    /**\n     * Marks the object to be removed (on next flush)\n     * @param obj object to be removed\n     */\n    persistence.remove = function(obj) {\n      if (obj._new) {\n        delete this.trackedObjects[obj.id];\n      } else {\n        if (!this.objectsToRemove[obj.id]) {\n          this.objectsToRemove[obj.id] = obj;\n        }\n        this.objectsRemoved.push({id: obj.id, entity: obj._type});\n      }\n      this.objectRemoved(obj);\n      return this;\n    };\n\n\n    /**\n     * Clean the persistence context of cached entities and such.\n     */\n    persistence.clean = function () {\n      this.trackedObjects = {};\n      this.objectsToRemove = {};\n      this.objectsRemoved = [];\n      this.globalPropertyListeners = {};\n      this.queryCollectionCache = {};\n    };\n\n    /**\n     * asynchronous sequential version of Array.prototype.forEach\n     * @param array the array to iterate over\n     * @param fn the function to apply to each item in the array, function\n     *        has two argument, the first is the item value, the second a\n     *        callback function\n     * @param callback the function to call when the forEach has ended\n     */\n    persistence.asyncForEach = function(array, fn, callback) {\n      array = array.slice(0); // Just to be sure\n      function processOne() {\n        var item = array.pop();\n        fn(item, function(result, err) {\n            if(array.length > 0) {\n              processOne();\n            } else {\n              callback(result, err);\n            }\n          });\n      }\n      if(array.length > 0) {\n        processOne();\n      } else {\n        callback();\n      }\n    };\n\n    /**\n     * asynchronous parallel version of Array.prototype.forEach\n     * @param array the array to iterate over\n     * @param fn the function to apply to each item in the array, function\n     *        has two argument, the first is the item value, the second a\n     *        callback function\n     * @param callback the function to call when the forEach has ended\n     */\n    persistence.asyncParForEach = function(array, fn, callback) {\n      var completed = 0;\n      var arLength = array.length;\n      if(arLength === 0) {\n        callback();\n      }\n      for(var i = 0; i < arLength; i++) {\n        fn(array[i], function(result, err) {\n            completed++;\n            if(completed === arLength) {\n              callback(result, err);\n            }\n          });\n      }\n    };\n\n    /**\n     * Retrieves or creates an entity constructor function for a given\n     * entity name\n     * @return the entity constructor function to be invoked with `new fn()`\n     */\n    function getEntity(entityName) {\n      if (entityClassCache[entityName]) {\n        return entityClassCache[entityName];\n      }\n      var meta = entityMeta[entityName];\n\n      /**\n       * @constructor\n       */\n      function Entity (session, obj, noEvents) {\n        var args = argspec.getArgs(arguments, [\n            { name: \"session\", optional: true, check: persistence.isSession, defaultValue: persistence },\n            { name: \"obj\", optional: true, check: function(obj) { return obj; }, defaultValue: {} }\n          ]);\n        if (meta.isMixin)\n          throw new Error(\"Cannot instantiate mixin\");\n        session = args.session;\n        obj = args.obj;\n\n        var that = this;\n        this.id = obj.id || persistence.createUUID();\n        this._new = true;\n        this._type = entityName;\n        this._dirtyProperties = {};\n        this._data = {};\n        this._data_obj = {}; // references to objects\n        this._session = session || persistence;\n        this.subscribers = {}; // observable\n\n        for ( var field in meta.fields) {\n          (function () {\n              if (meta.fields.hasOwnProperty(field)) {\n                var f = field; // Javascript scopes/closures SUCK\n                persistence.defineProp(that, f, function(val) {\n                    // setterCallback\n                    var oldValue = that._data[f];\n                    if(oldValue !== val || (oldValue && val && oldValue.getTime && val.getTime)) { // Don't mark properties as dirty and trigger events unnecessarily\n                      that._data[f] = val;\n                      that._dirtyProperties[f] = oldValue;\n                      that.triggerEvent('set', that, f, val);\n                      that.triggerEvent('change', that, f, val);\n                      session.propertyChanged(that, f, oldValue, val);\n                    }\n                  }, function() {\n                    // getterCallback\n                    return that._data[f];\n                  });\n                that._data[field] = defaultValue(meta.fields[field]);\n              }\n            }());\n        }\n\n        for ( var it in meta.hasOne) {\n          if (meta.hasOne.hasOwnProperty(it)) {\n            (function () {\n                var ref = it;\n                var mixinClass = meta.hasOne[it].type.meta.isMixin ? ref + '_class' : null;\n                persistence.defineProp(that, ref, function(val) {\n                    // setterCallback\n                    var oldValue = that._data[ref];\n                    var oldValueObj = that._data_obj[ref] || session.trackedObjects[that._data[ref]];\n                    if (val == null) {\n                      that._data[ref] = null;\n                      that._data_obj[ref] = undefined;\n                      if (mixinClass)\n                        that[mixinClass] = '';\n                    } else if (val.id) {\n                      that._data[ref] = val.id;\n                      that._data_obj[ref] = val;\n                      if (mixinClass)\n                        that[mixinClass] = val._type;\n                      session.add(val);\n                      session.add(that);\n                    } else { // let's assume it's an id\n                      that._data[ref] = val;\n                    }\n                    that._dirtyProperties[ref] = oldValue;\n                    that.triggerEvent('set', that, ref, val);\n                    that.triggerEvent('change', that, ref, val);\n                    // Inverse\n                    if(meta.hasOne[ref].inverseProperty) {\n                      var newVal = that[ref];\n                      if(newVal) {\n                        var inverse = newVal[meta.hasOne[ref].inverseProperty];\n                        if(inverse.list && inverse._filter) {\n                          inverse.triggerEvent('change', that, ref, val);\n                        }\n                      }\n                      if(oldValueObj) {\n                        var inverse = oldValueObj[meta.hasOne[ref].inverseProperty];\n                        if(inverse.list && inverse._filter) {\n                          inverse.triggerEvent('change', that, ref, val);\n                        }\n                      }\n                    }\n                  }, function() {\n                    // getterCallback\n                    if (!that._data[ref]) {\n                      return null;\n                    } else if(that._data_obj[ref] !== undefined) {\n                      return that._data_obj[ref];\n                    } else if(that._data[ref] && session.trackedObjects[that._data[ref]]) {\n                      that._data_obj[ref] = session.trackedObjects[that._data[ref]];\n                      return that._data_obj[ref];\n                    } else {\n                      throw new Error(\"Property '\" + ref + \"' of '\" + meta.name + \"' with id: \" + that._data[ref] + \" not fetched, either prefetch it or fetch it manually.\");\n                    }\n                  });\n              }());\n          }\n        }\n\n        for ( var it in meta.hasMany) {\n          if (meta.hasMany.hasOwnProperty(it)) {\n            (function () {\n                var coll = it;\n                if (meta.hasMany[coll].manyToMany) {\n                  persistence.defineProp(that, coll, function(val) {\n                      // setterCallback\n                      if(val && val._items) {\n                        // Local query collection, just add each item\n                        // TODO: this is technically not correct, should clear out existing items too\n                        var items = val._items;\n                        for(var i = 0; i < items.length; i++) {\n                          persistence.get(that, coll).add(items[i]);\n                        }\n                      } else {\n                        throw new Error(\"Not yet supported.\");\n                      }\n                    }, function() {\n                      // getterCallback\n                      if (that._data[coll]) {\n                        return that._data[coll];\n                      } else {\n                        var rel = meta.hasMany[coll];\n                        var inverseMeta = rel.type.meta;\n                        var inv = inverseMeta.hasMany[rel.inverseProperty];\n                        var direct = rel.mixin ? rel.mixin.meta.name : meta.name;\n                        var inverse = inv.mixin ? inv.mixin.meta.name : inverseMeta.name;\n\n                        var queryColl = new persistence.ManyToManyDbQueryCollection(session, inverseMeta.name);\n                        queryColl.initManyToMany(that, coll);\n                        queryColl._manyToManyFetch = {\n                            table: rel.tableName,\n                            prop: direct + '_' + coll,\n                            inverseProp: inverse + '_' + rel.inverseProperty,\n                            id: that.id\n                          };\n                        that._data[coll] = queryColl;\n                        return session.uniqueQueryCollection(queryColl);\n                      }\n                    });\n                } else { // one to many\n                  persistence.defineProp(that, coll, function(val) {\n                      // setterCallback\n                      if(val && val._items) {\n                        // Local query collection, just add each item\n                        // TODO: this is technically not correct, should clear out existing items too\n                        var items = val._items;\n                        for(var i = 0; i < items.length; i++) {\n                          persistence.get(that, coll).add(items[i]);\n                        }\n                      } else {\n                        throw new Error(\"Not yet supported.\");\n                      }\n                    }, function() {\n                      // getterCallback\n                      if (that._data[coll]) {\n                        return that._data[coll];\n                      } else {\n                        var queryColl = session.uniqueQueryCollection(new persistence.DbQueryCollection(session, meta.hasMany[coll].type.meta.name).filter(meta.hasMany[coll].inverseProperty, '=', that));\n                        that._data[coll] = queryColl;\n                        return queryColl;\n                      }\n                    });\n                }\n              }());\n          }\n        }\n\n        if(this.initialize) {\n          this.initialize();\n        }\n\n        for ( var f in obj) {\n          if (obj.hasOwnProperty(f)) {\n            if(f !== 'id') {\n              persistence.set(that, f, obj[f]);\n            }\n          }\n        }\n      } // Entity\n\n      Entity.prototype = new Observable();\n\n      Entity.meta = meta;\n\n      Entity.prototype.equals = function(other) {\n        return this.id == other.id;\n      };\n\n      Entity.prototype.toJSON = function() {\n        var json = {id: this.id};\n        for(var p in this._data) {\n          if(this._data.hasOwnProperty(p)) {\n            if (typeof this._data[p] == \"object\" && this._data[p] != null) {\n              if (this._data[p].toJSON != undefined) {\n                json[p] = this._data[p].toJSON();\n              }\n            } else {\n              json[p] = this._data[p];\n            }\n          }\n        }\n        return json;\n      };\n\n\n      /**\n       * Select a subset of data as a JSON structure (Javascript object)\n       *\n       * A property specification is passed that selects the\n       * properties to be part of the resulting JSON object. Examples:\n       *    ['id', 'name'] -> Will return an object with the id and name property of this entity\n       *    ['*'] -> Will return an object with all the properties of this entity, not recursive\n       *    ['project.name'] -> will return an object with a project property which has a name\n       *                        property containing the project name (hasOne relationship)\n       *    ['project.[id, name]'] -> will return an object with a project property which has an\n       *                              id and name property containing the project name\n       *                              (hasOne relationship)\n       *    ['tags.name'] -> will return an object with an array `tags` property containing\n       *                     objects each with a single property: name\n       *\n       * @param tx database transaction to use, leave out to start a new one\n       * @param props a property specification\n       * @param callback(result)\n       */\n      Entity.prototype.selectJSON = function(tx, props, callback) {\n        var that = this;\n        var args = argspec.getArgs(arguments, [\n            { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n            { name: \"props\", optional: false },\n            { name: \"callback\", optional: false }\n          ]);\n        tx = args.tx;\n        props = args.props;\n        callback = args.callback;\n\n        if(!tx) {\n          this._session.transaction(function(tx) {\n              that.selectJSON(tx, props, callback);\n            });\n          return;\n        }\n        var includeProperties = {};\n        props.forEach(function(prop) {\n            var current = includeProperties;\n            var parts = prop.split('.');\n            for(var i = 0; i < parts.length; i++) {\n              var part = parts[i];\n              if(i === parts.length-1) {\n                if(part === '*') {\n                  current.id = true;\n                  for(var p in meta.fields) {\n                    if(meta.fields.hasOwnProperty(p)) {\n                      current[p] = true;\n                    }\n                  }\n                  for(var p in meta.hasOne) {\n                    if(meta.hasOne.hasOwnProperty(p)) {\n                      current[p] = true;\n                    }\n                  }\n                  for(var p in meta.hasMany) {\n                    if(meta.hasMany.hasOwnProperty(p)) {\n                      current[p] = true;\n                    }\n                  }\n                } else if(part[0] === '[') {\n                  part = part.substring(1, part.length-1);\n                  var propList = part.split(/,\\s*/);\n                  propList.forEach(function(prop) {\n                      current[prop] = true;\n                    });\n                } else {\n                  current[part] = true;\n                }\n              } else {\n                current[part] = current[part] || {};\n                current = current[part];\n              }\n            }\n          });\n        buildJSON(this, tx, includeProperties, callback);\n      };\n\n      function buildJSON(that, tx, includeProperties, callback) {\n        var session = that._session;\n        var properties = [];\n        var meta = getMeta(that._type);\n        var fieldSpec = meta.fields;\n\n        for(var p in includeProperties) {\n          if(includeProperties.hasOwnProperty(p)) {\n            properties.push(p);\n          }\n        }\n\n        var cheapProperties = [];\n        var expensiveProperties = [];\n\n        properties.forEach(function(p) {\n            if(includeProperties[p] === true && !meta.hasMany[p]) { // simple, loaded field\n              cheapProperties.push(p);\n            } else {\n              expensiveProperties.push(p);\n            }\n          });\n\n        var itemData = that._data;\n        var item = {};\n\n        cheapProperties.forEach(function(p) {\n            if(p === 'id') {\n              item.id = that.id;\n            } else if(meta.hasOne[p]) {\n              item[p] = itemData[p] ? {id: itemData[p]} : null;\n            } else {\n              item[p] = persistence.entityValToJson(itemData[p], fieldSpec[p]);\n            }\n          });\n        properties = expensiveProperties.slice();\n\n        persistence.asyncForEach(properties, function(p, callback) {\n          if(meta.hasOne[p]) {\n            that.fetch(tx, p, function(obj) {\n                if(obj) {\n                  buildJSON(obj, tx, includeProperties[p], function(result) {\n                      item[p] = result;\n                      callback();\n                    });\n                } else {\n                  item[p] = null;\n                  callback();\n                }\n              });\n          } else if(meta.hasMany[p]) {\n            persistence.get(that, p).list(function(objs) {\n                item[p] = [];\n                persistence.asyncForEach(objs, function(obj, callback) {\n                    var obj = objs.pop();\n                    if(includeProperties[p] === true) {\n                      item[p].push({id: obj.id});\n                      callback();\n                    } else {\n                      buildJSON(obj, tx, includeProperties[p], function(result) {\n                          item[p].push(result);\n                          callback();\n                        });\n                    }\n                  }, callback);\n              });\n          }\n        }, function() {\n          callback(item);\n        });\n      }; // End of buildJson\n\n      Entity.prototype.fetch = function(tx, rel, callback) {\n        var args = argspec.getArgs(arguments, [\n            { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n            { name: 'rel', optional: false, check: argspec.hasType('string') },\n            { name: 'callback', optional: false, check: argspec.isCallback() }\n          ]);\n        tx = args.tx;\n        rel = args.rel;\n        callback = args.callback;\n\n        var that = this;\n        var session = this._session;\n\n        if(!tx) {\n          session.transaction(function(tx) {\n              that.fetch(tx, rel, callback);\n            });\n          return;\n        }\n        if(!this._data[rel]) { // null\n          if(callback) {\n            callback(null);\n          }\n        } else if(this._data_obj[rel]) { // already loaded\n          if(callback) {\n            callback(this._data_obj[rel]);\n          }\n        } else {\n          var type = meta.hasOne[rel].type;\n          if (type.meta.isMixin) {\n            type = getEntity(this._data[rel + '_class']);\n          }\n          type.load(session, tx, this._data[rel], function(obj) {\n              that._data_obj[rel] = obj;\n              if(callback) {\n                callback(obj);\n              }\n            });\n        }\n      };\n\n      /**\n       * Currently this is only required when changing JSON properties\n       */\n      Entity.prototype.markDirty = function(prop) {\n        this._dirtyProperties[prop] = true;\n      };\n\n      /**\n       * Returns a QueryCollection implementation matching all instances\n       * of this entity in the database\n       */\n      Entity.all = function(session) {\n        var args = argspec.getArgs(arguments, [\n            { name: 'session', optional: true, check: persistence.isSession, defaultValue: persistence }\n          ]);\n        session = args.session;\n        return session.uniqueQueryCollection(new AllDbQueryCollection(session, entityName));\n      };\n\n      Entity.fromSelectJSON = function(session, tx, jsonObj, callback) {\n        var args = argspec.getArgs(arguments, [\n            { name: 'session', optional: true, check: persistence.isSession, defaultValue: persistence },\n            { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n            { name: 'jsonObj', optional: false },\n            { name: 'callback', optional: false, check: argspec.isCallback() }\n          ]);\n        session = args.session;\n        tx = args.tx;\n        jsonObj = args.jsonObj;\n        callback = args.callback;\n\n        if(!tx) {\n          session.transaction(function(tx) {\n              Entity.fromSelectJSON(session, tx, jsonObj, callback);\n            });\n          return;\n        }\n\n        if(typeof jsonObj === 'string') {\n          jsonObj = JSON.parse(jsonObj);\n        }\n\n        if(!jsonObj) {\n          callback(null);\n          return;\n        }\n\n        function loadedObj(obj) {\n          if(!obj) {\n            obj = new Entity(session);\n            if(jsonObj.id) {\n              obj.id = jsonObj.id;\n            }\n          }\n          session.add(obj);\n          var expensiveProperties = [];\n          for(var p in jsonObj) {\n            if(jsonObj.hasOwnProperty(p)) {\n              if(p === 'id') {\n                continue;\n              } else if(meta.fields[p]) { // regular field\n                persistence.set(obj, p, persistence.jsonToEntityVal(jsonObj[p], meta.fields[p]));\n              } else if(meta.hasOne[p] || meta.hasMany[p]){\n                expensiveProperties.push(p);\n              }\n            }\n          }\n          persistence.asyncForEach(expensiveProperties, function(p, callback) {\n              if(meta.hasOne[p]) {\n                meta.hasOne[p].type.fromSelectJSON(session, tx, jsonObj[p], function(result) {\n                    persistence.set(obj, p, result);\n                    callback();\n                  });\n            } else if(meta.hasMany[p]) {\n              var coll = persistence.get(obj, p);\n              var ar = jsonObj[p].slice(0);\n              var PropertyEntity = meta.hasMany[p].type;\n              // get all current items\n              coll.list(tx, function(currentItems) {\n                  persistence.asyncForEach(ar, function(item, callback) {\n                      PropertyEntity.fromSelectJSON(session, tx, item, function(result) {\n                          // Check if not already in collection\n                          for(var i = 0; i < currentItems.length; i++) {\n                            if(currentItems[i].id === result.id) {\n                              callback();\n                              return;\n                            }\n                          }\n                          coll.add(result);\n                          callback();\n                        });\n                    }, function() {\n                      callback();\n                    });\n                });\n            }\n          }, function() {\n            callback(obj);\n          });\n        }\n        if(jsonObj.id) {\n          Entity.load(session, tx, jsonObj.id, loadedObj);\n        } else {\n          loadedObj(new Entity(session));\n        }\n      };\n\n      Entity.load = function(session, tx, id, callback) {\n        var args = argspec.getArgs(arguments, [\n            { name: 'session', optional: true, check: persistence.isSession, defaultValue: persistence },\n            { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n            { name: 'id', optional: false, check: argspec.hasType('string') },\n            { name: 'callback', optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n          ]);\n        Entity.findBy(args.session, args.tx, \"id\", args.id, args.callback);\n      };\n\n      Entity.findBy = function(session, tx, property, value, callback) {\n        var args = argspec.getArgs(arguments, [\n            { name: 'session', optional: true, check: persistence.isSession, defaultValue: persistence },\n            { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n            { name: 'property', optional: false, check: argspec.hasType('string') },\n            { name: 'value', optional: false },\n            { name: 'callback', optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n          ]);\n        session = args.session;\n        tx = args.tx;\n        property = args.property;\n        value = args.value;\n        callback = args.callback;\n\n        if(property === 'id' && value in session.trackedObjects) {\n          callback(session.trackedObjects[value]);\n          return;\n        }\n        if(!tx) {\n          session.transaction(function(tx) {\n              Entity.findBy(session, tx, property, value, callback);\n            });\n          return;\n        }\n        Entity.all(session).filter(property, \"=\", value).one(tx, function(obj) {\n            callback(obj);\n          });\n      }\n\n\n      Entity.index = function(cols,options) {\n        var opts = options || {};\n        if (typeof cols==\"string\") {\n          cols = [cols];\n        }\n        opts.columns = cols;\n        meta.indexes.push(opts);\n      };\n\n      /**\n       * Declares a one-to-many or many-to-many relationship to another entity\n       * Whether 1:N or N:M is chosed depends on the inverse declaration\n       * @param collName the name of the collection (becomes a property of\n         *   Entity instances\n         * @param otherEntity the constructor function of the entity to define\n         *   the relation to\n         * @param inverseRel the name of the inverse property (to be) defined on otherEntity\n         */\n      Entity.hasMany = function (collName, otherEntity, invRel) {\n        var otherMeta = otherEntity.meta;\n        if (otherMeta.hasMany[invRel]) {\n          // other side has declared it as a one-to-many relation too -> it's in\n          // fact many-to-many\n          var tableName = meta.name + \"_\" + collName + \"_\" + otherMeta.name;\n          var inverseTableName = otherMeta.name + '_' + invRel + '_' + meta.name;\n\n          if (tableName > inverseTableName) {\n            // Some arbitrary way to deterministically decide which table to generate\n            tableName = inverseTableName;\n          }\n          meta.hasMany[collName] = {\n            type: otherEntity,\n            inverseProperty: invRel,\n            manyToMany: true,\n            tableName: tableName\n          };\n          otherMeta.hasMany[invRel] = {\n            type: Entity,\n            inverseProperty: collName,\n            manyToMany: true,\n            tableName: tableName\n          };\n          delete meta.hasOne[collName];\n          delete meta.fields[collName + \"_class\"]; // in case it existed\n        } else {\n          meta.hasMany[collName] = {\n            type: otherEntity,\n            inverseProperty: invRel\n          };\n          otherMeta.hasOne[invRel] = {\n            type: Entity,\n            inverseProperty: collName\n          };\n          if (meta.isMixin)\n            otherMeta.fields[invRel + \"_class\"] = persistence.typeMapper ? persistence.typeMapper.classNameType : \"TEXT\";\n        }\n      }\n\n      Entity.hasOne = function (refName, otherEntity, inverseProperty) {\n        meta.hasOne[refName] = {\n          type: otherEntity,\n          inverseProperty: inverseProperty\n        };\n        if (otherEntity.meta.isMixin)\n          meta.fields[refName + \"_class\"] = persistence.typeMapper ? persistence.typeMapper.classNameType : \"TEXT\";\n      };\n\n      Entity.is = function(mixin){\n        var mixinMeta = mixin.meta;\n        if (!mixinMeta.isMixin)\n          throw new Error(\"not a mixin: \" + mixin);\n\n        mixin.meta.mixedIns = mixin.meta.mixedIns || [];\n        mixin.meta.mixedIns.push(meta);\n\n        for (var field in mixinMeta.fields) {\n          if (mixinMeta.fields.hasOwnProperty(field))\n            meta.fields[field] = mixinMeta.fields[field];\n        }\n        for (var it in mixinMeta.hasOne) {\n          if (mixinMeta.hasOne.hasOwnProperty(it))\n            meta.hasOne[it] = mixinMeta.hasOne[it];\n        }\n        for (var it in mixinMeta.hasMany) {\n          if (mixinMeta.hasMany.hasOwnProperty(it)) {\n            mixinMeta.hasMany[it].mixin = mixin;\n            meta.hasMany[it] = mixinMeta.hasMany[it];\n          }\n        }\n      }\n\n      // Allow decorator functions to add more stuff\n      var fns = persistence.entityDecoratorHooks;\n      for(var i = 0; i < fns.length; i++) {\n        fns[i](Entity);\n      }\n\n      entityClassCache[entityName] = Entity;\n      return Entity;\n    }\n\n    persistence.jsonToEntityVal = function(value, type) {\n      if(type) {\n        switch(type) {\n        case 'DATE':\n          if(typeof value === 'number') {\n            if (value > 1000000000000) {\n              // it's in milliseconds\n              return new Date(value); \n            } else {\n              return new Date(value * 1000); \n            }\n          } else {\n            return null;\n          }\n          break;\n        default:\n          return value;\n        }\n      } else {\n        return value;\n      }\n    };\n\n    persistence.entityValToJson = function(value, type) {\n      if(type) {\n        switch(type) {\n        case 'DATE':\n          if(value) {\n            value = new Date(value);\n            return Math.round(value.getTime() / 1000);\n          } else {\n            return null;\n          }\n          break;\n        default:\n          return value;\n        }\n      } else {\n        return value;\n      }\n    };\n\n    /**\n     * Dumps the entire database into an object (that can be serialized to JSON for instance)\n     * @param tx transaction to use, use `null` to start a new one\n     * @param entities a list of entity constructor functions to serialize, use `null` for all\n     * @param callback (object) the callback function called with the results.\n     */\n    persistence.dump = function(tx, entities, callback) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'entities', optional: true, check: function(obj) { return !obj || (obj && obj.length && !obj.apply); }, defaultValue: null },\n          { name: 'callback', optional: false, check: argspec.isCallback(), defaultValue: function(){} }\n        ]);\n      tx = args.tx;\n      entities = args.entities;\n      callback = args.callback;\n\n      if(!entities) { // Default: all entity types\n        entities = [];\n        for(var e in entityClassCache) {\n          if(entityClassCache.hasOwnProperty(e)) {\n            entities.push(entityClassCache[e]);\n          }\n        }\n      }\n\n      var result = {};\n      persistence.asyncParForEach(entities, function(Entity, callback) {\n          Entity.all().list(tx, function(all) {\n              var items = [];\n              persistence.asyncParForEach(all, function(e, callback) {\n                  var rec = {};\n                  var fields = Entity.meta.fields;\n                  for(var f in fields) {\n                    if(fields.hasOwnProperty(f)) {\n                      rec[f] = persistence.entityValToJson(e._data[f], fields[f]);\n                    }\n                  }\n                  var refs = Entity.meta.hasOne;\n                  for(var r in refs) {\n                    if(refs.hasOwnProperty(r)) {\n                      rec[r] = e._data[r];\n                    }\n                  }\n                  var colls = Entity.meta.hasMany;\n                  var collArray = [];\n                  for(var coll in colls) {\n                    if(colls.hasOwnProperty(coll)) {\n                      collArray.push(coll);\n                    }\n                  }\n                  persistence.asyncParForEach(collArray, function(collP, callback) {\n                      var coll = persistence.get(e, collP);\n                      coll.list(tx, function(results) {\n                          rec[collP] = results.map(function(r) { return r.id; });\n                          callback();\n                        });\n                    }, function() {\n                      rec.id = e.id;\n                      items.push(rec);\n                      callback();\n                    });\n                }, function() {\n                  result[Entity.meta.name] = items;\n                  callback();\n                });\n            });\n        }, function() {\n          callback(result);\n        });\n    };\n\n    /**\n     * Loads a set of entities from a dump object\n     * @param tx transaction to use, use `null` to start a new one\n     * @param dump the dump object\n     * @param callback the callback function called when done.\n     */\n    persistence.load = function(tx, dump, callback) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'dump', optional: false },\n          { name: 'callback', optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n        ]);\n      tx = args.tx;\n      dump = args.dump;\n      callback = args.callback;\n\n      var finishedCount = 0;\n      var collItemsToAdd = [];\n      var session = this;\n      for(var entityName in dump) {\n        if(dump.hasOwnProperty(entityName)) {\n          var Entity = getEntity(entityName);\n          var fields = Entity.meta.fields;\n          var instances = dump[entityName];\n          for(var i = 0; i < instances.length; i++) {\n            var instance = instances[i];\n            var ent = new Entity();\n            ent.id = instance.id;\n            for(var p in instance) {\n              if(instance.hasOwnProperty(p)) {\n                if (persistence.isImmutable(p)) {\n                  ent[p] = instance[p];\n                } else if(Entity.meta.hasMany[p]) { // collection\n                  var many = Entity.meta.hasMany[p];\n                  if(many.manyToMany && Entity.meta.name < many.type.meta.name) { // Arbitrary way to avoid double adding\n                    continue;\n                  }\n                  var coll = persistence.get(ent, p);\n                  if(instance[p].length > 0) {\n                    instance[p].forEach(function(it) {\n                        collItemsToAdd.push({Entity: Entity, coll: coll, id: it});\n                      });\n                  }\n                } else {\n                  persistence.set(ent, p, persistence.jsonToEntityVal(instance[p], fields[p]));\n                }\n              }\n            }\n            this.add(ent);\n          }\n        }\n      }\n      session.flush(tx, function() {\n          persistence.asyncForEach(collItemsToAdd, function(collItem, callback) {\n              collItem.Entity.load(session, tx, collItem.id, function(obj) {\n                  collItem.coll.add(obj);\n                  callback();\n                });\n            }, function() {\n              session.flush(tx, callback);\n            });\n        });\n    };\n\n    /**\n     * Dumps the entire database to a JSON string\n     * @param tx transaction to use, use `null` to start a new one\n     * @param entities a list of entity constructor functions to serialize, use `null` for all\n     * @param callback (jsonDump) the callback function called with the results.\n     */\n    persistence.dumpToJson = function(tx, entities, callback) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'entities', optional: true, check: function(obj) { return obj && obj.length && !obj.apply; }, defaultValue: null },\n          { name: 'callback', optional: false, check: argspec.isCallback(), defaultValue: function(){} }\n        ]);\n      tx = args.tx;\n      entities = args.entities;\n      callback = args.callback;\n      this.dump(tx, entities, function(obj) {\n          callback(JSON.stringify(obj));\n        });\n    };\n\n    /**\n     * Loads data from a JSON string (as dumped by `dumpToJson`)\n     * @param tx transaction to use, use `null` to start a new one\n     * @param jsonDump JSON string\n     * @param callback the callback function called when done.\n     */\n    persistence.loadFromJson = function(tx, jsonDump, callback) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'jsonDump', optional: false },\n          { name: 'callback', optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n        ]);\n      tx = args.tx;\n      jsonDump = args.jsonDump;\n      callback = args.callback;\n      this.load(tx, JSON.parse(jsonDump), callback);\n    };\n\n\n    /**\n     * Generates a UUID according to http://www.ietf.org/rfc/rfc4122.txt\n     */\n    function createUUID () {\n      if(persistence.typeMapper && persistence.typeMapper.newUuid) {\n        return persistence.typeMapper.newUuid();\n      }\n      var s = [];\n      var hexDigits = \"0123456789ABCDEF\";\n      for ( var i = 0; i < 32; i++) {\n        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);\n      }\n      s[12] = \"4\";\n      s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1);\n\n      var uuid = s.join(\"\");\n      return uuid;\n    }\n\n    persistence.createUUID = createUUID;\n\n\n    function defaultValue(type) {\n      if(persistence.typeMapper && persistence.typeMapper.defaultValue) {\n        return persistence.typeMapper.defaultValue(type);\n      }\n      switch(type) {\n      case \"TEXT\": return \"\";\n      case \"BOOL\": return false;\n      default:\n        if(type.indexOf(\"INT\") !== -1) {\n          return 0;\n        } else if(type.indexOf(\"CHAR\") !== -1) {\n          return \"\";\n        } else {\n          return null;\n        }\n      }\n    }\n\n    function arrayContains(ar, item) {\n      var l = ar.length;\n      for(var i = 0; i < l; i++) {\n        var el = ar[i];\n        if(el.equals && el.equals(item)) {\n          return true;\n        } else if(el === item) {\n          return true;\n        }\n      }\n      return false;\n    }\n\n    function arrayRemove(ar, item) {\n      var l = ar.length;\n      for(var i = 0; i < l; i++) {\n        var el = ar[i];\n        if(el.equals && el.equals(item)) {\n          ar.splice(i, 1);\n          return;\n        } else if(el === item) {\n          ar.splice(i, 1);\n          return;\n        }\n      }\n    }\n\n    ////////////////// QUERY COLLECTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n\n    function Subscription(obj, eventType, fn) {\n      this.obj = obj;\n      this.eventType = eventType;\n      this.fn = fn;\n    }\n\n    Subscription.prototype.unsubscribe = function() {\n      this.obj.removeEventListener(this.eventType, this.fn);\n    };\n\n    /**\n     * Simple observable function constructor\n     * @constructor\n     */\n    function Observable() {\n      this.subscribers = {};\n    }\n\n    Observable.prototype.addEventListener = function (eventType, fn) {\n      if (!this.subscribers[eventType]) {\n        this.subscribers[eventType] = [];\n      }\n      this.subscribers[eventType].push(fn);\n      return new Subscription(this, eventType, fn);\n    };\n\n    Observable.prototype.removeEventListener = function(eventType, fn) {\n      var subscribers = this.subscribers[eventType];\n      for ( var i = 0; i < subscribers.length; i++) {\n        if(subscribers[i] == fn) {\n          this.subscribers[eventType].splice(i, 1);\n          return true;\n        }\n      }\n      return false;\n    };\n\n    Observable.prototype.triggerEvent = function (eventType) {\n      if (!this.subscribers[eventType]) { // No subscribers to this event type\n        return;\n      }\n      var subscribers = this.subscribers[eventType].slice(0);\n      for(var i = 0; i < subscribers.length; i++) {\n        subscribers[i].apply(null, arguments);\n      }\n    };\n\n    /*\n     * Each filter has 4 methods:\n     * - sql(prefix, values) -- returns a SQL representation of this filter,\n     *     possibly pushing additional query arguments to `values` if ?'s are used\n     *     in the query\n     * - match(o) -- returns whether the filter matches the object o.\n     * - makeFit(o) -- attempts to adapt the object o in such a way that it matches\n     *     this filter.\n     * - makeNotFit(o) -- the oppositive of makeFit, makes the object o NOT match\n     *     this filter\n     */\n\n    /**\n     * Default filter that does not filter on anything\n     * currently it generates a 1=1 SQL query, which is kind of ugly\n     */\n    function NullFilter () {\n    }\n\n    NullFilter.prototype.match = function (o) {\n      return true;\n    };\n\n    NullFilter.prototype.makeFit = function(o) {\n    };\n\n    NullFilter.prototype.makeNotFit = function(o) {\n    };\n\n    NullFilter.prototype.toUniqueString = function() {\n      return \"NULL\";\n    };\n\n    NullFilter.prototype.subscribeGlobally = function() { };\n\n    NullFilter.prototype.unsubscribeGlobally = function() { };\n\n    /**\n     * Filter that makes sure that both its left and right filter match\n     * @param left left-hand filter object\n     * @param right right-hand filter object\n     */\n    function AndFilter (left, right) {\n      this.left = left;\n      this.right = right;\n    }\n\n    AndFilter.prototype.match = function (o) {\n      return this.left.match(o) && this.right.match(o);\n    };\n\n    AndFilter.prototype.makeFit = function(o) {\n      this.left.makeFit(o);\n      this.right.makeFit(o);\n    };\n\n    AndFilter.prototype.makeNotFit = function(o) {\n      this.left.makeNotFit(o);\n      this.right.makeNotFit(o);\n    };\n\n    AndFilter.prototype.toUniqueString = function() {\n      return this.left.toUniqueString() + \" AND \" + this.right.toUniqueString();\n    };\n\n    AndFilter.prototype.subscribeGlobally = function(coll, entityName) { \n      this.left.subscribeGlobally(coll, entityName);\n      this.right.subscribeGlobally(coll, entityName);\n    };\n\n    AndFilter.prototype.unsubscribeGlobally = function(coll, entityName) { \n      this.left.unsubscribeGlobally(coll, entityName);\n      this.right.unsubscribeGlobally(coll, entityName);\n    };\n\n    /**\n     * Filter that makes sure that either its left and right filter match\n     * @param left left-hand filter object\n     * @param right right-hand filter object\n     */\n    function OrFilter (left, right) {\n      this.left = left;\n      this.right = right;\n    }\n\n    OrFilter.prototype.match = function (o) {\n      return this.left.match(o) || this.right.match(o);\n    };\n\n    OrFilter.prototype.makeFit = function(o) {\n      this.left.makeFit(o);\n      this.right.makeFit(o);\n    };\n\n    OrFilter.prototype.makeNotFit = function(o) {\n      this.left.makeNotFit(o);\n      this.right.makeNotFit(o);\n    };\n\n    OrFilter.prototype.toUniqueString = function() {\n      return this.left.toUniqueString() + \" OR \" + this.right.toUniqueString();\n    };\n\n    OrFilter.prototype.subscribeGlobally = function(coll, entityName) { \n      this.left.subscribeGlobally(coll, entityName);\n      this.right.subscribeGlobally(coll, entityName);\n    };\n\n    OrFilter.prototype.unsubscribeGlobally = function(coll, entityName) { \n      this.left.unsubscribeGlobally(coll, entityName);\n      this.right.unsubscribeGlobally(coll, entityName);\n    };\n\n    /**\n     * Filter that checks whether a certain property matches some value, based on an\n     * operator. Supported operators are '=', '!=', '<', '<=', '>' and '>='.\n     * @param property the property name\n     * @param operator the operator to compare with\n     * @param value the literal value to compare to\n     */\n    function PropertyFilter (property, operator, value) {\n      this.property = property;\n      this.operator = operator.toLowerCase();\n      this.value = value;\n    }\n\n    PropertyFilter.prototype.match = function (o) {\n      var value = this.value;\n      var propValue = persistence.get(o, this.property);\n      if(value && value.getTime) { // DATE\n        // TODO: Deal with arrays of dates for 'in' and 'not in'\n        value = Math.round(value.getTime() / 1000) * 1000; // Deal with precision\n        if(propValue && propValue.getTime) { // DATE\n          propValue = Math.round(propValue.getTime() / 1000) * 1000; // Deal with precision\n        }\n      }\n      switch (this.operator) {\n      case '=':\n        return propValue === value;\n        break;\n      case '!=':\n        return propValue !== value;\n        break;\n      case '<':\n        return propValue < value;\n        break;\n      case '<=':\n        return propValue <= value;\n        break;\n      case '>':\n        return propValue > value;\n        break;\n      case '>=':\n        return propValue >= value;\n        break;\n      case 'in':\n        return arrayContains(value, propValue);\n        break;\n      case 'not in':\n        return !arrayContains(value, propValue);\n        break;\n      }\n    };\n\n    PropertyFilter.prototype.makeFit = function(o) {\n      if(this.operator === '=') {\n        persistence.set(o, this.property, this.value);\n      } else {\n        throw new Error(\"Sorry, can't perform makeFit for other filters than =\");\n      }\n    };\n\n    PropertyFilter.prototype.makeNotFit = function(o) {\n      if(this.operator === '=') {\n        persistence.set(o, this.property, null);\n      } else {\n        throw new Error(\"Sorry, can't perform makeNotFit for other filters than =\");\n      }\n    };\n\n    PropertyFilter.prototype.subscribeGlobally = function(coll, entityName) {\n      persistence.subscribeToGlobalPropertyListener(coll, entityName, this.property);\n    };\n\n    PropertyFilter.prototype.unsubscribeGlobally = function(coll, entityName) {\n      persistence.unsubscribeFromGlobalPropertyListener(coll, entityName, this.property);\n    };\n\n    PropertyFilter.prototype.toUniqueString = function() {\n      var val = this.value;\n      if(val && val._type) {\n        val = val.id;\n      }\n      return this.property + this.operator + val;\n    };\n\n    persistence.NullFilter = NullFilter;\n    persistence.AndFilter = AndFilter;\n    persistence.OrFilter = OrFilter;\n    persistence.PropertyFilter = PropertyFilter;\n\n    /**\n     * Ensure global uniqueness of query collection object\n     */\n    persistence.uniqueQueryCollection = function(coll) {\n      var entityName = coll._entityName;\n      if(coll._items) { // LocalQueryCollection\n        return coll;\n      }\n      if(!this.queryCollectionCache[entityName]) {\n        this.queryCollectionCache[entityName] = {};\n      }\n      var uniqueString = coll.toUniqueString();\n      if(!this.queryCollectionCache[entityName][uniqueString]) {\n        this.queryCollectionCache[entityName][uniqueString] = coll;\n      }\n      return this.queryCollectionCache[entityName][uniqueString];\n    }\n\n    /**\n     * The constructor function of the _abstract_ QueryCollection\n     * DO NOT INSTANTIATE THIS\n     * @constructor\n     */\n    function QueryCollection () {\n    }\n\n    QueryCollection.prototype = new Observable();\n\n    QueryCollection.prototype.oldAddEventListener = QueryCollection.prototype.addEventListener;\n\n    QueryCollection.prototype.setupSubscriptions = function() { \n      this._filter.subscribeGlobally(this, this._entityName);\n    };\n\n    QueryCollection.prototype.teardownSubscriptions = function() { \n      this._filter.unsubscribeGlobally(this, this._entityName);\n    };\n\n    QueryCollection.prototype.addEventListener = function(eventType, fn) {\n      var that = this;\n      var subscription = this.oldAddEventListener(eventType, fn);\n      if(this.subscribers[eventType].length === 1) { // first subscriber\n        this.setupSubscriptions();\n      }\n      subscription.oldUnsubscribe = subscription.unsubscribe;\n      subscription.unsubscribe = function() {\n        this.oldUnsubscribe();\n\n        if(that.subscribers[eventType].length === 0) { // last subscriber\n          that.teardownSubscriptions();\n        }\n      };\n      return subscription;\n    };\n\n    /**\n     * Function called when session is flushed, returns list of SQL queries to execute\n     * (as [query, arg] tuples)\n     */\n    QueryCollection.prototype.persistQueries = function() { return []; };\n\n    /**\n     * Invoked by sub-classes to initialize the query collection\n     */\n    QueryCollection.prototype.init = function (session, entityName, constructor) {\n      this._filter = new NullFilter();\n      this._orderColumns = []; // tuples of [column, ascending]\n      this._prefetchFields = [];\n      this._entityName = entityName;\n      this._constructor = constructor;\n      this._limit = -1;\n      this._skip = 0;\n      this._reverse = false;\n      this._session = session || persistence;\n      // For observable\n      this.subscribers = {};\n    }\n\n    QueryCollection.prototype.toUniqueString = function() {\n      var s = this._constructor.name + \": \" + this._entityName;\n      s += '|Filter:';\n      var values = [];\n      s += this._filter.toUniqueString();\n      s += '|Values:';\n      for(var i = 0; i < values.length; i++) {\n        s += values + \"|^|\";\n      }\n      s += '|Order:';\n      for(var i = 0; i < this._orderColumns.length; i++) {\n        var col = this._orderColumns[i];\n        s += col[0] + \", \" + col[1] + \", \" + col[2];\n      }\n      s += '|Prefetch:';\n      for(var i = 0; i < this._prefetchFields.length; i++) {\n        s += this._prefetchFields[i];\n      }\n      s += '|Limit:';\n      s += this._limit;\n      s += '|Skip:';\n      s += this._skip;\n      s += '|Reverse:';\n      s += this._reverse;\n      return s;\n    };\n\n    /**\n     * Creates a clone of this query collection\n     * @return a clone of the collection\n     */\n    QueryCollection.prototype.clone = function (cloneSubscribers) {\n      var c = new (this._constructor)(this._session, this._entityName);\n      c._filter = this._filter;\n      c._prefetchFields = this._prefetchFields.slice(0); // clone\n      c._orderColumns = this._orderColumns.slice(0);\n      c._limit = this._limit;\n      c._skip = this._skip;\n      c._reverse = this._reverse;\n      if(cloneSubscribers) {\n        var subscribers = {};\n        for(var eventType in this.subscribers) {\n          if(this.subscribers.hasOwnProperty(eventType)) {\n            subscribers[eventType] = this.subscribers[eventType].slice(0);\n          }\n        }\n        c.subscribers = subscribers; //this.subscribers;\n      } else {\n        c.subscribers = this.subscribers;\n      }\n      return c;\n    };\n\n    /**\n     * Returns a new query collection with a property filter condition added\n     * @param property the property to filter on\n     * @param operator the operator to use\n     * @param value the literal value that the property should match\n     * @return the query collection with the filter added\n     */\n    QueryCollection.prototype.filter = function (property, operator, value) {\n      var c = this.clone(true);\n      c._filter = new AndFilter(this._filter, new PropertyFilter(property,\n          operator, value));\n      // Add global listener (TODO: memory leak waiting to happen!)\n      var session = this._session;\n      c = session.uniqueQueryCollection(c);\n      //session.subscribeToGlobalPropertyListener(c, this._entityName, property);\n      return session.uniqueQueryCollection(c);\n    };\n\n    /**\n     * Returns a new query collection with an OR condition between the\n     * current filter and the filter specified as argument\n     * @param filter the other filter\n     * @return the new query collection\n     */\n    QueryCollection.prototype.or = function (filter) {\n      var c = this.clone(true);\n      c._filter = new OrFilter(this._filter, filter);\n      return this._session.uniqueQueryCollection(c);\n    };\n\n    /**\n     * Returns a new query collection with an AND condition between the\n     * current filter and the filter specified as argument\n     * @param filter the other filter\n     * @return the new query collection\n     */\n    QueryCollection.prototype.and = function (filter) {\n      var c = this.clone(true);\n      c._filter = new AndFilter(this._filter, filter);\n      return this._session.uniqueQueryCollection(c);\n    };\n\n    /**\n     * Returns a new query collection with an ordering imposed on the collection\n     * @param property the property to sort on\n     * @param ascending should the order be ascending (= true) or descending (= false)\n     * @param caseSensitive should the order be case sensitive (= true) or case insensitive (= false)\n     *        note: using case insensitive ordering for anything other than TEXT fields yields\n     *        undefinded behavior\n     * @return the query collection with imposed ordering\n     */\n    QueryCollection.prototype.order = function (property, ascending, caseSensitive) {\n      ascending = ascending === undefined ? true : ascending;\n      caseSensitive = caseSensitive === undefined ? true : caseSensitive;\n      var c = this.clone();\n      c._orderColumns.push( [ property, ascending, caseSensitive ]);\n      return this._session.uniqueQueryCollection(c);\n    };\n\n    /**\n     * Returns a new query collection will limit its size to n items\n     * @param n the number of items to limit it to\n     * @return the limited query collection\n     */\n    QueryCollection.prototype.limit = function(n) {\n      var c = this.clone();\n      c._limit = n;\n      return this._session.uniqueQueryCollection(c);\n    };\n\n    /**\n     * Returns a new query collection which will skip the first n results\n     * @param n the number of results to skip\n     * @return the query collection that will skip n items\n     */\n    QueryCollection.prototype.skip = function(n) {\n      var c = this.clone();\n      c._skip = n;\n      return this._session.uniqueQueryCollection(c);\n    };\n\n    /**\n     * Returns a new query collection which reverse the order of the result set\n     * @return the query collection that will reverse its items\n     */\n    QueryCollection.prototype.reverse = function() {\n      var c = this.clone();\n      c._reverse = true;\n      return this._session.uniqueQueryCollection(c);\n    };\n\n    /**\n     * Returns a new query collection which will prefetch a certain object relationship.\n     * Only works with 1:1 and N:1 relations.\n     * Relation must target an entity, not a mix-in.\n     * @param rel the relation name of the relation to prefetch\n     * @return the query collection prefetching `rel`\n     */\n    QueryCollection.prototype.prefetch = function (rel) {\n      var c = this.clone();\n      c._prefetchFields.push(rel);\n      return this._session.uniqueQueryCollection(c);\n    };\n\n\n    /**\n     * Select a subset of data, represented by this query collection as a JSON\n     * structure (Javascript object)\n     *\n     * @param tx database transaction to use, leave out to start a new one\n     * @param props a property specification\n     * @param callback(result)\n     */\n    QueryCollection.prototype.selectJSON = function(tx, props, callback) {\n      var args = argspec.getArgs(arguments, [\n          { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: \"props\", optional: false },\n          { name: \"callback\", optional: false }\n        ]);\n      var session = this._session;\n      var that = this;\n      tx = args.tx;\n      props = args.props;\n      callback = args.callback;\n\n      if(!tx) {\n        session.transaction(function(tx) {\n            that.selectJSON(tx, props, callback);\n          });\n        return;\n      }\n      var Entity = getEntity(this._entityName);\n      // TODO: This could do some clever prefetching to make it more efficient\n      this.list(function(items) {\n          var resultArray = [];\n          persistence.asyncForEach(items, function(item, callback) {\n              item.selectJSON(tx, props, function(obj) {\n                  resultArray.push(obj);\n                  callback();\n                });\n            }, function() {\n              callback(resultArray);\n            });\n        });\n    };\n\n    /**\n     * Adds an object to a collection\n     * @param obj the object to add\n     */\n    QueryCollection.prototype.add = function(obj) {\n      if(!obj.id || !obj._type) {\n        throw new Error(\"Cannot add object of non-entity type onto collection.\");\n      }\n      this._session.add(obj);\n      this._filter.makeFit(obj);\n      this.triggerEvent('add', this, obj);\n      this.triggerEvent('change', this, obj);\n    }\n    \n    /**\n     * Adds an an array of objects to a collection\n     * @param obj the object to add\n     */\n    QueryCollection.prototype.addAll = function(objs) {\n      for(var i = 0; i < objs.length; i++) {\n        var obj = objs[i];\n        this._session.add(obj);\n        this._filter.makeFit(obj);\n        this.triggerEvent('add', this, obj);\n      }\n      this.triggerEvent('change', this);\n    }\n\n    /**\n     * Removes an object from a collection\n     * @param obj the object to remove from the collection\n     */\n    QueryCollection.prototype.remove = function(obj) {\n      if(!obj.id || !obj._type) {\n        throw new Error(\"Cannot remove object of non-entity type from collection.\");\n      }\n      this._filter.makeNotFit(obj);\n      this.triggerEvent('remove', this, obj);\n      this.triggerEvent('change', this, obj);\n    }\n\n\n    /**\n     * A database implementation of the QueryCollection\n     * @param entityName the name of the entity to create the collection for\n     * @constructor\n     */\n    function DbQueryCollection (session, entityName) {\n      this.init(session, entityName, DbQueryCollection);\n    }\n\n    /**\n     * Execute a function for each item in the list\n     * @param tx the transaction to use (or null to open a new one)\n     * @param eachFn (elem) the function to be executed for each item\n     */\n    QueryCollection.prototype.each = function (tx, eachFn) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'eachFn', optional: true, check: argspec.isCallback() }\n        ]);\n      tx = args.tx;\n      eachFn = args.eachFn;\n\n      this.list(tx, function(results) {\n          for(var i = 0; i < results.length; i++) {\n            eachFn(results[i]);\n          }\n        });\n    }\n\n    // Alias\n    QueryCollection.prototype.forEach = QueryCollection.prototype.each;\n\n    QueryCollection.prototype.one = function (tx, oneFn) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'oneFn', optional: false, check: argspec.isCallback() }\n        ]);\n      tx = args.tx;\n      oneFn = args.oneFn;\n\n      var that = this;\n\n      this.limit(1).list(tx, function(results) {\n          if(results.length === 0) {\n            oneFn(null);\n          } else {\n            oneFn(results[0]);\n          }\n        });\n    }\n\n    DbQueryCollection.prototype = new QueryCollection();\n\n\n    /**\n     * An implementation of QueryCollection, that is used\n     * to represent all instances of an entity type\n     * @constructor\n     */\n    function AllDbQueryCollection (session, entityName) {\n      this.init(session, entityName, AllDbQueryCollection);\n    }\n\n    AllDbQueryCollection.prototype = new DbQueryCollection();\n\n    AllDbQueryCollection.prototype.add = function(obj) {\n      this._session.add(obj);\n      this.triggerEvent('add', this, obj);\n      this.triggerEvent('change', this, obj);\n    };\n\n    AllDbQueryCollection.prototype.remove = function(obj) {\n      this._session.remove(obj);\n      this.triggerEvent('remove', this, obj);\n      this.triggerEvent('change', this, obj);\n    };\n\n    /**\n     * A ManyToMany implementation of QueryCollection\n     * @constructor\n     */\n    function ManyToManyDbQueryCollection (session, entityName) {\n      this.init(session, entityName, persistence.ManyToManyDbQueryCollection);\n      this._localAdded = [];\n      this._localRemoved = [];\n    }\n\n    ManyToManyDbQueryCollection.prototype = new DbQueryCollection();\n\n    ManyToManyDbQueryCollection.prototype.initManyToMany = function(obj, coll) {\n      this._obj = obj;\n      this._coll = coll;\n    };\n\n    ManyToManyDbQueryCollection.prototype.add = function(obj) {\n      if(!arrayContains(this._localAdded, obj)) {\n        this._session.add(obj);\n        this._localAdded.push(obj);\n        this.triggerEvent('add', this, obj);\n        this.triggerEvent('change', this, obj);\n      }\n    };\n\n    ManyToManyDbQueryCollection.prototype.addAll = function(objs) {\n      for(var i = 0; i < objs.length; i++) {\n        var obj = objs[i];\n        if(!arrayContains(this._localAdded, obj)) {\n          this._session.add(obj);\n          this._localAdded.push(obj);\n          this.triggerEvent('add', this, obj);\n        }\n      }\n      this.triggerEvent('change', this);\n    }\n\n    ManyToManyDbQueryCollection.prototype.clone = function() {\n      var c = DbQueryCollection.prototype.clone.call(this);\n      c._localAdded = this._localAdded;\n      c._localRemoved = this._localRemoved;\n      c._obj = this._obj;\n      c._coll = this._coll;\n      return c;\n    };\n\n    ManyToManyDbQueryCollection.prototype.remove = function(obj) {\n      if(arrayContains(this._localAdded, obj)) { // added locally, can just remove it from there\n        arrayRemove(this._localAdded, obj);\n      } else if(!arrayContains(this._localRemoved, obj)) {\n        this._localRemoved.push(obj);\n      }\n      this.triggerEvent('remove', this, obj);\n      this.triggerEvent('change', this, obj);\n    };\n\n    ////////// Local implementation of QueryCollection \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n\n    function LocalQueryCollection(initialArray) {\n      this.init(persistence, null, LocalQueryCollection);\n      this._items = initialArray || [];\n    }\n\n    LocalQueryCollection.prototype = new QueryCollection();\n\n    LocalQueryCollection.prototype.clone = function() {\n      var c = DbQueryCollection.prototype.clone.call(this);\n      c._items = this._items;\n      return c;\n    };\n\n    LocalQueryCollection.prototype.add = function(obj) {\n      if(!arrayContains(this._items, obj)) {\n        this._session.add(obj);\n        this._items.push(obj);\n        this.triggerEvent('add', this, obj);\n        this.triggerEvent('change', this, obj);\n      }\n    };\n\n    LocalQueryCollection.prototype.addAll = function(objs) {\n      for(var i = 0; i < objs.length; i++) {\n        var obj = objs[i];\n        if(!arrayContains(this._items, obj)) {\n          this._session.add(obj);\n          this._items.push(obj);\n          this.triggerEvent('add', this, obj);\n        }\n      }\n      this.triggerEvent('change', this);\n    }\n\n    LocalQueryCollection.prototype.remove = function(obj) {\n      var items = this._items;\n      for(var i = 0; i < items.length; i++) {\n        if(items[i] === obj) {\n          this._items.splice(i, 1);\n          this.triggerEvent('remove', this, obj);\n          this.triggerEvent('change', this, obj);\n        }\n      }\n    };\n\n    LocalQueryCollection.prototype.list = function(tx, callback) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'callback', optional: true, check: argspec.isCallback() }\n        ]);\n      callback = args.callback;\n\n      if(!callback || callback.executeSql) { // first argument is transaction\n        callback = arguments[1]; // set to second argument\n      }\n      var array = this._items.slice(0);\n      var that = this;\n      var results = [];\n      for(var i = 0; i < array.length; i++) {\n        if(this._filter.match(array[i])) {\n          results.push(array[i]);\n        }\n      }\n      results.sort(function(a, b) {\n          for(var i = 0; i < that._orderColumns.length; i++) {\n            var col = that._orderColumns[i][0];\n            var asc = that._orderColumns[i][1];\n            var sens = that._orderColumns[i][2];\n            var aVal = persistence.get(a, col);\n            var bVal = persistence.get(b, col);\n            if (!sens) {\n              aVal = aVal.toLowerCase();\n              bVal = bVal.toLowerCase();\n            }\n            if(aVal < bVal) {\n              return asc ? -1 : 1;\n            } else if(aVal > bVal) {\n              return asc ? 1 : -1;\n            }\n          }\n          return 0;\n        });\n      if(this._skip) {\n        results.splice(0, this._skip);\n      }\n      if(this._limit > -1) {\n        results = results.slice(0, this._limit);\n      }\n      if(this._reverse) {\n        results.reverse();\n      }\n      if(callback) {\n        callback(results);\n      } else {\n        return results;\n      }\n    };\n\n    LocalQueryCollection.prototype.destroyAll = function(callback) {\n      if(!callback || callback.executeSql) { // first argument is transaction\n        callback = arguments[1]; // set to second argument\n      }\n      this._items = [];\n      this.triggerEvent('change', this);\n      if(callback) callback();\n    };\n\n    LocalQueryCollection.prototype.count = function(tx, callback) {\n      var args = argspec.getArgs(arguments, [\n          { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n          { name: 'callback', optional: true, check: argspec.isCallback() }\n        ]);\n      tx = args.tx;\n      callback = args.callback;\n\n      var result = this.list();\n\n      if(callback) {\n        callback(result.length);\n      } else {\n        return result.length;\n      }\n    };\n\n    persistence.QueryCollection             = QueryCollection;\n    persistence.DbQueryCollection           = DbQueryCollection;\n    persistence.ManyToManyDbQueryCollection = ManyToManyDbQueryCollection;\n    persistence.LocalQueryCollection        = LocalQueryCollection;\n    persistence.Observable                  = Observable;\n    persistence.Subscription                = Subscription;\n    persistence.AndFilter                   = AndFilter;\n    persistence.OrFilter                    = OrFilter;\n    persistence.PropertyFilter              = PropertyFilter;\n  }());\n\n// ArgSpec.js library: http://github.com/zefhemel/argspecjs\nvar argspec = {};\n\n(function() {\n    argspec.getArgs = function(args, specs) {\n      var argIdx = 0;\n      var specIdx = 0;\n      var argObj = {};\n      while(specIdx < specs.length) {\n        var s = specs[specIdx];\n        var a = args[argIdx];\n        if(s.optional) {\n          if(a !== undefined && s.check(a)) {\n            argObj[s.name] = a;\n            argIdx++;\n            specIdx++;\n          } else {\n            if(s.defaultValue !== undefined) {\n              argObj[s.name] = s.defaultValue;\n            }\n            specIdx++;\n          }\n        } else {\n          if(s.check && !s.check(a)) {\n            throw new Error(\"Invalid value for argument: \" + s.name + \" Value: \" + a);\n          }\n          argObj[s.name] = a;\n          specIdx++;\n          argIdx++;\n        }\n      }\n      return argObj;\n    }\n\n    argspec.hasProperty = function(name) {\n      return function(obj) {\n        return obj && obj[name] !== undefined;\n      };\n    }\n\n    argspec.hasType = function(type) {\n      return function(obj) {\n        return typeof obj === type;\n      };\n    }\n\n    argspec.isCallback = function() {\n      return function(obj) {\n        return obj && obj.apply;\n      };\n    }\n  }());\n\npersistence.argspec = argspec;\n\n  return persistence;\n} // end of createPersistence\n\n\n\n// JSON2 library, source: http://www.JSON.org/js.html\n// Most modern browsers already support this natively, but mobile\n// browsers often don't, hence this implementation\n// Relevant APIs:\n//    JSON.stringify(value, replacer, space)\n//    JSON.parse(text, reviver)\n\nif(typeof JSON === 'undefined') {\n  JSON = {};\n}\n//var JSON = typeof JSON === 'undefined' ? window.JSON : {};\nif (!JSON.stringify) {\n  (function () {\n      function f(n) {\n        return n < 10 ? '0' + n : n;\n      }\n      if (typeof Date.prototype.toJSON !== 'function') {\n\n        Date.prototype.toJSON = function (key) {\n\n          return isFinite(this.valueOf()) ?\n          this.getUTCFullYear()   + '-' +\n            f(this.getUTCMonth() + 1) + '-' +\n            f(this.getUTCDate())      + 'T' +\n            f(this.getUTCHours())     + ':' +\n            f(this.getUTCMinutes())   + ':' +\n            f(this.getUTCSeconds())   + 'Z' : null;\n        };\n\n        String.prototype.toJSON =\n          Number.prototype.toJSON =\n          Boolean.prototype.toJSON = function (key) {\n            return this.valueOf();\n          };\n      }\n\n      var cx = /[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n      escapable = /[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n      gap, indent,\n      meta = {\n        '\\b': '\\\\b',\n        '\\t': '\\\\t',\n        '\\n': '\\\\n',\n        '\\f': '\\\\f',\n        '\\r': '\\\\r',\n        '\"' : '\\\\\"',\n        '\\\\': '\\\\\\\\'\n      },\n      rep;\n\n      function quote(string) {\n        escapable.lastIndex = 0;\n        return escapable.test(string) ?\n        '\"' + string.replace(escapable, function (a) {\n            var c = meta[a];\n            return typeof c === 'string' ? c :\n            '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n          }) + '\"' :\n        '\"' + string + '\"';\n      }\n\n\n      function str(key, holder) {\n        var i, k, v, length, mind = gap, partial, value = holder[key];\n\n        if (value && typeof value === 'object' &&\n          typeof value.toJSON === 'function') {\n          value = value.toJSON(key);\n        }\n\n        if (typeof rep === 'function') {\n          value = rep.call(holder, key, value);\n        }\n\n        switch (typeof value) {\n        case 'string':\n          return quote(value);\n        case 'number':\n          return isFinite(value) ? String(value) : 'null';\n        case 'boolean':\n        case 'null':\n          return String(value);\n        case 'object':\n          if (!value) {\n            return 'null';\n          }\n\n          gap += indent;\n          partial = [];\n\n          if (Object.prototype.toString.apply(value) === '[object Array]') {\n            length = value.length;\n            for (i = 0; i < length; i += 1) {\n              partial[i] = str(i, value) || 'null';\n            }\n\n            v = partial.length === 0 ? '[]' :\n            gap ? '[\\n' + gap +\n              partial.join(',\\n' + gap) + '\\n' +\n              mind + ']' :\n            '[' + partial.join(',') + ']';\n            gap = mind;\n            return v;\n          }\n\n          if (rep && typeof rep === 'object') {\n            length = rep.length;\n            for (i = 0; i < length; i += 1) {\n              k = rep[i];\n              if (typeof k === 'string') {\n                v = str(k, value);\n                if (v) {\n                  partial.push(quote(k) + (gap ? ': ' : ':') + v);\n                }\n              }\n            }\n          } else {\n            for (k in value) {\n              if (Object.hasOwnProperty.call(value, k)) {\n                v = str(k, value);\n                if (v) {\n                  partial.push(quote(k) + (gap ? ': ' : ':') + v);\n                }\n              }\n            }\n          }\n\n          v = partial.length === 0 ? '{}' :\n          gap ? '{\\n' + gap + partial.join(',\\n' + gap) + '\\n' +\n            mind + '}' : '{' + partial.join(',') + '}';\n          gap = mind;\n          return v;\n        }\n      }\n\n      if (typeof JSON.stringify !== 'function') {\n        JSON.stringify = function (value, replacer, space) {\n          var i;\n          gap = '';\n          indent = '';\n          if (typeof space === 'number') {\n            for (i = 0; i < space; i += 1) {\n              indent += ' ';\n            }\n          } else if (typeof space === 'string') {\n            indent = space;\n          }\n\n          rep = replacer;\n          if (replacer && typeof replacer !== 'function' &&\n            (typeof replacer !== 'object' ||\n              typeof replacer.length !== 'number')) {\n            throw new Error('JSON.stringify');\n          }\n\n          return str('', {'': value});\n        };\n      }\n\n      if (typeof JSON.parse !== 'function') {\n        JSON.parse = function (text, reviver) {\n          var j;\n          function walk(holder, key) {\n            var k, v, value = holder[key];\n            if (value && typeof value === 'object') {\n              for (k in value) {\n                if (Object.hasOwnProperty.call(value, k)) {\n                  v = walk(value, k);\n                  if (v !== undefined) {\n                    value[k] = v;\n                  } else {\n                    delete value[k];\n                  }\n                }\n              }\n            }\n            return reviver.call(holder, key, value);\n          }\n\n          cx.lastIndex = 0;\n          if (cx.test(text)) {\n            text = text.replace(cx, function (a) {\n                return '\\\\u' +\n                  ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n              });\n          }\n\n          if (/^[\\],:{}\\s]*$/.\n          test(text.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').\n            replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').\n            replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n            j = eval('(' + text + ')');\n            return typeof reviver === 'function' ?\n            walk({'': j}, '') : j;\n          }\n          throw new SyntaxError('JSON.parse');\n        };\n      }\n    }());\n}\n\n"
  },
  {
    "path": "lib/persistence.migrations.js",
    "content": "/**\n * @license\n * Copyright (c) 2010 Fábio Rehm <fgrehm@gmail.com>\n * \n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n * \n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\nif(!window.persistence) { // persistence.js not loaded!\n  throw new Error(\"persistence.js should be loaded before persistence.migrations.js\");\n}\n\n(function() {\n  \n    var Migrator = {\n      migrations: [],\n      \n      version: function(callback) {\n        persistence.transaction(function(t){\n          t.executeSql('SELECT current_version FROM schema_version', null, function(result){\n            if (result.length == 0) {\n              t.executeSql('INSERT INTO schema_version VALUES (0)', null, function(){\n                callback(0);\n              });\n            } else {\n              callback(result[0].current_version);\n            }\n          });\n        });\n      },\n      \n      setVersion: function(v, callback) {\n        persistence.transaction(function(t){\n          t.executeSql('UPDATE schema_version SET current_version = ?', [v], function(){\n            Migrator._version = v;\n            if (callback) callback();\n          });\n        });\n      },\n      \n      setup: function(callback) {\n        persistence.transaction(function(t){\n          t.executeSql('CREATE TABLE IF NOT EXISTS schema_version (current_version INTEGER)', null, function(){\n            // Creates a dummy migration just to force setting schema version when cleaning DB\n            Migrator.migration(0, { up: function() { }, down: function() { } });\n            if (callback) callback();\n          });\n        });\n      },\n      \n      // Method should only be used for testing\n      reset: function(callback) {\n        // Creates a dummy migration just to force setting schema version when cleaning DB\n        Migrator.migrations = [];\n        Migrator.migration(0, { up: function() { }, down: function() { } });\n        Migrator.setVersion(0, callback);\n      },\n      \n      migration: function(version, actions) {\n        Migrator.migrations[version] = new Migration(version, actions);\n        return Migrator.migrations[version];\n      },\n      \n      migrateUpTo: function(version, callback) {\n        var migrationsToRun = [];\n        \n        function migrateOne() {\n          var migration = migrationsToRun.pop();\n          \n          if (!migration) callback();\n          \n          migration.up(function(){\n            if (migrationsToRun.length > 0) {\n              migrateOne();\n            } else if (callback) {\n              callback();\n            }\n          });\n        }\n        \n        this.version(function(currentVersion){\n          for (var v = currentVersion+1; v <= version; v++)\n            migrationsToRun.unshift(Migrator.migrations[v]);\n          \n          if (migrationsToRun.length > 0) {\n            migrateOne();\n          } else if (callback) {\n            callback();\n          }\n        });\n      },\n      \n      migrateDownTo: function(version, callback) {\n        var migrationsToRun = [];\n        \n        function migrateOne() {\n          var migration = migrationsToRun.pop();\n          \n          if (!migration) callback();\n          \n          migration.down(function(){\n            if (migrationsToRun.length > 0) {\n              migrateOne();\n            } else if (callback) {\n              callback();\n            }\n          });\n        }\n        \n        this.version(function(currentVersion){\n          for (var v = currentVersion; v > version; v--)\n            migrationsToRun.unshift(Migrator.migrations[v]);\n          \n          if (migrationsToRun.length > 0) {\n            migrateOne();\n          } else if (callback) {\n            callback();\n          }\n        });\n      },\n      \n      migrate: function(version, callback) {\n        if ( arguments.length === 1 ) {\n          callback = version;\n          version = this.migrations.length-1;\n        }\n        \n        this.version(function(curVersion){\n          if (curVersion < version)\n            Migrator.migrateUpTo(version, callback);\n          else if (curVersion > version)\n            Migrator.migrateDownTo(version, callback);\n          else\n            callback();\n        });\n      }\n    }\n    \n    var Migration = function(version, body) {\n      this.version = version;\n      // TODO check if actions contains up and down methods\n      this.body = body;\n      this.actions = [];\n    };\n    \n    Migration.prototype.executeActions = function(callback, customVersion) {\n      var actionsToRun = this.actions;\n      var version = (customVersion!==undefined) ? customVersion : this.version;\n      \n      persistence.transaction(function(tx){\n        function nextAction() {\n          if (actionsToRun.length == 0)\n            Migrator.setVersion(version, callback);\n          else {\n            var action = actionsToRun.pop();\n            action(tx, nextAction);\n          }\n        }\n        \n        nextAction();\n      });\n    }\n    \n    Migration.prototype.up = function(callback) {\n      if (this.body.up) this.body.up.apply(this);\n      this.executeActions(callback);\n    }\n    \n    Migration.prototype.down = function(callback) {\n      if (this.body.down) this.body.down.apply(this);\n      this.executeActions(callback, this.version-1);\n    }\n    \n    Migration.prototype.createTable = function(tableName, callback) {\n      var table = new ColumnsHelper();\n      \n      if (callback) callback(table);\n      \n      var column;\n      var sql = 'CREATE TABLE ' + tableName + ' (id VARCHAR(32) PRIMARY KEY';\n      while (column = table.columns.pop())\n        sql += ', ' + column;\n      \n      this.executeSql(sql + ')');\n    }\n    \n    Migration.prototype.dropTable = function(tableName) {\n      var sql = 'DROP TABLE ' + tableName;\n      this.executeSql(sql);\n    }\n    \n    Migration.prototype.addColumn = function(tableName, columnName, columnType) {\n      var sql = 'ALTER TABLE ' + tableName + ' ADD ' + columnName + ' ' + columnType;\n      this.executeSql(sql);\n    }\n    \n    Migration.prototype.removeColumn = function(tableName, columnName) {\n      this.action(function(tx, nextCommand){\n        var sql = 'select sql from sqlite_master where type = \"table\" and name == \"'+tableName+'\"';\n        tx.executeSql(sql, null, function(result){\n          var columns = new RegExp(\"CREATE TABLE `\\\\w+` |\\\\w+ \\\\((.+)\\\\)\").exec(result[0].sql)[1].split(', ');\n          var selectColumns = [];\n          var columnsSql = [];\n          \n          for (var i = 0; i < columns.length; i++) {\n            var colName = new RegExp(\"((`\\\\w+`)|(\\\\w+)) .+\").exec(columns[i])[1];\n            if (colName == columnName) continue;\n            \n            columnsSql.push(columns[i]);\n            selectColumns.push(colName);\n          }\n          columnsSql = columnsSql.join(', ');\n          selectColumns = selectColumns.join(', ');\n          \n          var queries = [];\n          queries.unshift([\"ALTER TABLE \" + tableName + \" RENAME TO \" + tableName + \"_bkp;\", null]);\n          queries.unshift([\"CREATE TABLE \" + tableName + \" (\" + columnsSql + \");\", null]);\n          queries.unshift([\"INSERT INTO \" + tableName + \" SELECT \" + selectColumns + \" FROM \" + tableName + \"_bkp;\", null]);\n          queries.unshift([\"DROP TABLE \" + tableName + \"_bkp;\", null]);\n          \n          persistence.executeQueriesSeq(tx, queries, nextCommand);\n        });\n      });\n    }\n    \n    Migration.prototype.addIndex = function(tableName, columnName, unique) {\n      var sql = 'CREATE ' + (unique === true ? 'UNIQUE' : '') + ' INDEX ' + tableName + '_' + columnName + ' ON ' + tableName + ' (' + columnName + ')';\n      this.executeSql(sql);\n    }\n    \n    Migration.prototype.removeIndex = function(tableName, columnName) {\n      var sql = 'DROP INDEX ' + tableName + '_' + columnName;\n      this.executeSql(sql);\n    }\n    \n    Migration.prototype.executeSql = function(sql, args) {\n      this.action(function(tx, nextCommand){\n        tx.executeSql(sql, args, nextCommand);\n      });\n    }\n    \n    Migration.prototype.action = function(callback) {\n      this.actions.unshift(callback);\n    }\n    \n    var ColumnsHelper = function() {\n      this.columns = [];\n    }\n    \n    ColumnsHelper.prototype.text = function(columnName) {\n      this.columns.unshift(columnName + ' TEXT');\n    }\n    \n    ColumnsHelper.prototype.integer = function(columnName) {\n      this.columns.unshift(columnName + ' INT');\n    }\n    \n    ColumnsHelper.prototype.real = function(columnName) {\n      this.columns.unshift(columnName + ' REAL');\n    }\n    \n    ColumnsHelper.prototype['boolean'] = function(columnName) {\n      this.columns.unshift(columnName + ' BOOL');\n    }\n    \n    ColumnsHelper.prototype.date = function(columnName) {\n      this.columns.unshift(columnName + ' DATE');\n    }\n    \n    ColumnsHelper.prototype.json = function(columnName) {\n      this.columns.unshift(columnName + ' TEXT');\n    }\n    \n    // Makes Migrator and Migration available to tests\n    persistence.migrations = {};\n    persistence.migrations.Migrator = Migrator;\n    persistence.migrations.Migration = Migration;\n    persistence.migrations.init = function() { Migrator.setup.apply(Migrator, Array.prototype.slice.call(arguments, 0))};\n    \n    persistence.migrate = function() { Migrator.migrate.apply(Migrator, Array.prototype.slice.call(arguments, 0))};\n    persistence.defineMigration = function() { Migrator.migration.apply(Migrator, Array.prototype.slice.call(arguments, 0))};\n    \n}());\n"
  },
  {
    "path": "lib/persistence.pool.js",
    "content": "var sys = require('sys');\nvar mysql = require('mysql');\n\nfunction log(o) {\n  sys.print(sys.inspect(o) + \"\\n\");\n}\n\nfunction ConnectionPool(getSession, initialPoolSize) {\n  this.newConnection = getSession;\n  this.pool = [];\n  for(var i = 0; i < initialPoolSize; i++) {\n    this.pool.push({available: true, session: getSession()});\n  }\n}\n\nConnectionPool.prototype.obtain = function() {\n  var session = null;\n  for(var i = 0; i < this.pool.length; i++) {\n    if(this.pool[i].available) {\n      var pool = this.pool[i];\n      session = pool.session;\n      pool.available = false;\n      pool.claimed = new Date();\n      break;\n    }\n  }\n  if(!session) {\n    session = getSession();\n    this.pool.push({available: false, session: session, claimed: new Date() });\n  }\n};\n\nConnectionPool.prototype.release = function(session) {\n  for(var i = 0; i < this.pool.length; i++) {\n    if(this.pool[i].session === session) {\n      var pool = this.pool[i];\n      pool.available = true;\n      pool.claimed = null;\n      return;\n    }\n  }\n  return false;\n};\n\nexports.ConnectionPool = ConnectionPool;\n"
  },
  {
    "path": "lib/persistence.search.js",
    "content": "/**\n * @license\n * Copyright (c) 2010 Zef Hemel <zef@zef.me>\n * \n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n * \n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\ntry {\n  if(!window) {\n    window = {};\n  }\n} catch(e) {\n  window = {};\n  exports.console = console;\n}\n\nvar persistence = (window && window.persistence) ? window.persistence : {}; \n\npersistence.search = {};\n\npersistence.search.config = function(persistence, dialect) {\n  var filteredWords = {'and':true, 'the': true, 'are': true};\n\n  var argspec = persistence.argspec;\n\n  function normalizeWord(word, filterShortWords) {\n    if(!(word in filteredWords || (filterShortWords && word.length < 3))) {\n      word = word.replace(/ies$/, 'y');\n      word = word.length > 3 ? word.replace(/s$/, '') : word;\n      return word;\n    } else {\n      return false;\n    }\n  }\n\n  /**\n   * Does extremely basic tokenizing of text. Also includes some basic stemming.\n   */\n  function searchTokenizer(text) {\n    var words = text.toLowerCase().split(/[^\\w\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]+/);\n    var wordDict = {};\n    // Prefixing words with _ to also index Javascript keywords and special fiels like 'constructor'\n    for(var i = 0; i < words.length; i++) {\n      var normalizedWord = normalizeWord(words[i]);\n      if(normalizedWord) {\n        var word = '_' + normalizedWord;\n        // Some extremely basic stemming\n        if(word in wordDict) {\n          wordDict[word]++;\n        } else {\n          wordDict[word] = 1;\n        }\n      }\n    }\n    return wordDict;\n  }\n\n  /**\n   * Parses a search query and returns it as list SQL parts later to be OR'ed or AND'ed.\n   */\n  function searchPhraseParser(query, indexTbl, prefixByDefault) {\n    query = query.toLowerCase().replace(/['\"]/, '').replace(/(^\\s+|\\s+$)/g, '');\n    var words = query.split(/\\s+/);\n    var sqlParts = [];\n    var restrictedToColumn = null;\n    for(var i = 0; i < words.length; i++) {\n      var word = normalizeWord(words[i]);\n      if(!word) {\n        continue;\n      }\n      if(word.search(/:$/) !== -1) {\n        restrictedToColumn = word.substring(0, word.length-1);\n        continue;\n      } \n      var sql = '(';\n      if(word.search(/\\*/) !== -1) {\n        sql += \"`\" + indexTbl + \"`.`word` LIKE '\" + word.replace(/\\*/g, '%') + \"'\";\n      } else if(prefixByDefault) {\n        sql += \"`\" + indexTbl + \"`.`word` LIKE '\" + word + \"%'\";\n      } else {\n        sql += \"`\" + indexTbl + \"`.`word` = '\" + word + \"'\";\n      }\n      if(restrictedToColumn) {\n        sql += ' AND `' + indexTbl + \"`.`prop` = '\" + restrictedToColumn + \"'\";\n      }\n      sql += ')';\n      sqlParts.push(sql);\n    }\n    return sqlParts.length === 0 ? [\"1=1\"] : sqlParts;\n  }\n\n  var queryCollSubscribers = {}; // entityName -> subscription obj\n  persistence.searchQueryCollSubscribers = queryCollSubscribers;\n\n  function SearchFilter(query, entityName) {\n    this.query = query;\n    this.entityName = entityName;\n  }\n\n  SearchFilter.prototype.match = function (o) {\n    var meta = persistence.getMeta(this.entityName);\n    var query = this.query.toLowerCase();\n    var text = '';\n    for(var p in o) {\n      if(meta.textIndex.hasOwnProperty(p)) {\n        if(o[p]) {\n          text += o[p];\n        }\n      }\n    }\n    text = text.toLowerCase();\n    return text && text.indexOf(query) !== -1;\n  }\n\n  SearchFilter.prototype.sql = function (o) {\n    return \"1=1\";\n  }\n\n  SearchFilter.prototype.subscribeGlobally = function(coll, entityName) {\n    var meta = persistence.getMeta(entityName);\n    for(var p in meta.textIndex) {\n      if(meta.textIndex.hasOwnProperty(p)) {\n        persistence.subscribeToGlobalPropertyListener(coll, entityName, p);\n      }\n    }\n  };\n\n  SearchFilter.prototype.unsubscribeGlobally = function(coll, entityName) {\n    var meta = persistence.getMeta(entityName);\n    for(var p in meta.textIndex) {\n      if(meta.textIndex.hasOwnProperty(p)) {\n        persistence.unsubscribeFromGlobalPropertyListener(coll, entityName, p);\n      }\n    }\n  };\n\n  SearchFilter.prototype.toUniqueString = function() {\n    return \"SEARCH: \" + this.query;\n  }\n\n  function SearchQueryCollection(session, entityName, query, prefixByDefault) {\n    this.init(session, entityName, SearchQueryCollection);\n    this.subscribers = queryCollSubscribers[entityName];\n    this._filter = new SearchFilter(query, entityName);\n\n\n    if(query) {\n      this._additionalJoinSqls.push(', `' + entityName + '_Index`');\n      this._additionalWhereSqls.push('`root`.id = `' + entityName + '_Index`.`entityId`');\n      this._additionalWhereSqls.push('(' + searchPhraseParser(query, entityName + '_Index', prefixByDefault).join(' OR ') + ')');\n      this._additionalGroupSqls.push(' GROUP BY (`' + entityName + '_Index`.`entityId`)');\n      this._additionalGroupSqls.push(' ORDER BY SUM(`' + entityName + '_Index`.`occurrences`) DESC');\n    }\n  }\n\n  SearchQueryCollection.prototype = new persistence.DbQueryCollection();\n\n  SearchQueryCollection.prototype.oldClone = SearchQueryCollection.prototype.clone;\n\n\n  SearchQueryCollection.prototype.clone = function() {\n    var clone = this.oldClone(false);\n    var entityName = this._entityName;\n    clone.subscribers = queryCollSubscribers[entityName];\n    return clone;\n  };\n\n  SearchQueryCollection.prototype.order = function() {\n    throw new Error(\"Imposing additional orderings is not support for search query collections.\");\n  };\n\n  /*\n  SearchQueryCollection.prototype.filter = function (property, operator, value) {\n    var c = this.clone();\n    c._filter = new persistence.AndFilter(this._filter, new persistence.PropertyFilter(property, operator, value));\n    // Add global listener (TODO: memory leak waiting to happen!)\n    //session.subscribeToGlobalPropertyListener(c, this._entityName, property);\n    return c;\n  };\n  */\n\n  persistence.entityDecoratorHooks.push(function(Entity) {\n      /**\n       * Declares a property to be full-text indexed.\n       */\n      Entity.textIndex = function(prop) {\n        if(!Entity.meta.textIndex) {\n          Entity.meta.textIndex = {};\n        }\n        Entity.meta.textIndex[prop] = true;\n        // Subscribe\n        var entityName = Entity.meta.name;\n        if(!queryCollSubscribers[entityName]) {\n          queryCollSubscribers[entityName] = {};\n        }\n      };\n\n      /**\n       * Returns a query collection representing the result of a search\n       * @param query an object with the following fields:\n       */\n      Entity.search = function(session, query, prefixByDefault) {\n        var args = argspec.getArgs(arguments, [\n            { name: 'session', optional: true, check: function(obj) { return obj.schemaSync; }, defaultValue: persistence },\n            { name: 'query', optional: false, check: argspec.hasType('string') },\n            { name: 'prefixByDefault', optional: false }\n          ]);\n        session = args.session;\n        query = args.query;\n        prefixByDefault = args.prefixByDefault;\n\n        return session.uniqueQueryCollection(new SearchQueryCollection(session, Entity.meta.name, query, prefixByDefault));\n      };\n    });\n\n  persistence.schemaSyncHooks.push(function(tx) {\n      var entityMeta = persistence.getEntityMeta();\n      var queries = [];\n      for(var entityName in entityMeta) {\n        var meta = entityMeta[entityName];\n        if(meta.textIndex) {\n          queries.push([dialect.createTable(entityName + '_Index', [['entityId', 'VARCHAR(32)'], ['prop', 'VARCHAR(30)'], ['word', 'VARCHAR(100)'], ['occurrences', 'INT']]), null]);\n          queries.push([dialect.createIndex(entityName + '_Index', ['prop', 'word']), null]);\n          queries.push([dialect.createIndex(entityName + '_Index', ['word']), null]);\n          persistence.generatedTables[entityName + '_Index'] = true;\n        }\n      }\n      queries.reverse();\n      persistence.executeQueriesSeq(tx, queries);\n    });\n\n\n  persistence.flushHooks.push(function(session, tx, callback) {\n      var queries = [];\n      for (var id in session.getTrackedObjects()) {\n        if (session.getTrackedObjects().hasOwnProperty(id)) {\n          var obj = session.getTrackedObjects()[id];\n          var meta = session.define(obj._type).meta;\n          var indexTbl = obj._type + '_Index';\n          if(meta.textIndex) {\n            for ( var p in obj._dirtyProperties) {\n              if (obj._dirtyProperties.hasOwnProperty(p) && p in meta.textIndex) {\n                queries.push(['DELETE FROM `' + indexTbl + '` WHERE `entityId` = ? AND `prop` = ?', [id, p]]);\n                var occurrences = searchTokenizer(obj._data[p]);\n                for(var word in occurrences) {\n                  if(occurrences.hasOwnProperty(word)) {\n                    queries.push(['INSERT INTO `' + indexTbl + '` VALUES (?, ?, ?, ?)', [obj.id, p, word.substring(1), occurrences[word]]]);\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n      for (var id in persistence.getObjectsToRemove()) {\n        if (persistence.getObjectsToRemove().hasOwnProperty(id)) {\n          var obj = persistence.getObjectsToRemove()[id];\n          var meta = persistence.getEntityMeta()[obj._type];\n          if(meta.textIndex) {\n            queries.push(['DELETE FROM `' + obj._type + '_Index` WHERE `entityId` = ?', [id]]);\n          }\n        }\n      }\n      queries.reverse();\n      persistence.executeQueriesSeq(tx, queries, callback);\n    });\n};\n\nif(typeof exports === 'object') {\n  exports.config = persistence.search.config;\n}\n\n"
  },
  {
    "path": "lib/persistence.store.appengine.js",
    "content": "var jdatastore = Packages.com.google.appengine.api.datastore,\n    JDatastoreServiceFactory = jdatastore.DatastoreServiceFactory,\n    JKeyFactory = jdatastore.KeyFactory,\n    JDatastoreService = jdatastore.DatastoreService,\n    JFilterOperator = jdatastore.Query.FilterOperator,\n    JSortDirection = jdatastore.Query.SortDirection,\n    JQuery = jdatastore.Query,\n    JInteger = java.lang.Integer;\n\nexports.config = function(persistence) {\n  var argspec = persistence.argspec;\n\n  exports.getSession = function() {\n    var session = new persistence.Session();\n    session.dsService = JDatastoreServiceFactory.getDatastoreService();\n    session.transaction = function (fn) {\n      fn({executeSql: function() {}});\n    };\n\n    session.close = function() { };\n    return session;\n  };\n\n  /**\n   * Converts a value from the data store to a value suitable for the entity\n   * (also does type conversions, if necessary)\n   */\n  function dbValToEntityVal(val, type) {\n    if (val === null || val === undefined) {\n      return val;\n    }\n    switch (type) {\n    case 'DATE':\n      // SQL is in seconds and JS in miliseconds\n        if (val > 1000000000000) {\n          // usually in seconds, but sometimes it's milliseconds\n          return new Date(parseInt(val, 10));\n        } else {\n          return new Date(parseInt(val, 10) * 1000);\n        }\n    case 'BOOL':\n      return val === 1 || val === '1';\n      break;\n    case 'INT':\n      return +val;\n      break;\n    case 'BIGINT':\n      return +val;\n      break;\n    case 'JSON':\n      if (val) {\n        return JSON.parse(val);\n      }\n      else {\n        return val;\n      }\n      break;\n    default:\n      return val;\n    }\n  }\n\n  /**\n   * Converts an entity value to a data store value, inverse of\n   *   dbValToEntityVal\n   */\n  function entityValToDbVal(val, type) {\n    if (val === undefined || val === null) {\n      return null;\n    } else if (type === 'JSON' && val) {\n      return JSON.stringify(val);\n    } else if (val.id) {\n      return val.id;\n    } else if (type === 'BOOL') {\n      return (val === 'false') ? 0 : (val ? 1 : 0);\n    } else if (type === 'DATE' || val.getTime) {\n      val = new Date(val);\n      return new JInteger(Math.round(val.getTime() / 1000));\n    } else {\n      return val;\n    }\n  }\n\n  /**\n   * Converts a data store entity to an entity object\n   */\n  function aeEntityToEntity(session, aeEnt, Entity) {\n    if(!aeEnt) return null;\n\n    var o = new Entity(session);\n    var meta = Entity.meta;\n    o.id = aeEnt.key.name;\n    var propMap = aeEnt.properties;\n    for(var prop in Iterator(propMap.keySet())) {\n      persistence.set(o, prop, dbValToEntityVal(propMap.get(prop), meta.fields[prop]));\n    }\n    return o;\n  }\n\n  /**\n   * Converts a data store entity to an entity object\n   */\n  function entityToAEEntity(meta, o) {\n    var ent = new jdatastore.Entity(o._type, o.id);\n    for(var k in meta.fields) {\n      if(meta.fields.hasOwnProperty(k)) {\n        ent.setProperty(k, entityValToDbVal(o._data[k], meta.fields[k]));\n      }\n    }\n    for(var k in meta.hasOne) {\n      if(meta.hasOne.hasOwnProperty(k)) {\n        ent.setProperty(k, entityValToDbVal(o._data[k], meta.fields[k]));\n      }\n    }\n    return ent;\n  }\n\n  var allEntities = [];\n\n  persistence.schemaSync = function (tx, callback, emulate) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: function(){} },\n        { name: \"emulate\", optional: true, check: argspec.hasType('boolean') }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var entityMeta = persistence.getEntityMeta();\n    for (var entityName in entityMeta) {\n      if (entityMeta.hasOwnProperty(entityName)) {\n        allEntities.push(persistence.define(entityName));\n      }\n    }\n\n    callback();\n  };\n\n  persistence.flush = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: null }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var session = this;\n    var fns = persistence.flushHooks;\n    persistence.asyncForEach(fns, function(fn, callback) {\n        fn(session, tx, callback);\n      }, function() {\n        // After applying the hooks\n        var entitiesToPersist = [];\n        var removeArrayList = new java.util.ArrayList();\n\n        for (var id in session.trackedObjects) {\n          if (session.trackedObjects.hasOwnProperty(id)) {\n            var ent = prepareDbEntity(session.trackedObjects[id]);\n            if(ent) {\n              entitiesToPersist.push(ent);\n            }\n          }\n        }\n        for (var id in session.objectsToRemove) {\n          if (session.objectsToRemove.hasOwnProperty(id)) {\n            removeArrayList.add(JKeyFactory.createKey(session.trackedObjects[id]._type, id));\n            delete session.trackedObjects[id]; // Stop tracking\n          }\n        }\n        if(entitiesToPersist.length > 0) {\n          session.dsService.put(entitiesToPersist);\n        }\n        if(removeArrayList.size() > 0) {\n          session.dsService['delete'](removeArrayList);\n        }\n        callback();\n      });\n  };\n  \n  persistence.reset = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n      ]);\n    callback = args.callback;\n\n    var session = this;\n\n    persistence.asyncParForEach(allEntities, function(Entity, callback) {\n        Entity.all(session).destroyAll(callback);\n      }, callback);\n  };\n\n  /**\n   * Internal function to persist an object to the database\n   * this function is invoked by persistence.flush()\n   */\n  function prepareDbEntity(obj) {\n    var meta = persistence.getMeta(obj._type);\n    var isDirty = obj._new;\n    if(Object.keys(obj._dirtyProperties).length > 0) {\n      isDirty = true;\n    }\n    if(isDirty) {\n      return entityToAEEntity(meta, obj);\n    } else {\n      return null; // no saving required\n    }\n  }\n\n  /////////////////////////// QueryCollection patches to work in AppEngine environment\n\n  persistence.NullFilter.prototype.addFilter = function (meta, query) {\n  };\n\n  persistence.AndFilter.prototype.addFilter = function (meta, query) {\n    this.left.addFilter(meta, query);\n    this.right.addFilter(meta, query);\n  };\n\n  persistence.OrFilter.prototype.addFilter = function (meta, query) {\n    throw new Error(\"OrFilter Not supported\");\n  };\n\n  persistence.PropertyFilter.prototype.addFilter = function (meta, query) {\n    var filterOp;\n    var value = this.value;\n    switch(this.operator) {\n    case '=':\n      filterOp = JFilterOperator.EQUAL;\n      break;\n    case '!=':\n      filterOp = JFilterOperator.NOT_EQUAL;\n      break;\n    case '>':\n      filterOp = JFilterOperator.GREATER_THAN;\n      break;\n    case '>=':\n      filterOp = JFilterOperator.GREATER_THAN_OR_EQUAL;\n      break;\n    case '<':\n      filterOp = JFilterOperator.LESS_THAN;\n      break;\n    case '<=':\n      filterOp = JFilterOperator.LESS_THAN_OR_EQUAL;\n      break;\n    case 'in':\n      var values = new java.util.ArrayList();\n      var type = meta.fields[this.property];\n      for(var i = 0; i < value.length; i++) {\n        values.add(entityValToDbVal(value[i], type));\n      }\n      value = values;\n      filterOp = JFilterOperator.IN;\n      break;\n    };\n    query.addFilter(this.property, filterOp, entityValToDbVal(value, meta.fields[this.property]));\n  };\n\n\n  function prepareQuery(coll, callback) {\n    var session = coll._session;\n    var entityName = coll._entityName;\n    var meta = persistence.getMeta(entityName);\n    \n    // handles mixin case -- this logic is generic and could be in persistence.\n    if (meta.isMixin) {\n      var result = [];\n      persistence.asyncForEach(meta.mixedIns, function(realMeta, next) {\n        var query = coll.clone();\n        query._entityName = realMeta.name;\n        query.list(tx, function(array) {\n          result = result.concat(array);\n          next();\n        });\n      }, function() {\n        var query = new persistence.LocalQueryCollection(result);\n        query._orderColumns = coll._orderColumns;\n        query._reverse = coll._reverse;\n        // TODO: handle skip and limit -- do we really want to do it?\n        query.list(null, callback);\n      });\n      return;\n    }    \n\n    var query = new JQuery(entityName); // Not tbe confused with jQuery\n    coll._filter.addFilter(meta, query);\n\n    coll._orderColumns.forEach(function(col) {\n        query.addSort(col[0], col[1] ? JSortDirection.ASCENDING : JSortDirection.DESCENDING);\n      });\n\n    callback(session.dsService.prepare(query));\n  }\n\n\n  /**\n   * Asynchronous call to actually fetch the items in the collection\n   * @param tx transaction to use\n   * @param callback function to be called taking an array with \n   *   result objects as argument\n   */\n  persistence.DbQueryCollection.prototype.list = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: false, check: argspec.isCallback() }\n      ]);\n    callback = args.callback;\n    var that = this;\n    var entityName = this._entityName;\n    var session = this._session;\n\n    // TODO: Check if filtering for 'id' property, then use key lookup\n    //\n    if(this._filter.right && this._filter.right.property && this._filter.right.property === 'id') {\n      var idToLoad = this._filter.right.value;\n      var obj;\n      try {\n        obj = session.dsService.get(JKeyFactory.createKey(entityName, idToLoad));\n      } catch(e) { // com.google.appengine.api.datastore.EntityNotFoundException\n        obj = null;\n      }\n      if(obj) {\n        callback([aeEntityToEntity(session, obj, persistence.define(entityName))]);\n      } else {\n        callback([]);\n      }\n    } else {\n      session.flush(function() {\n          prepareQuery(that, function(preparedQuery) {\n              if(that._limit === 1) {\n                var row = preparedQuery.asSingleEntity();\n                var e = aeEntityToEntity(session, row, persistence.define(entityName));\n                callback([e]);\n              } else {\n                var rows = preparedQuery.asList(jdatastore.FetchOptions.Builder.withLimit(that._limit === -1 ? 1000 : that._limit).offset(that._skip));\n                var results = [];\n\n                var Entity = persistence.define(entityName);\n                for (var i = 0; i < rows.size(); i++) {\n                  var r = rows.get(i);\n                  var e = aeEntityToEntity(session, r, Entity);\n                  results.push(e);\n                  session.add(e);\n                }\n                if(that._reverse) {\n                  results.reverse();\n                }\n                that.triggerEvent('list', that, results);\n                callback(results);\n              }\n            });\n        });\n    }\n  };\n\n  persistence.DbQueryCollection.prototype.destroyAll = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n      ]);\n    callback = args.callback;\n\n    var that = this;\n    var session = this._session;\n\n    session.flush(function() {\n        prepareQuery(that, function(preparedQuery) {\n            var rows = preparedQuery.asList(jdatastore.FetchOptions.Builder.withLimit(that._limit === -1 ? 1000 : that._limit).offset(that._skip));\n            var keys = new java.util.ArrayList();\n            for (var i = 0; i < rows.size(); i++) {\n              var r = rows.get(i);\n              keys.add(r.getKey());\n            }\n            that._session.dsService['delete'](keys);\n            that.triggerEvent('change', that);\n            callback();\n          });\n      });\n  };\n\n  persistence.DbQueryCollection.prototype.count = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: false, check: argspec.isCallback() }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var that = this;\n    var session = this._session;\n\n    session.flush(function() {\n        prepareQuery(that, function(preparedQuery) {\n            var n = preparedQuery.countEntities(jdatastore.FetchOptions.Builder.withDefaults());\n            callback(n);\n          });\n      });\n\n  };\n\n  persistence.isSession = function(obj) {\n    var isSession = !obj || (obj && obj.schemaSync);\n    if(!isSession) {\n      throw Error(\"No session argument passed, you should!\");\n    }\n    return isSession;\n  };\n};\n\n"
  },
  {
    "path": "lib/persistence.store.config.js",
    "content": "exports.init = function(persistence, config) {\n  var persistenceStore;\n  switch (config.adaptor) {\n    case 'memory':\n      persistenceStore = require('./persistence.store.memory');\n      break;\n    case 'mysql':\n      persistenceStore = require('./persistence.store.mysql');\n      break;\n    case 'sqlite3':\n      persistenceStore = require('./persistence.store.sqlite3');\n      break;\n    case 'react-native':\n      persistenceStore = require('./persistence.store.react-native');\n      break;\n    default:\n      persistenceStore = require('./persistence.store.mysql');\n      break;\n  }\n\n  if (config.username) config.user = config.username;\n  if (config.hostname) config.host = config.hostname;\n  persistenceStore.config(persistence,\n                          config.host,\n                          config.port,\n                          config.database,\n                          config.user,\n                          config.password);\n  return persistenceStore;\n};\n"
  },
  {
    "path": "lib/persistence.store.cordovasql.js",
    "content": "try {\n  if (!window) {\n    window = {};\n    //exports.console = console;\n  }\n} catch (e) {\n  window = {};\n  exports.console = console;\n}\n\nvar persistence = (window && window.persistence) ? window.persistence : {};\n\nif (!persistence.store) {\n  persistence.store = {};\n}\n\npersistence.store.cordovasql = {};\n\n/**\n * Configure the database connection (either sqliteplugin or websql)\n *\n * @param persistence\n * @param dbname\n * @param dbversion\n * @param description\n * @param size\n * @param backgroundProcessing\n * @param iOSLocation\n */\npersistence.store.cordovasql.config = function (persistence, dbname, dbversion, description, size, backgroundProcessing, iOSLocation) {\n  var conn = null;\n\n  /**\n   * Create a transaction\n   *\n   * @param callback\n   *            the callback function to be invoked when the transaction\n   *            starts, taking the transaction object as argument\n   */\n  persistence.transaction = function (callback) {\n    if (!conn) {\n      throw new Error(\"No ongoing database connection, please connect first.\");\n    } else {\n      conn.transaction(callback);\n    }\n  };\n\n  persistence.db = persistence.db || {};\n  persistence.db.implementation = \"unsupported\";\n  persistence.db.conn = null;\n\n  /* Find out if sqliteplugin is loaded. Otherwise, we'll fall back to WebSql */\n  if (window && 'sqlitePlugin' in window) {\n    persistence.db.implementation = 'sqliteplugin';\n  } else if (window && window.openDatabase) {\n    persistence.db.implementation = \"websql\";\n  } else {\n    // Well, we are stuck!\n  }\n\n  /*\n   * Cordova SqlitePlugin\n   */\n  persistence.db.sqliteplugin = {};\n\n  /**\n   * Connect to Sqlite plugin database\n   *\n   * @param dbname\n   * @param backgroundProcessing\n   * @param iOSLocation\n   * @returns {{}}\n   */\n  persistence.db.sqliteplugin.connect = function (dbname, backgroundProcessing, iOSLocation) {\n    var that = {};\n    var conn = window.sqlitePlugin.openDatabase({name: dbname, bgType: backgroundProcessing, location: (iOSLocation || 0)});\n\n    that.transaction = function (fn) {\n      return conn.transaction(function (sqlt) {\n        return fn(persistence.db.websql.transaction(sqlt));\n      });\n    };\n    return that;\n  };\n\n  /**\n   * Run transaction on Sqlite plugin database\n   *\n   * @param t\n   * @returns {{}}\n   */\n  persistence.db.sqliteplugin.transaction = function (t) {\n    var that = {};\n    that.executeSql = function (query, args, successFn, errorFn) {\n      if (persistence.debug) {\n        console.log(query, args);\n      }\n      t.executeSql(query, args, function (_, result) {\n        if (successFn) {\n          var results = [];\n          for (var i = 0; i < result.rows.length; i++) {\n            results.push(result.rows.item(i));\n          }\n          successFn(results);\n        }\n      }, errorFn);\n    };\n    return that;\n  };\n\n  /*\n   * WebSQL\n   */\n  persistence.db.websql = {};\n\n  /**\n   * Connect to the default WebSQL database\n   *\n   * @param dbname\n   * @param dbversion\n   * @param description\n   * @param size\n   * @returns {{}}\n   */\n  persistence.db.websql.connect = function (dbname, dbversion, description, size) {\n    var that = {};\n    var conn = openDatabase(dbname, dbversion, description, size);\n\n    that.transaction = function (fn) {\n      return conn.transaction(function (sqlt) {\n        return fn(persistence.db.websql.transaction(sqlt));\n      });\n    };\n    return that;\n  };\n\n  /**\n   * Run transaction on WebSQL database\n   *\n   * @param t\n   * @returns {{}}\n   */\n  persistence.db.websql.transaction = function (t) {\n    var that = {};\n    that.executeSql = function (query, args, successFn, errorFn) {\n      if (persistence.debug) {\n        console.log(query, args);\n      }\n      t.executeSql(query, args, function (_, result) {\n        if (successFn) {\n          var results = [];\n          for (var i = 0; i < result.rows.length; i++) {\n            results.push(result.rows.item(i));\n          }\n          successFn(results);\n        }\n      }, errorFn);\n    };\n    return that;\n  };\n\n  /**\n   * Connect() wrapper\n   *\n   * @param dbname\n   * @param dbversion\n   * @param description\n   * @param size\n   * @param backgroundProcessing\n   * @param iOSLocation\n   * @returns {*}\n   */\n  persistence.db.connect = function (dbname, dbversion, description, size, backgroundProcessing, iOSLocation) {\n    if (persistence.db.implementation == \"sqliteplugin\") {\n      return persistence.db.sqliteplugin.connect(dbname, backgroundProcessing, iOSLocation);\n    } else if (persistence.db.implementation == \"websql\") {\n      return persistence.db.websql.connect(dbname, dbversion, description, size);\n    }\n\n    return null;\n  };\n\n  /**\n   * Set the sqlite dialect\n   *\n   * @type {{createTable: createTable, createIndex: createIndex}}\n   */\n  persistence.store.cordovasql.sqliteDialect = {\n\n    /**\n     * columns is an array of arrays, e.g. [[\"id\", \"VARCHAR(32)\", \"PRIMARY KEY\"], [\"name\", \"TEXT\"]]\n     *\n     * @param tableName\n     * @param columns\n     * @returns {string}\n     */\n    createTable: function (tableName, columns) {\n      var tm = persistence.typeMapper;\n      var sql = \"CREATE TABLE IF NOT EXISTS `\" + tableName + \"` (\";\n      var defs = [];\n      for (var i = 0; i < columns.length; i++) {\n        var column = columns[i];\n        defs.push(\"`\" + column[0] + \"` \" + tm.columnType(column[1]) + (column[2] ? \" \" + column[2] : \"\"));\n      }\n      sql += defs.join(\", \");\n      sql += ')';\n      return sql;\n    },\n\n    /**\n     * columns is array of column names, e.g. [\"id\"]\n     * @param tableName\n     * @param columns\n     * @param options\n     * @returns {string}\n     */\n    createIndex: function (tableName, columns, options) {\n      options = options || {};\n      return \"CREATE \" + (options.unique ? \"UNIQUE \" : \"\") + \"INDEX IF NOT EXISTS `\" + tableName + \"__\" + columns.join(\"_\") +\n        \"` ON `\" + tableName + \"` (\" +\n        columns.map(function (col) {\n          return \"`\" + col + \"`\";\n        }).join(\", \") + \")\";\n    }\n  };\n\n  // Configure persistence for generic sql persistence, using sqliteDialect\n  persistence.store.sql.config(persistence, persistence.store.cordovasql.sqliteDialect);\n\n  // Make the connection\n  conn = persistence.db.connect(dbname, dbversion, description, size, backgroundProcessing, iOSLocation);\n  if (!conn) {\n    throw new Error(\"No supported database found in this browser.\");\n  }\n};\n\ntry {\n  exports.persistence = persistence;\n} catch (e) {\n}\n"
  },
  {
    "path": "lib/persistence.store.memory.js",
    "content": "try {\n  if(!window) {\n    window = {};\n    //exports.console = console;\n  }\n} catch(e) {\n  window = {};\n  exports.console = console;\n}\n\nvar persistence = (window && window.persistence) ? window.persistence : {}; \n\nif(!persistence.store) {\n  persistence.store = {};\n}\n\npersistence.store.memory = {};\n\npersistence.store.memory.config = function(persistence, dbname) {\n  var argspec = persistence.argspec;\n  dbname = dbname || 'persistenceData';\n\n  var allObjects = {}; // entityName -> LocalQueryCollection\n\n  persistence.getAllObjects = function() { return allObjects; };\n\n  var defaultAdd = persistence.add;\n\n  persistence.add = function(obj) {\n    if(!this.trackedObjects[obj.id]) {\n      defaultAdd.call(this, obj);\n      var entityName = obj._type;\n      if(!allObjects[entityName]) {\n        allObjects[entityName] = new persistence.LocalQueryCollection();\n        allObjects[entityName]._session = persistence;\n      }\n      allObjects[entityName].add(obj);\n    }\n    return this;\n  };\n\n  var defaultRemove = persistence.remove;\n\n  persistence.remove = function(obj) {\n    defaultRemove.call(this, obj);\n    var entityName = obj._type;\n    allObjects[entityName].remove(obj);\n  };\n\n  persistence.schemaSync = function (tx, callback, emulate) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: function(){} },\n        { name: \"emulate\", optional: true, check: argspec.hasType('boolean') }\n      ]);\n\n    args.callback();\n  };\n\n  persistence.flush = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n      ]);\n\n    var fns = persistence.flushHooks;\n    var session = this;\n    persistence.asyncForEach(fns, function(fn, callback) {\n        fn(session, tx, callback);\n      }, function() {\n        var trackedObjects = persistence.trackedObjects;\n        for(var id in trackedObjects) {\n          if(trackedObjects.hasOwnProperty(id)) {\n            if (persistence.objectsToRemove.hasOwnProperty(id)) {\n              delete trackedObjects[id];\n            } else {\n              trackedObjects[id]._dirtyProperties = {};\n            }\n          }\n        }\n        args.callback();\n      });\n  };\n\n  persistence.transaction = function(callback) {\n    setTimeout(function() {\n        callback({executeSql: function() {} });\n      }, 0);\n  };\n\n  persistence.loadFromLocalStorage = function(callback) {\n    var dump = window.localStorage.getItem(dbname);\n    if(dump) {\n      this.loadFromJson(dump, callback);\n    } else {\n      callback && callback();\n    }\n  };\n\n  persistence.saveToLocalStorage = function(callback) {\n    this.dumpToJson(function(dump) {\n        window.localStorage.setItem(dbname, dump);\n        if(callback) {\n          callback();\n        }\n      });\n  };\n\n  /**\n   * Remove all tables in the database (as defined by the model)\n   */\n  persistence.reset = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    allObjects = {};\n    this.clean();\n    callback();\n  };\n\n  /**\n   * Dummy\n   */\n  persistence.close = function() {};\n\n  // QueryCollection's list\n\n  function makeLocalClone(otherColl) {\n    var coll = allObjects[otherColl._entityName];\n    if(!coll) {\n      coll = new persistence.LocalQueryCollection();\n    }\n    coll = coll.clone();\n    coll._filter = otherColl._filter;\n    coll._prefetchFields = otherColl._prefetchFields;\n    coll._orderColumns = otherColl._orderColumns;\n    coll._limit = otherColl._limit;\n    coll._skip = otherColl._skip;\n    coll._reverse = otherColl._reverse;\n    return coll;\n  }\n  /**\n   * Asynchronous call to actually fetch the items in the collection\n   * @param tx transaction to use\n   * @param callback function to be called taking an array with \n   *   result objects as argument\n   */\n  persistence.DbQueryCollection.prototype.list = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: false, check: argspec.isCallback() }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var coll = makeLocalClone(this);\n    coll.list(null, callback);\n  };\n\n  /**\n   * Asynchronous call to remove all the items in the collection. \n   * Note: does not only remove the items from the collection, but\n   * the items themselves.\n   * @param tx transaction to use\n   * @param callback function to be called when clearing has completed\n   */\n  persistence.DbQueryCollection.prototype.destroyAll = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var coll = makeLocalClone(this);\n    coll.destroyAll(null, callback);\n  };\n\n  /**\n   * Asynchronous call to count the number of items in the collection.\n   * @param tx transaction to use\n   * @param callback function to be called when clearing has completed\n   */\n  persistence.DbQueryCollection.prototype.count = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: false, check: argspec.isCallback() }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var coll = makeLocalClone(this);\n    coll.count(null, callback);\n  };\n\n  persistence.ManyToManyDbQueryCollection = function(session, entityName) {\n    this.init(session, entityName, persistence.ManyToManyDbQueryCollection);\n    this._items = [];\n  };\n\n  persistence.ManyToManyDbQueryCollection.prototype = new persistence.LocalQueryCollection();\n\n  persistence.ManyToManyDbQueryCollection.prototype.initManyToMany = function(obj, coll) {\n    this._obj = obj;\n    this._coll = coll; // column name\n  };\n\n  persistence.ManyToManyDbQueryCollection.prototype.add = function(item, recursing) {\n    persistence.LocalQueryCollection.prototype.add.call(this, item);\n    if(!recursing) { // prevent recursively adding to one another\n      // Let's find the inverse collection\n      var meta = persistence.getMeta(this._obj._type);\n      var inverseProperty = meta.hasMany[this._coll].inverseProperty;\n      persistence.get(item, inverseProperty).add(this._obj, true);\n    }\n  };\n\n  persistence.ManyToManyDbQueryCollection.prototype.remove = function(item, recursing) {\n    persistence.LocalQueryCollection.prototype.remove.call(this, item);\n    if(!recursing) { // prevent recursively adding to one another\n      // Let's find the inverse collection\n      var meta = persistence.getMeta(this._obj._type);\n      var inverseProperty = meta.hasMany[this._coll].inverseProperty;\n      persistence.get(item, inverseProperty).remove(this._obj, true); \n    }\n  };\n};\n\ntry {\n  exports.config = persistence.store.memory.config;\n  exports.getSession = function() { return persistence; };\n} catch(e) {}\n\n"
  },
  {
    "path": "lib/persistence.store.mysql.js",
    "content": "/**\n * This back-end depends on the node.js asynchronous MySQL driver as found on:\n * http://github.com/felixge/node-mysql/\n * Easy install using npm:\n *   npm install mysql\n */\nvar sys = require('sys');\nvar sql = require('./persistence.store.sql');\nvar mysql = require('mysql');\n\nvar db, username, password;\n\nfunction log(o) {\n  sys.print(sys.inspect(o) + \"\\n\");\n}\n\n\nexports.config = function(persistence, hostname, port, db, username, password) {\n  exports.getSession = function(cb) {\n    var that = {};\n    var opts = {\n\t\t\t\t\t\thost: hostname,\n\t\t\t\t\t\tport: port,\n\t\t\t\t\t\tdatabase: db,\n\t\t\t\t\t\tuser: username,\n\t\t\t\t\t\tpassword: password\n\t\t \t  };\n    var client;\n    function handleDisconnect() {\n      connection = mysql.createConnection(opts); \n    }\n    if(typeof(mysql.createConnection)=='undefined'){\n      client = mysql.createClient(opts);\n    }else{\n      client = new mysql.createConnection(opts);\n      client.connect(function(err) {\n        if(err){\n          console.error(err);\n          setTimeout(handleDisconnect, 2000);\n        }\n     });\n      client.on('error', function(err) {\n      \tconsole.error(err);\n        if(err.code === 'PROTOCOL_CONNECTION_LOST') { \n          handleDisconnect();                         \n        } else {                                      \n          throw err;                                  \n        }\n      });\n    }\n\n    var session = new persistence.Session(that);\n    session.transaction = function (explicitCommit, fn) {\n      if (typeof arguments[0] === \"function\") {\n        fn = arguments[0];\n        explicitCommit = false;\n      }\n      var tx = transaction(client);\n      if (explicitCommit) {\n        tx.executeSql(\"START TRANSACTION\", null, function(){\n          fn(tx)\n        });\n      }\n      else \n        fn(tx);\n    };\n\n    session.close = function() {\n      client.end();\n      //conn._connection.destroy();\n    };\n    session.client = client;\n    return session;\n  };\n\n  function transaction(conn){\n    var that = {};\n    if(conn.ending) {\n      throw new Error(\"Connection has been closed, cannot execute query.\");\n    }\n    that.executeSql = function(query, args, successFn, errorFn){\n      function cb(err, result){\n        if (err) {\n          log(err.message);\n          that.errorHandler && that.errorHandler(err);\n          errorFn && errorFn(null, err);\n          return;\n        }\n        if (successFn) {\n          successFn(result);\n        }\n      }\n      if (persistence.debug) {\n        sys.print(query + \"\\n\");\n        args && args.length > 0 && sys.print(args.join(\",\") + \"\\n\")\n      }\n      if (!args) {\n        conn.query(query, cb);\n      }\n      else {\n        conn.query(query, args, cb);\n      }\n    }\n    \n    that.commit = function(session, callback){\n      session.flush(that, function(){\n        that.executeSql(\"COMMIT\", null, callback);\n      })\n    }\n    \n    that.rollback = function(session, callback){\n      that.executeSql(\"ROLLBACK\", null, function() {\n        session.clean();\n        callback && callback();\n      });\n    }\n    return that;\n  }\n  \n  exports.mysqlDialect = {\n    // columns is an array of arrays, e.g.\n    // [[\"id\", \"VARCHAR(32)\", \"PRIMARY KEY\"], [\"name\", \"TEXT\"]]\n    createTable: function(tableName, columns) {\n      var tm = persistence.typeMapper;\n      var sql = \"CREATE TABLE IF NOT EXISTS `\" + tableName + \"` (\";\n      var defs = [];\n      for(var i = 0; i < columns.length; i++) {\n        var column = columns[i];\n        defs.push(\"`\" + column[0] + \"` \" + tm.columnType(column[1]) + (column[2] ? \" \" + column[2] : \"\"));\n      }\n      sql += defs.join(\", \");\n      sql += ') ENGINE=InnoDB DEFAULT CHARSET=utf8';\n      return sql;\n    },\n\n    // columns is array of column names, e.g.\n    // [\"id\"]\n    createIndex: function(tableName, columns, options) {\n      options = options || {};\n      return \"CREATE \"+(options.unique?\"UNIQUE \":\"\")+\"INDEX `\" + tableName + \"__\" + columns.join(\"_\") + \n             \"` ON `\" + tableName + \"` (\" + \n             columns.map(function(col) { return \"`\" + col + \"`\"; }).join(\", \") + \")\";\n    }\n  };\n\n  sql.config(persistence, exports.mysqlDialect);\n};\n\n"
  },
  {
    "path": "lib/persistence.store.react-native.js",
    "content": "/**\n * This module depends on the react-native asynchronous SQLite3 driver as found on:\n * https://github.com/almost/react-native-sqlite\n * Easy install using npm:\n *   npm install react-native-sqlite\n *   and follow the instructions provided in the README\n * @author Lukas Reichart\n */\nvar sys = {};\nsys.print = console.log;\nvar sql = require('./persistence.store.sql');\nvar sqlite = require('react-native-sqlite');\n\nvar db, username, password;\n\nfunction log(o) {\n  sys.print(o + \"\\n\");\n}\n\n\nexports.config = function(persistence, dbPath) {\n  exports.getSession = function(cb) {\n    var that = {};\n    cb = cb || function() { };\n    var conn = new sqlite.Database(dbPath, cb);\n\n    var session = new persistence.Session(that);\n    session.transaction = function (explicitCommit, fn) {\n      if (typeof arguments[0] === \"function\") {\n        fn = arguments[0];\n        explicitCommit = false;\n      }\n      var tx = transaction(conn);\n      if (explicitCommit) {\n        tx.executeSql(\"START TRANSACTION\", null, function(){\n          fn(tx)\n        });\n      }\n      else\n        fn(tx);\n    };\n\n    session.close = function(cb) {\n      cb = cb || function() {};\n      conn.close(cb);\n    };\n    return session;\n  };\n\n  function transaction(conn){\n    var that = {};\n    // TODO: add check for db opened or closed\n    that.executeSql = function(query, args, successFn, errorFn){\n      var queryResult = [];\n      function cb(err){\n        if (err) {\n          log(err.message);\n          that.errorHandler && that.errorHandler(err);\n          errorFn && errorFn(null, err);\n          return;\n        }\n        if (successFn) {\n          if( !queryResult ) {\n            queryResult = [];\n          }\n          successFn(queryResult);\n        }\n      }\n      function rowCallback(row) {\n        queryResult.push(row);\n      }\n      if (persistence.debug) {\n        console.log(query + \"\\n\");\n        //args && args.length > 0 && sys.print(args.join(\",\") + \"\\n\")\n      }\n      if (!args) {\n        conn.executeSQL(query, [], rowCallback, cb );\n      }\n      else {\n        conn.executeSQL(query, args, rowCallback, cb );\n      }\n    }\n\n    that.commit = function(session, callback){\n      session.flush(that, function(){\n        that.executeSQL(\"COMMIT\", [], function(){}, callback);\n      })\n    }\n\n    that.rollback = function(session, callback){\n      that.executeSQL(\"ROLLBACK\", [], function() {}, function() {\n        session.clean();\n        callback();\n      });\n    }\n    return that;\n  }\n\n  ///////////////////////// SQLite dialect\n\n  persistence.sqliteDialect = {\n    // columns is an array of arrays, e.g.\n    // [[\"id\", \"VARCHAR(32)\", \"PRIMARY KEY\"], [\"name\", \"TEXT\"]]\n    createTable: function(tableName, columns) {\n      var tm = persistence.typeMapper;\n      var sql = \"CREATE TABLE IF NOT EXISTS `\" + tableName + \"` (\";\n      var defs = [];\n      for(var i = 0; i < columns.length; i++) {\n        var column = columns[i];\n        defs.push(\"`\" + column[0] + \"` \" + tm.columnType(column[1]) + (column[2] ? \" \" + column[2] : \"\"));\n      }\n      sql += defs.join(\", \");\n      sql += ')';\n      return sql;\n    },\n\n    // columns is array of column names, e.g.\n    // [\"id\"]\n    createIndex: function(tableName, columns, options) {\n      options = options || {};\n      return \"CREATE \"+(options.unique?\"UNIQUE \":\"\")+\"INDEX IF NOT EXISTS `\" + tableName + \"__\" + columns.join(\"_\") +\n        \"` ON `\" + tableName + \"` (\" +\n        columns.map(function(col) { return \"`\" + col + \"`\"; }).join(\", \") + \")\";\n    }\n  };\n\n  sql.config(persistence, persistence.sqliteDialect);\n};\n\n"
  },
  {
    "path": "lib/persistence.store.sql.js",
    "content": "/**\n * Default type mapper. Override to support more types or type options.\n */\nvar defaultTypeMapper = {\n  /**\n   * SQL type for ids\n   */\n  idType: \"VARCHAR(32)\",\n\n  /**\n   * SQL type for class names (used by mixins)\n   */\n  classNameType: \"TEXT\",\n\n  /**\n   * Returns SQL type for column definition\n   */\n  columnType: function(type){\n    switch(type) {\n    case 'JSON': return 'TEXT';\n    case 'BOOL': return 'INT';\n    case 'DATE': return 'INT';\n    default: return type;\n    }\n  },\n\n  inVar: function(str, type){\n    return str;\n  },\n  outVar: function(str, type){\n    return str;\n  },\n  outId: function(str){\n    return \"'\" + str + \"'\";\n  },\n  /**\n   * Converts a value from the database to a value suitable for the entity\n   * (also does type conversions, if necessary)\n   */\n  dbValToEntityVal: function(val, type){\n    if (val === null || val === undefined) {\n      return val;\n    }\n    switch (type) {\n      case 'DATE':\n        // SQL is in seconds and JS in miliseconds\n        if (val > 1000000000000) {\n          // usually in seconds, but sometimes it's milliseconds\n          return new Date(parseInt(val, 10));\n        } else {\n          return new Date(parseInt(val, 10) * 1000);\n        }\n      case 'BOOL':\n        return val === 1 || val === '1';\n        break;\n      case 'INT':\n        return +val;\n        break;\n      case 'BIGINT':\n        return +val;\n        break;\n      case 'JSON':\n        if (val) {\n          return JSON.parse(val);\n        }\n        else {\n          return val;\n        }\n        break;\n      default:\n        return val;\n    }\n  },\n\n  /**\n   * Converts an entity value to a database value, inverse of\n   *   dbValToEntityVal\n   */\n  entityValToDbVal: function(val, type){\n    if (val === undefined || val === null) {\n      return null;\n    }\n    else if (type === 'JSON' && val) {\n      return JSON.stringify(val);\n    }\n    else if (val.id) {\n      return val.id;\n    }\n    else if (type === 'BOOL') {\n      return (val === 'false') ? 0 : (val ? 1 : 0);\n    }\n    else if (type === 'DATE' || val.getTime) {\n      // In order to make SQLite Date/Time functions work we should store\n      // values in seconds and not as miliseconds as JS Date.getTime()\n      val = new Date(val);\n      return Math.round(val.getTime() / 1000);\n    }\n    else if(type === 'VARCHAR(32)'){\n      return val.toString();\n    }\n    else {\n      return val;\n    }\n  },\n  /**\n   * Shortcut for inVar when type is id -- no need to override\n   */\n  inIdVar: function(str){\n    return this.inVar(str, this.idType);\n  },\n  /**\n   * Shortcut for outVar when type is id -- no need to override\n   */\n  outIdVar: function(str){\n    return this.outVar(str, this.idType);\n  },\n  /**\n   * Shortcut for entityValToDbVal when type is id -- no need to override\n   */\n  entityIdToDbId: function(id){\n    return this.entityValToDbVal(id, this.idType);\n  }\n}\n\nfunction config(persistence, dialect) {\n  var argspec = persistence.argspec;\n\n  persistence.typeMapper = dialect.typeMapper || defaultTypeMapper;\n\n  persistence.generatedTables = {}; // set\n\n  /**\n   * Synchronize the data model with the database, creates table that had not\n   * been defined before\n   *\n   * @param tx\n   *            transaction object to use (optional)\n   * @param callback\n   *            function to be called when synchronization has completed,\n   *            takes started transaction as argument\n   */\n  persistence.schemaSync = function (tx, callback, emulate) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: function(){} },\n        { name: \"emulate\", optional: true, check: argspec.hasType('boolean') }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n    emulate = args.emulate;\n\n    if(!tx) {\n      var session = this;\n      this.transaction(function(tx) { session.schemaSync(tx, callback, emulate); });\n      return;\n    }\n    var queries = [], meta, colDefs, otherMeta, tableName;\n\n\tvar tm = persistence.typeMapper;\n    var entityMeta = persistence.getEntityMeta();\n    for (var entityName in entityMeta) {\n      if (entityMeta.hasOwnProperty(entityName)) {\n        meta = entityMeta[entityName];\n        if (!meta.isMixin) {\n          colDefs = [];\n          for (var prop in meta.fields) {\n            if (meta.fields.hasOwnProperty(prop)) {\n              colDefs.push([prop, meta.fields[prop]]);\n            }\n          }\n          for (var rel in meta.hasOne) {\n            if (meta.hasOne.hasOwnProperty(rel)) {\n              otherMeta = meta.hasOne[rel].type.meta;\n              colDefs.push([rel, tm.idType]);\n              queries.push([dialect.createIndex(meta.name, [rel]), null]);\n            }\n          }\n          for (var i = 0; i < meta.indexes.length; i++) {\n            queries.push([dialect.createIndex(meta.name, meta.indexes[i].columns, meta.indexes[i]), null]);\n          }\n        }\n        for (var rel in meta.hasMany) {\n          if (meta.hasMany.hasOwnProperty(rel) && meta.hasMany[rel].manyToMany) {\n            tableName = meta.hasMany[rel].tableName;\n            if (!persistence.generatedTables[tableName]) {\n              var otherMeta = meta.hasMany[rel].type.meta;\n              var inv = meta.hasMany[rel].inverseProperty;\n              // following test ensures that mixin mtm tables get created with the mixin itself\n              // it seems superfluous because mixin will be processed before entitites that use it\n              // but better be safe than sorry.\n              if (otherMeta.hasMany[inv].type.meta != meta)\n                continue;\n              var p1 = meta.name + \"_\" + rel;\n              var p2 = otherMeta.name + \"_\" + inv;\n              queries.push([dialect.createIndex(tableName, [p1]), null]);\n              queries.push([dialect.createIndex(tableName, [p2]), null]);\n              var columns = [[p1, tm.idType], [p2, tm.idType]];\n              if (meta.isMixin)\n                columns.push([p1 + \"_class\", tm.classNameType])\n              if (otherMeta.isMixin)\n                columns.push([p2 + \"_class\", tm.classNameType])\n              queries.push([dialect.createTable(tableName, columns), null]);\n              persistence.generatedTables[tableName] = true;\n            }\n          }\n        }\n        if (!meta.isMixin) {\n          colDefs.push([\"id\", tm.idType, \"PRIMARY KEY\"]);\n          persistence.generatedTables[meta.name] = true;\n          queries.push([dialect.createTable(meta.name, colDefs), null]);\n        }\n      }\n    }\n    var fns = persistence.schemaSyncHooks;\n    for(var i = 0; i < fns.length; i++) {\n      fns[i](tx);\n    }\n    if(emulate) {\n      // Done\n      callback(tx);\n    } else {\n      executeQueriesSeq(tx, queries, function(_, err) {\n          callback(tx, err);\n        });\n    }\n  };\n\n  /**\n   * Persists all changes to the database transaction\n   *\n   * @param tx\n   *            transaction to use\n   * @param callback\n   *            function to be called when done\n   */\n  persistence.flush = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: null }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var session = this;\n    if(!tx) {\n      this.transaction(function(tx) { session.flush(tx, callback); });\n      return;\n    }\n    var fns = persistence.flushHooks;\n    persistence.asyncForEach(fns, function(fn, callback) {\n        fn(session, tx, callback);\n      }, function() {\n        // After applying the hooks\n        var persistObjArray = [];\n        for (var id in session.trackedObjects) {\n          if (session.trackedObjects.hasOwnProperty(id)) {\n            persistObjArray.push(session.trackedObjects[id]);\n          }\n        }\n        var removeObjArray = [];\n        for (var id in session.objectsToRemove) {\n          if (session.objectsToRemove.hasOwnProperty(id)) {\n            removeObjArray.push(session.objectsToRemove[id]);\n            delete session.trackedObjects[id]; // Stop tracking\n          }\n        }\n        session.objectsToRemove = {};\n        if(callback) {\n          persistence.asyncParForEach(removeObjArray, function(obj, callback) {\n              remove(obj, tx, callback);\n            }, function(result, err) {\n              if (err) return callback(result, err);\n              persistence.asyncParForEach(persistObjArray, function(obj, callback) {\n                  save(obj, tx, callback);\n                }, callback);\n            });\n        } else { // More efficient\n          for(var i = 0; i < persistObjArray.length; i++) {\n            save(persistObjArray[i], tx);\n          }\n          for(var i = 0; i < removeObjArray.length; i++) {\n            remove(removeObjArray[i], tx);\n          }\n        }\n      });\n  };\n\n  /**\n   * Remove all tables in the database (as defined by the model)\n   */\n  persistence.reset = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: \"tx\", optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: \"callback\", optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var session = this;\n    if(!tx) {\n      session.transaction(function(tx) { session.reset(tx, callback); });\n      return;\n    }\n    // First emulate syncing the schema (to know which tables were created)\n    this.schemaSync(tx, function() {\n        var tableArray = [];\n        for (var p in persistence.generatedTables) {\n          if (persistence.generatedTables.hasOwnProperty(p)) {\n            tableArray.push(p);\n          }\n        }\n        function dropOneTable () {\n          var tableName = tableArray.pop();\n          tx.executeSql(\"DROP TABLE IF EXISTS `\" + tableName + \"`\", null, function () {\n              if (tableArray.length > 0) {\n                dropOneTable();\n              } else {\n                cb();\n              }\n            }, cb);\n        }\n        if(tableArray.length > 0) {\n          dropOneTable();\n        } else {\n          cb();\n        }\n\n        function cb(result, err) {\n          session.clean();\n          persistence.generatedTables = {};\n          if (callback) callback(result, err);\n        }\n      }, true);\n  };\n\n  /**\n   * Converts a database row into an entity object\n   */\n  function rowToEntity(session, entityName, row, prefix) {\n    prefix = prefix || '';\n    if (session.trackedObjects[row[prefix + \"id\"]]) { // Cached version\n      return session.trackedObjects[row[prefix + \"id\"]];\n    }\n    var tm = persistence.typeMapper;\n    var rowMeta = persistence.getMeta(entityName);\n    var ent = persistence.define(entityName); // Get entity\n    if(!row[prefix+'id']) { // null value, no entity found\n      return null;\n    }\n    var o = new ent(session, undefined, true);\n    o.id = tm.dbValToEntityVal(row[prefix + 'id'], tm.idType);\n    o._new = false;\n    for ( var p in row) {\n      if (row.hasOwnProperty(p)) {\n        if (p.substring(0, prefix.length) === prefix) {\n          var prop = p.substring(prefix.length);\n          if (prop != 'id') {\n            o._data[prop] = tm.dbValToEntityVal(row[p], rowMeta.fields[prop] || tm.idType);\n          }\n        }\n      }\n    }\n    return o;\n  }\n\n  /**\n   * Internal function to persist an object to the database\n   * this function is invoked by persistence.flush()\n   */\n  function save(obj, tx, callback) {\n    var meta = persistence.getMeta(obj._type);\n    var tm = persistence.typeMapper;\n    var properties = [];\n    var values = [];\n    var qs = [];\n    var propertyPairs = [];\n    if(obj._new) { // Mark all properties dirty\n      for (var p in meta.fields) {\n        if(meta.fields.hasOwnProperty(p)) {\n          obj._dirtyProperties[p] = true;\n        }\n      }\n    }\n    for ( var p in obj._dirtyProperties) {\n      if (obj._dirtyProperties.hasOwnProperty(p)) {\n        properties.push(\"`\" + p + \"`\");\n        var type = meta.fields[p] || tm.idType;\n        values.push(tm.entityValToDbVal(obj._data[p], type));\n        qs.push(tm.outVar(\"?\", type));\n        propertyPairs.push(\"`\" + p + \"` = \" + tm.outVar(\"?\", type));\n      }\n    }\n    var additionalQueries = [];\n    if(meta && meta.hasMany) {\n      for(var p in meta.hasMany) {\n        if(meta.hasMany.hasOwnProperty(p)) {\n          additionalQueries = additionalQueries.concat(persistence.get(obj, p).persistQueries());\n        }\n      }\n    }\n    executeQueriesSeq(tx, additionalQueries, function() {\n        if (!obj._new && properties.length === 0) { // Nothing changed and not new\n          if(callback) callback();\n          return;\n        }\n        obj._dirtyProperties = {};\n        if (obj._new) {\n          properties.push('id');\n          values.push(tm.entityIdToDbId(obj.id));\n          qs.push(tm.outIdVar('?'));\n          var sql = \"INSERT INTO `\" + obj._type + \"` (\" + properties.join(\", \") + \") VALUES (\" + qs.join(', ') + \")\";\n          obj._new = false;\n          tx.executeSql(sql, values, callback, callback);\n        } else {\n          var sql = \"UPDATE `\" + obj._type + \"` SET \" + propertyPairs.join(',') + \" WHERE id = \" + tm.outId(obj.id);\n          tx.executeSql(sql, values, callback, callback);\n        }\n      });\n  }\n\n  persistence.save = save;\n\n  function remove (obj, tx, callback) {\n    var meta = persistence.getMeta(obj._type);\n\tvar tm = persistence.typeMapper;\n    var queries = [[\"DELETE FROM `\" + obj._type + \"` WHERE id = \" + tm.outId(obj.id), null]];\n    for (var rel in meta.hasMany) {\n      if (meta.hasMany.hasOwnProperty(rel) && meta.hasMany[rel].manyToMany) {\n        var tableName = meta.hasMany[rel].tableName;\n        //var inverseProperty = meta.hasMany[rel].inverseProperty;\n        queries.push([\"DELETE FROM `\" + tableName + \"` WHERE `\" + meta.name + '_' + rel + \"` = \" + tm.outId(obj.id), null]);\n      }\n    }\n    executeQueriesSeq(tx, queries, callback);\n  }\n\n  /**\n   * Utility function to execute a series of queries in an asynchronous way\n   * @param tx the transaction to execute the queries on\n   * @param queries an array of [query, args] tuples\n   * @param callback the function to call when all queries have been executed\n   */\n  function executeQueriesSeq (tx, queries, callback) {\n    // queries.reverse();\n    var callbackArgs = [];\n    for ( var i = 3; i < arguments.length; i++) {\n      callbackArgs.push(arguments[i]);\n    }\n    persistence.asyncForEach(queries, function(queryTuple, callback) {\n        tx.executeSql(queryTuple[0], queryTuple[1], callback, function(_, err) {\n            console.log(err.message);\n            callback(_, err);\n          });\n      }, function(result, err) {\n        if (err && callback) {\n          callback(result, err);\n          return;\n        }\n        if(callback) callback.apply(null, callbackArgs);\n      });\n  }\n\n  persistence.executeQueriesSeq = executeQueriesSeq;\n\n  /////////////////////////// QueryCollection patches to work in SQL environment\n\n  /**\n   * Function called when session is flushed, returns list of SQL queries to execute\n   * (as [query, arg] tuples)\n   */\n  persistence.QueryCollection.prototype.persistQueries = function() { return []; };\n\n  var oldQCClone = persistence.QueryCollection.prototype.clone;\n\n  persistence.QueryCollection.prototype.clone = function (cloneSubscribers) {\n    var c = oldQCClone.call(this, cloneSubscribers);\n    c._additionalJoinSqls = this._additionalJoinSqls.slice(0);\n    c._additionalWhereSqls = this._additionalWhereSqls.slice(0);\n    c._additionalGroupSqls = this._additionalGroupSqls.slice(0);\n    c._manyToManyFetch = this._manyToManyFetch;\n    return c;\n  };\n\n  var oldQCInit = persistence.QueryCollection.prototype.init;\n\n  persistence.QueryCollection.prototype.init = function(session, entityName, constructor) {\n    oldQCInit.call(this, session, entityName, constructor);\n    this._manyToManyFetch = null;\n    this._additionalJoinSqls = [];\n    this._additionalWhereSqls = [];\n    this._additionalGroupSqls = [];\n  };\n\n  var oldQCToUniqueString = persistence.QueryCollection.prototype.toUniqueString;\n\n  persistence.QueryCollection.prototype.toUniqueString = function() {\n    var s = oldQCToUniqueString.call(this);\n    s += '|JoinSQLs:';\n    for(var i = 0; i < this._additionalJoinSqls.length; i++) {\n      s += this._additionalJoinSqls[i];\n    }\n    s += '|WhereSQLs:';\n    for(var i = 0; i < this._additionalWhereSqls.length; i++) {\n      s += this._additionalWhereSqls[i];\n    }\n    s += '|GroupSQLs:';\n    for(var i = 0; i < this._additionalGroupSqls.length; i++) {\n      s += this._additionalGroupSqls[i];\n    }\n    if(this._manyToManyFetch) {\n      s += '|ManyToManyFetch:';\n      s += JSON.stringify(this._manyToManyFetch); // TODO: Do something more efficient\n    }\n    return s;\n  };\n\n  persistence.NullFilter.prototype.sql = function (meta, alias, values) {\n    return \"1=1\";\n  };\n\n  persistence.AndFilter.prototype.sql = function (meta, alias, values) {\n    return \"(\" + this.left.sql(meta, alias, values) + \" AND \"\n    + this.right.sql(meta, alias, values) + \")\";\n  };\n\n  persistence.OrFilter.prototype.sql = function (meta, alias, values) {\n    return \"(\" + this.left.sql(meta, alias, values) + \" OR \"\n    + this.right.sql(meta, alias, values) + \")\";\n  };\n\n  persistence.PropertyFilter.prototype.sql = function (meta, alias, values) {\n    var tm = persistence.typeMapper;\n    var aliasPrefix = alias ? \"`\" + alias + \"`.\" : \"\";\n  \tvar sqlType = meta.fields[this.property] || tm.idType;\n    if (this.operator === '=' && this.value === null) {\n      return aliasPrefix + '`' + this.property + \"` IS NULL\";\n    } else if (this.operator === '!=' && this.value === null) {\n      return aliasPrefix + '`' + this.property + \"` IS NOT NULL\";\n    } else if (this.operator === 'in') {\n      var vals = this.value;\n      var qs = [];\n      for(var i = 0; i < vals.length; i++) {\n        qs.push('?');\n        values.push(tm.entityValToDbVal(vals[i], sqlType));\n      }\n      if(vals.length === 0) {\n        // Optimize this a little\n        return \"1 = 0\";\n      } else {\n        return aliasPrefix + '`' + this.property + \"` IN (\" + qs.join(', ') + \")\";\n      }\n    } else if (this.operator === 'not in') {\n      var vals = this.value;\n      var qs = [];\n      for(var i = 0; i < vals.length; i++) {\n        qs.push('?');\n        values.push(tm.entityValToDbVal(vals[i], sqlType));\n      }\n\n      if(vals.length === 0) {\n        // Optimize this a little\n        return \"1 = 1\";\n      } else {\n        return aliasPrefix + '`' + this.property + \"` NOT IN (\" + qs.join(', ') + \")\";\n      }\n    } else {\n      var value = this.value;\n      if(value === true || value === false) {\n        value = value ? 1 : 0;\n      }\n      values.push(tm.entityValToDbVal(value, sqlType));\n \t  return aliasPrefix + '`' + this.property + \"` \" + this.operator + \" \" + tm.outVar(\"?\", sqlType);\n   }\n  };\n\n  // QueryColleciton's list\n\n  /**\n   * Asynchronous call to actually fetch the items in the collection\n   * @param tx transaction to use\n   * @param callback function to be called taking an array with\n   *   result objects as argument\n   */\n  persistence.DbQueryCollection.prototype.list = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: false, check: argspec.isCallback() }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var that = this;\n    var session = this._session;\n    if(!tx) { // no transaction supplied\n      session.transaction(function(tx) {\n          that.list(tx, callback);\n        });\n      return;\n    }\n    var entityName = this._entityName;\n    var meta = persistence.getMeta(entityName);\n    var tm = persistence.typeMapper;\n\n    // handles mixin case -- this logic is generic and could be in persistence.\n    if (meta.isMixin) {\n      var result = [];\n      persistence.asyncForEach(meta.mixedIns, function(realMeta, next) {\n        var query = that.clone();\n        query._entityName = realMeta.name;\n        query.list(tx, function(array) {\n          result = result.concat(array);\n          next();\n        });\n      }, function() {\n        var query = new persistence.LocalQueryCollection(result);\n        query._orderColumns = that._orderColumns;\n        query._reverse = that._reverse;\n        // TODO: handle skip and limit -- do we really want to do it?\n        query.list(null, callback);\n      });\n      return;\n    }\n\n    function selectAll (meta, tableAlias, prefix) {\n      var selectFields = [ tm.inIdVar(\"`\" + tableAlias + \"`.id\") + \" AS \" + prefix + \"id\" ];\n      for ( var p in meta.fields) {\n        if (meta.fields.hasOwnProperty(p)) {\n          selectFields.push(tm.inVar(\"`\" + tableAlias + \"`.`\" + p + \"`\", meta.fields[p]) + \" AS `\"\n            + prefix + p + \"`\");\n        }\n      }\n      for ( var p in meta.hasOne) {\n        if (meta.hasOne.hasOwnProperty(p)) {\n          selectFields.push(tm.inIdVar(\"`\" + tableAlias + \"`.`\" + p + \"`\") + \" AS `\"\n            + prefix + p + \"`\");\n        }\n      }\n      return selectFields;\n    }\n    var args = [];\n    var mainPrefix = entityName + \"_\";\n\n    var mainAlias = 'root';\n    var selectFields = selectAll(meta, mainAlias, mainPrefix);\n\n    var joinSql = '';\n    var additionalWhereSqls = this._additionalWhereSqls.slice(0);\n    var mtm = this._manyToManyFetch;\n    if(mtm) {\n      joinSql += \"LEFT JOIN `\" + mtm.table + \"` AS mtm ON mtm.`\" + mtm.inverseProp + \"` = `root`.`id` \";\n      additionalWhereSqls.push(\"mtm.`\" + mtm.prop + \"` = \" + tm.outId(mtm.id));\n    }\n\n    joinSql += this._additionalJoinSqls.join(' ');\n\n    for ( var i = 0; i < this._prefetchFields.length; i++) {\n      var prefetchFieldParts = this._prefetchFields[i].split('.');\n      var prefetchField = prefetchFieldParts[0];\n      var eName = entityName;\n      if(prefetchFieldParts.length > 1){\n        prefetchField = prefetchFieldParts[1];\n        eName = prefetchFieldParts[0];\n      }\n      var theMeta = persistence.getMeta(eName);\n      var thisMeta = theMeta.hasOne[prefetchField].type.meta;\n      if (thisMeta.isMixin)\n        throw new Error(\"cannot prefetch a mixin\");\n      var tableAlias = thisMeta.name + '_' + prefetchField + \"_tbl\";\n      var PrefetchFrom = mainAlias;\n      if(prefetchFieldParts.length > 1){\n        PrefetchFrom = eName + '_' + eName + \"_tbl\";;\n      }\n      selectFields = selectFields.concat(selectAll(thisMeta, tableAlias,\n          prefetchField + \"_\"));\n      joinSql += \"LEFT JOIN `\" + thisMeta.name + \"` AS `\" + tableAlias\n      + \"` ON `\" + tableAlias + \"`.`id` = `\" + PrefetchFrom + '`.`' + prefetchField + \"` \";\n\n    }\n\n    var whereSql = \"WHERE \"\n    + [ this._filter.sql(meta, mainAlias, args) ].concat(additionalWhereSqls).join(' AND ');\n\n    var sql = \"SELECT \" + selectFields.join(\", \") + \" FROM `\" + entityName\n    + \"` AS `\" + mainAlias + \"` \" + joinSql + \" \" + whereSql;\n\n    if(this._additionalGroupSqls.length > 0) {\n      sql += this._additionalGroupSqls.join(' ');\n    }\n\n    if(this._orderColumns.length > 0) {\n      sql += \" ORDER BY \"\n      + this._orderColumns.map(\n        function (c) {\n          return (c[2] ? \"`\" : \"LOWER(`\") + mainPrefix + c[0] + (c[2] ? \"` \" : \"`) \")\n          + (c[1] ? \"ASC\" : \"DESC\");\n        }).join(\", \");\n    }\n    if(this._limit >= 0) {\n      sql += \" LIMIT \" + this._limit;\n    }\n    if(this._skip > 0) {\n      sql += \" OFFSET \" + this._skip;\n    }\n    session.flush(tx, function () {\n        tx.executeSql(sql, args, function (rows) {\n            var results = [];\n            if(that._reverse) {\n              rows.reverse();\n            }\n            for ( var i = 0; i < rows.length; i++) {\n              var r = rows[i];\n              var e = rowToEntity(session, entityName, r, mainPrefix);\n              for ( var j = 0; j < that._prefetchFields.length; j++) {\n                \n                var prefetchFieldParts = that._prefetchFields[j].split('.');\n                var prefetchField = prefetchFieldParts[0];\n                var eName = entityName;\n                if(prefetchFieldParts.length > 1){\n                  prefetchField = prefetchFieldParts[1];\n                  eName = prefetchFieldParts[0];\n                }\n                var theMeta = persistence.getMeta(eName);\n                var thisMeta = theMeta.hasOne[prefetchField].type.meta;\n                \n                e._data_obj[prefetchField] = rowToEntity(session, thisMeta.name, r, prefetchField + '_');\n                session.add(e._data_obj[prefetchField]);\n              }\n              results.push(e);\n              session.add(e);\n            }\n            callback(results);\n            that.triggerEvent('list', that, results);\n          });\n      });\n  };\n\n  /**\n   * Asynchronous call to remove all the items in the collection.\n   * Note: does not only remove the items from the collection, but\n   * the items themselves.\n   * @param tx transaction to use\n   * @param callback function to be called when clearing has completed\n   */\n  persistence.DbQueryCollection.prototype.destroyAll = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: true, check: argspec.isCallback(), defaultValue: function(){} }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var that = this;\n    var session = this._session;\n    if(!tx) { // no transaction supplied\n      session.transaction(function(tx) {\n          that.destroyAll(tx, callback);\n        });\n      return;\n    }\n    var entityName = this._entityName;\n    var meta = persistence.getMeta(entityName);\n    var tm = persistence.typeMapper;\n\n    // handles mixin case -- this logic is generic and could be in persistence.\n    if (meta.isMixin) {\n      persistence.asyncForEach(meta.mixedIns, function(realMeta, next) {\n        var query = that.clone();\n        query._entityName = realMeta.name;\n        query.destroyAll(tx, callback);\n      }, callback);\n      return;\n    }\n\n    var joinSql = '';\n    var additionalWhereSqls = this._additionalWhereSqls.slice(0);\n    var mtm = this._manyToManyFetch;\n    if(mtm) {\n      joinSql += \"LEFT JOIN `\" + mtm.table + \"` AS mtm ON mtm.`\" + mtm.inverseProp + \"` = `root`.`id` \";\n      additionalWhereSqls.push(\"mtm.`\" + mtm.prop + \"` = \" + tm.outId(mtm.id));\n    }\n\n    joinSql += this._additionalJoinSqls.join(' ');\n\n    var args = [];\n    var whereSql = \"WHERE \"\n    + [ this._filter.sql(meta, null, args) ].concat(additionalWhereSqls).join(' AND ');\n\n    var selectSql = \"SELECT id FROM `\" + entityName + \"` \" + joinSql + ' ' + whereSql;\n    var deleteSql = \"DELETE FROM `\" + entityName + \"` \" + joinSql + ' ' + whereSql;\n    var args2 = args.slice(0);\n\n    session.flush(tx, function () {\n        tx.executeSql(selectSql, args, function(results) {\n            for(var i = 0; i < results.length; i++) {\n              delete session.trackedObjects[results[i].id];\n              session.objectsRemoved.push({id: results[i].id, entity: entityName});\n            }\n            that.triggerEvent('change', that);\n            tx.executeSql(deleteSql, args2, callback, callback);\n          }, callback);\n      });\n  };\n\n  /**\n   * Asynchronous call to count the number of items in the collection.\n   * @param tx transaction to use\n   * @param callback function to be called when clearing has completed\n   */\n  persistence.DbQueryCollection.prototype.count = function (tx, callback) {\n    var args = argspec.getArgs(arguments, [\n        { name: 'tx', optional: true, check: persistence.isTransaction, defaultValue: null },\n        { name: 'callback', optional: false, check: argspec.isCallback() }\n      ]);\n    tx = args.tx;\n    callback = args.callback;\n\n    var that = this;\n    var session = this._session;\n    if(tx && !tx.executeSql) { // provided callback as first argument\n      callback = tx;\n      tx = null;\n    }\n    if(!tx) { // no transaction supplied\n      session.transaction(function(tx) {\n          that.count(tx, callback);\n        });\n      return;\n    }\n    var entityName = this._entityName;\n    var meta = persistence.getMeta(entityName);\n    var tm = persistence.typeMapper;\n\n    // handles mixin case -- this logic is generic and could be in persistence.\n    if (meta.isMixin) {\n      var result = 0;\n      persistence.asyncForEach(meta.mixedIns, function(realMeta, next) {\n        var query = that.clone();\n        query._entityName = realMeta.name;\n        query.count(tx, function(count) {\n          result += count;\n          next();\n        });\n      }, function() {\n        callback(result);\n      });\n      return;\n    }\n\n    var joinSql = '';\n    var additionalWhereSqls = this._additionalWhereSqls.slice(0);\n    var mtm = this._manyToManyFetch;\n    if(mtm) {\n      joinSql += \"LEFT JOIN `\" + mtm.table + \"` AS mtm ON mtm.`\" + mtm.inverseProp + \"` = `root`.`id` \";\n      additionalWhereSqls.push(\"mtm.`\" + mtm.prop + \"` = \" + tm.outId(mtm.id));\n    }\n\n    joinSql += this._additionalJoinSqls.join(' ');\n    var args = [];\n    var whereSql = \"WHERE \" + [ this._filter.sql(meta, \"root\", args) ].concat(additionalWhereSqls).join(' AND ');\n\n    var sql = \"SELECT COUNT(*) AS cnt FROM `\" + entityName + \"` AS `root` \" + joinSql + \" \" + whereSql;\n\n    session.flush(tx, function () {\n        tx.executeSql(sql, args, function(results) {\n            callback(parseInt(results[0].cnt, 10));\n          });\n      });\n  };\n\n  persistence.ManyToManyDbQueryCollection.prototype.persistQueries = function() {\n    var queries = [];\n    var meta = persistence.getMeta(this._obj._type);\n    var inverseMeta = meta.hasMany[this._coll].type.meta;\n    var tm = persistence.typeMapper;\n    var rel = meta.hasMany[this._coll];\n    var inv = inverseMeta.hasMany[rel.inverseProperty];\n    var direct = rel.mixin ? rel.mixin.meta.name : meta.name;\n    var inverse = inv.mixin ? inv.mixin.meta.name : inverseMeta.name;\n\n    // Added\n    for(var i = 0; i < this._localAdded.length; i++) {\n      var columns = [direct + \"_\" + this._coll, inverse + '_' + rel.inverseProperty];\n      var vars = [tm.outIdVar(\"?\"), tm.outIdVar(\"?\")];\n      var args = [tm.entityIdToDbId(this._obj.id), tm.entityIdToDbId(this._localAdded[i].id)];\n      if (rel.mixin) {\n        columns.push(direct + \"_\" + this._coll + \"_class\");\n        vars.push(\"?\");\n        args.push(meta.name);\n      }\n      if (inv.mixin) {\n        columns.push(inverse + \"_\" + rel.inverseProperty + \"_class\");\n        vars.push(\"?\");\n        args.push(inverseMeta.name);\n      }\n      queries.push([\"INSERT INTO \" + rel.tableName +\n            \" (`\" + columns.join(\"`, `\") + \"`) VALUES (\" + vars.join(\",\") + \")\", args]);\n    }\n    this._localAdded = [];\n    // Removed\n    for(var i = 0; i < this._localRemoved.length; i++) {\n    queries.push([\"DELETE FROM  \" + rel.tableName +\n          \" WHERE `\" + direct + \"_\" + this._coll + \"` = \" + tm.outIdVar(\"?\") + \" AND `\" +\n          inverse + '_' + rel.inverseProperty +\n          \"` = \" + tm.outIdVar(\"?\"), [tm.entityIdToDbId(this._obj.id), tm.entityIdToDbId(this._localRemoved[i].id)]]);\n    }\n    this._localRemoved = [];\n    return queries;\n  };\n};\n\nif (typeof exports !== 'undefined') {\n\texports.defaultTypeMapper = defaultTypeMapper;\n\texports.config = config;\n}\nelse {\n\twindow = window || {};\n\twindow.persistence = window.persistence || persistence || {};\n\twindow.persistence.store = window.persistence.store || {};\n\twindow.persistence.store.sql = {\n\t\tdefaultTypeMapper: defaultTypeMapper,\n\t\tconfig: config\n\t};\n}\n"
  },
  {
    "path": "lib/persistence.store.sqlite.js",
    "content": "/**\n * This back-end depends on the node.js asynchronous SQLite driver as found on:\n * https://github.com/orlandov/node-sqlite\n * Easy install using npm:\n *   npm install sqlite\n * @author Eugene Ware\n */\nvar sys = require('sys');\nvar sql = require('./persistence.store.sql');\nvar sqlite = require('sqlite');\n\nvar db, username, password;\n\nfunction log(o) {\n  sys.print(sys.inspect(o) + \"\\n\");\n}\n\n\nexports.config = function(persistence, dbPath) {\n  exports.getSession = function(cb) {\n    var that = {};\n    cb = cb || function() { };\n    var conn = new sqlite.Database();\n    conn.open(dbPath, cb);\n\n    var session = new persistence.Session(that);\n    session.transaction = function (explicitCommit, fn) {\n      if (typeof arguments[0] === \"function\") {\n        fn = arguments[0];\n        explicitCommit = false;\n      }\n      var tx = transaction(conn);\n      if (explicitCommit) {\n        tx.executeSql(\"START TRANSACTION\", null, function(){\n          fn(tx)\n        });\n      }\n      else \n        fn(tx);\n    };\n\n    session.close = function(cb) {\n      cb = cb || function() {};\n      conn.close(cb);\n    };\n    return session;\n  };\n\n  function transaction(conn){\n    var that = {};\n    // TODO: add check for db opened or closed\n    that.executeSql = function(query, args, successFn, errorFn){\n      function cb(err, result){\n        if (err) {\n          log(err.message);\n          that.errorHandler && that.errorHandler(err);\n          errorFn && errorFn(null, err);\n          return;\n        }\n        if (successFn) {\n          successFn(result);\n        }\n      }\n      if (persistence.debug) {\n        sys.print(query + \"\\n\");\n        args && args.length > 0 && sys.print(args.join(\",\") + \"\\n\")\n      }\n      if (!args) {\n        conn.execute(query, cb);\n      }\n      else {\n        conn.execute(query, args, cb);\n      }\n    }\n    \n    that.commit = function(session, callback){\n      session.flush(that, function(){\n        that.executeSql(\"COMMIT\", null, callback);\n      })\n    }\n    \n    that.rollback = function(session, callback){\n      that.executeSql(\"ROLLBACK\", null, function() {\n        session.clean();\n        callback();\n      });\n    }\n    return that;\n  }\n  \n  ///////////////////////// SQLite dialect\n\n  persistence.sqliteDialect = {\n    // columns is an array of arrays, e.g.\n    // [[\"id\", \"VARCHAR(32)\", \"PRIMARY KEY\"], [\"name\", \"TEXT\"]]\n    createTable: function(tableName, columns) {\n      var tm = persistence.typeMapper;\n      var sql = \"CREATE TABLE IF NOT EXISTS `\" + tableName + \"` (\";\n      var defs = [];\n      for(var i = 0; i < columns.length; i++) {\n        var column = columns[i];\n        defs.push(\"`\" + column[0] + \"` \" + tm.columnType(column[1]) + (column[2] ? \" \" + column[2] : \"\"));\n      }\n      sql += defs.join(\", \");\n      sql += ')';\n      return sql;\n    },\n\n    // columns is array of column names, e.g.\n    // [\"id\"]\n    createIndex: function(tableName, columns, options) {\n      options = options || {};\n      return \"CREATE \"+(options.unique?\"UNIQUE \":\"\")+\"INDEX IF NOT EXISTS `\" + tableName + \"__\" + columns.join(\"_\") + \n             \"` ON `\" + tableName + \"` (\" + \n             columns.map(function(col) { return \"`\" + col + \"`\"; }).join(\", \") + \")\";\n    }\n  };\n\n  sql.config(persistence, persistence.sqliteDialect);\n};\n\n"
  },
  {
    "path": "lib/persistence.store.sqlite3.js",
    "content": "/**\n * This back-end depends on the node.js asynchronous SQLite3 driver as found on:\n * https://github.com/developmentseed/node-sqlite3\n * Easy install using npm:\n *   npm install sqlite3\n * @author Eugene Ware\n * @author Jeff Kunkle\n * @author Joe Ferner\n */\nvar sys = require('sys');\nvar sql = require('./persistence.store.sql');\nvar sqlite = require('sqlite3');\n\nvar db, username, password;\n\nfunction log(o) {\n  sys.print(sys.inspect(o) + \"\\n\");\n}\n\n\nexports.config = function(persistence, dbPath) {\n  exports.getSession = function(cb) {\n    var that = {};\n    cb = cb || function() { };\n    var conn = new sqlite.Database(dbPath, cb);\n\n    var session = new persistence.Session(that);\n    session.transaction = function (explicitCommit, fn) {\n      if (typeof arguments[0] === \"function\") {\n        fn = arguments[0];\n        explicitCommit = false;\n      }\n      var tx = transaction(conn);\n      if (explicitCommit) {\n        tx.executeSql(\"START TRANSACTION\", null, function(){\n          fn(tx)\n        });\n      }\n      else \n        fn(tx);\n    };\n\n    session.close = function(cb) {\n      cb = cb || function() {};\n      conn.close(cb);\n    };\n    return session;\n  };\n\n  function transaction(conn){\n    var that = {};\n    // TODO: add check for db opened or closed\n    that.executeSql = function(query, args, successFn, errorFn){\n      function cb(err, result){\n        if (err) {\n          log(err.message);\n          that.errorHandler && that.errorHandler(err);\n          errorFn && errorFn(null, err);\n          return;\n        }\n        if (successFn) {\n          successFn(result);\n        }\n      }\n      if (persistence.debug) {\n        sys.print(query + \"\\n\");\n        args && args.length > 0 && sys.print(args.join(\",\") + \"\\n\")\n      }\n      if (!args) {\n        conn.all(query, cb);\n      }\n      else {\n        conn.all(query, args, cb);\n      }\n    }\n\n    that.commit = function(session, callback){\n      session.flush(that, function(){\n        that.executeSql(\"COMMIT\", null, callback);\n      })\n    }\n\n    that.rollback = function(session, callback){\n      that.executeSql(\"ROLLBACK\", null, function() {\n        session.clean();\n        callback();\n      });\n    }\n    return that;\n  }\n\n  ///////////////////////// SQLite dialect\n\n  persistence.sqliteDialect = {\n    // columns is an array of arrays, e.g.\n    // [[\"id\", \"VARCHAR(32)\", \"PRIMARY KEY\"], [\"name\", \"TEXT\"]]\n    createTable: function(tableName, columns) {\n      var tm = persistence.typeMapper;\n      var sql = \"CREATE TABLE IF NOT EXISTS `\" + tableName + \"` (\";\n      var defs = [];\n      for(var i = 0; i < columns.length; i++) {\n        var column = columns[i];\n        defs.push(\"`\" + column[0] + \"` \" + tm.columnType(column[1]) + (column[2] ? \" \" + column[2] : \"\"));\n      }\n      sql += defs.join(\", \");\n      sql += ')';\n      return sql;\n    },\n\n    // columns is array of column names, e.g.\n    // [\"id\"]\n    createIndex: function(tableName, columns, options) {\n      options = options || {};\n      return \"CREATE \"+(options.unique?\"UNIQUE \":\"\")+\"INDEX IF NOT EXISTS `\" + tableName + \"__\" + columns.join(\"_\") + \n             \"` ON `\" + tableName + \"` (\" + \n             columns.map(function(col) { return \"`\" + col + \"`\"; }).join(\", \") + \")\";\n    }\n  };\n\n  sql.config(persistence, persistence.sqliteDialect);\n};\n\n"
  },
  {
    "path": "lib/persistence.store.titanium.js",
    "content": "try {\n  if(!window) {\n    window = {};\n    //exports.console = console;\n  }\n} catch(e) {\n  window = {};\n  exports.console = console;\n}\n\nvar persistence = (window && window.persistence) ? window.persistence : {};\n\nif(!persistence.store) {\n  persistence.store = {};\n}\n\npersistence.store.titanium = {};\n\npersistence.store.titanium.config = function(persistence, dbname) {\n  var conn = null;\n\n  /**\n   * Create a transaction\n   *\n   * @param callback,\n   *            the callback function to be invoked when the transaction\n   *            starts, taking the transaction object as argument\n   */\n  persistence.transaction = function (callback) {\n    if(!conn) {\n      throw new Error(\"No ongoing database connection, please connect first.\");\n    } else {\n      conn.transaction(callback);\n    }\n  };\n\n  ////////// Low-level database interface, abstracting from HTML5 and Gears databases \\\\\\\\\n  persistence.db = persistence.db || {};\n\n  persistence.db.conn = null;\n\n  persistence.db.titanium = {};\n\n  persistence.db.titanium.connect = function (dbname) {\n    var that = {};\n    var conn = Titanium.Database.open(dbname);\n\n    that.transaction = function (fn) {\n      fn(persistence.db.titanium.transaction(conn));\n    };\n    return that;\n  };\n\n  persistence.db.titanium.transaction = function (conn) {\n    var that = {};\n    that.executeSql = function (query, args, successFn, errorFn) {\n\n      if(persistence.debug) {\n        console.log(query, args);\n      }\n      try {\n        var executeVarArgs = [query];\n        if (args) {\n          executeVarArgs = executeVarArgs.concat(args);\n        };\n        var rs = Function.apply.call(conn.execute, conn, executeVarArgs);\n        if (successFn) {\n          var results = [];\n          if (rs) {\n            while (rs.isValidRow()) {\n              var result = {};\n              for ( var i = 0; i < rs.fieldCount(); i++) {\n                result[rs.fieldName(i)] = rs.field(i);\n              }\n              results.push(result);\n              rs.next();\n            }\n            rs.close();\n          };\n          successFn(results);\n        }\n      } catch(e) {\n        if (errorFn) {\n          errorFn(null, e);\n        };\n      }\n    };\n    return that;\n  };\n\n  ///////////////////////// SQLite dialect\n\n  persistence.store.titanium.sqliteDialect = {\n    // columns is an array of arrays, e.g.\n    // [[\"id\", \"VARCHAR(32)\", \"PRIMARY KEY\"], [\"name\", \"TEXT\"]]\n    createTable: function(tableName, columns) {\n      var tm = persistence.typeMapper;\n      var sql = \"CREATE TABLE IF NOT EXISTS `\" + tableName + \"` (\";\n      var defs = [];\n      for(var i = 0; i < columns.length; i++) {\n        var column = columns[i];\n        defs.push(\"`\" + column[0] + \"` \" + tm.columnType(column[1]) + (column[2] ? \" \" + column[2] : \"\"));\n      }\n      sql += defs.join(\", \");\n      sql += ')';\n      return sql;\n    },\n\n    // columns is array of column names, e.g.\n    // [\"id\"]\n    createIndex: function(tableName, columns, options) {\n      options = options || {};\n      return \"CREATE \"+(options.unique?\"UNIQUE \":\"\")+\"INDEX IF NOT EXISTS `\" + tableName + \"__\" + columns.join(\"_\") +\n             \"` ON `\" + tableName + \"` (\" +\n             columns.map(function(col) { return \"`\" + col + \"`\"; }).join(\", \") + \")\";\n    },\n\n    typeMapper: {\n      idType: persistence.store.sql.defaultTypeMapper.idType,\n      classNameType: persistence.store.sql.defaultTypeMapper.classNameType,\n      inVar: persistence.store.sql.defaultTypeMapper.inVar,\n      outVar: persistence.store.sql.defaultTypeMapper.outVar,\n      outId: persistence.store.sql.defaultTypeMapper.outId,\n      inIdVar: persistence.store.sql.defaultTypeMapper.inIdVar,\n      outIdVar: persistence.store.sql.defaultTypeMapper.outIdVar,\n      entityIdToDbId: persistence.store.sql.defaultTypeMapper.entityIdToDbId,\n      zeroPaddingMap: ['0000000000000000',\n                       '000000000000000',\n                       '00000000000000',\n                       '0000000000000',\n                       '000000000000',\n                       '00000000000',\n                       '0000000000',\n                       '000000000',\n                       '00000000',\n                       '0000000',\n                       '000000',\n                       '00000',\n                       '0000',\n                       '000',\n                       '00',\n                       '0'],\n      zeroPadded: function(val) {\n        var result = val.toString();\n        if (result.length < 16) {\n          return persistence.store.titanium.sqliteDialect.typeMapper.zeroPaddingMap[result.length] + result;\n        } else {\n          return result;\n        };\n      },\n      columnType: function(type) {\n        if (type === 'BIGINT') {\n          return 'TEXT';\n        } else {\n          return persistence.store.sql.defaultTypeMapper.columnType(type);\n        };\n      },\n      dbValToEntityVal: function(val, type){\n        if (val === null || val === undefined) {\n          return val;\n        } else if (type === 'BIGIN') {\n          return parseInt(val);\n        } else {\n          return persistence.store.sql.defaultTypeMapper.dbValToEntityVal(val, type);\n        }\n      },\n      entityValToDbVal: function(val, type){\n        if (val === undefined || val === null) {\n          return null;\n        } else if (type === 'BIGINT') {\n          return persistence.store.titanium.sqliteDialect.typeMapper.zeroPadded(val);\n        } else {\n          return persistence.store.sql.defaultTypeMapper.entityValToDbVal(val, type);\n        };\n      }\n    }\n  };\n\n  // Configure persistence for generic sql persistence, using sqliteDialect\n  persistence.store.sql.config(persistence, persistence.store.titanium.sqliteDialect);\n\n  // Make the connection\n  conn = persistence.db.titanium.connect(dbname);\n  if(!conn) {\n    throw new Error(\"No supported database found\");\n  }\n};\n\ntry {\n  exports.persistence = persistence;\n} catch(e) {}\n"
  },
  {
    "path": "lib/persistence.store.websql.js",
    "content": "try {\n  if(!window) {\n    window = {};\n    //exports.console = console;\n  }\n} catch(e) {\n  window = {};\n  exports.console = console;\n}\n\nvar persistence = (window && window.persistence) ? window.persistence : {}; \n\nif(!persistence.store) {\n  persistence.store = {};\n}\n\npersistence.store.websql = {};\n\n\npersistence.store.websql.config = function(persistence, dbname, description, size) {\n  var conn = null;\n\n  /**\n   * Create a transaction\n   * \n   * @param callback,\n   *            the callback function to be invoked when the transaction\n   *            starts, taking the transaction object as argument\n   */\n  persistence.transaction = function (callback) {\n    if(!conn) {\n      throw new Error(\"No ongoing database connection, please connect first.\");\n    } else {\n      conn.transaction(callback);\n    }\n  };\n\n  ////////// Low-level database interface, abstracting from HTML5 and Gears databases \\\\\\\\\n  persistence.db = persistence.db || {};\n\n  persistence.db.implementation = \"unsupported\";\n  persistence.db.conn = null;\n\n  // window object does not exist on Qt Declarative UI (http://doc.trolltech.org/4.7-snapshot/declarativeui.html)\n  if (window && window.openDatabase) {\n    persistence.db.implementation = \"html5\";\n  } else if (window && window.google && google.gears) {\n    persistence.db.implementation = \"gears\";\n  } else {\n    try {\n      if (openDatabaseSync) {\n        // TODO: find a browser that implements openDatabaseSync and check out if\n        //       it is attached to the window or some other object\n        persistence.db.implementation = \"html5-sync\";\n      }\n    } catch(e) {\n    }\n  }\n\n  persistence.db.html5 = {};\n\n  persistence.db.html5.connect = function (dbname, description, size) {\n    var that = {};\n    var conn = openDatabase(dbname, '1.0', description, size);\n\n    that.transaction = function (fn) {\n      return conn.transaction(function (sqlt) {\n          return fn(persistence.db.html5.transaction(sqlt));\n        });\n    };\n    return that;\n  };\n\n  persistence.db.html5.transaction = function (t) {\n    var that = {};\n    that.executeSql = function (query, args, successFn, errorFn) {\n      if(persistence.debug) {\n        console.log(query, args);\n      }\n      t.executeSql(query, args, function (_, result) {\n          if (successFn) {\n            var results = [];\n            for ( var i = 0; i < result.rows.length; i++) {\n              results.push(result.rows.item(i));\n            }\n            successFn(results);\n          }\n        }, errorFn);\n    };\n    return that;\n  };\n\n  persistence.db.html5Sync = {};\n\n  persistence.db.html5Sync.connect = function (dbname, description, size) {\n    var that = {};\n    var conn = openDatabaseSync(dbname, '1.0', description, size);\n\n    that.transaction = function (fn) {\n      return conn.transaction(function (sqlt) {\n          return fn(persistence.db.html5Sync.transaction(sqlt));\n        });\n    };\n    return that;\n  };\n\n  persistence.db.html5Sync.transaction = function (t) {\n    var that = {};\n    that.executeSql = function (query, args, successFn, errorFn) {\n      if (args == null) args = [];\n\n      if(persistence.debug) {\n        console.log(query, args);\n      }\n\n      var result = t.executeSql(query, args);\n      if (result) {\n        if (successFn) {\n          var results = [];\n          for ( var i = 0; i < result.rows.length; i++) {\n            results.push(result.rows.item(i));\n          }\n          successFn(results);\n        }\n      }\n    };\n    return that;\n  };\n\n  persistence.db.gears = {};\n\n  persistence.db.gears.connect = function (dbname) {\n    var that = {};\n    var conn = google.gears.factory.create('beta.database');\n    conn.open(dbname);\n\n    that.transaction = function (fn) {\n      fn(persistence.db.gears.transaction(conn));\n    };\n    return that;\n  };\n\n  persistence.db.gears.transaction = function (conn) {\n    var that = {};\n    that.executeSql = function (query, args, successFn, errorFn) {\n      if(persistence.debug) {\n        console.log(query, args);\n      }\n      var rs = conn.execute(query, args);\n      if (successFn) {\n        var results = [];\n        while (rs.isValidRow()) {\n          var result = {};\n          for ( var i = 0; i < rs.fieldCount(); i++) {\n            result[rs.fieldName(i)] = rs.field(i);\n          }\n          results.push(result);\n          rs.next();\n        }\n        successFn(results);\n      }\n    };\n    return that;\n  };\n\n  persistence.db.connect = function (dbname, description, size) {\n    if (persistence.db.implementation == \"html5\") {\n      return persistence.db.html5.connect(dbname, description, size);\n    } else if (persistence.db.implementation == \"html5-sync\") {\n      return persistence.db.html5Sync.connect(dbname, description, size);\n    } else if (persistence.db.implementation == \"gears\") {\n      return persistence.db.gears.connect(dbname);\n    }\n  };\n\n  ///////////////////////// SQLite dialect\n\n  persistence.store.websql.sqliteDialect = {\n    // columns is an array of arrays, e.g.\n    // [[\"id\", \"VARCHAR(32)\", \"PRIMARY KEY\"], [\"name\", \"TEXT\"]]\n    createTable: function(tableName, columns) {\n      var tm = persistence.typeMapper;\n      var sql = \"CREATE TABLE IF NOT EXISTS `\" + tableName + \"` (\";\n      var defs = [];\n      for(var i = 0; i < columns.length; i++) {\n        var column = columns[i];\n        defs.push(\"`\" + column[0] + \"` \" + tm.columnType(column[1]) + (column[2] ? \" \" + column[2] : \"\"));\n      }\n      sql += defs.join(\", \");\n      sql += ')';\n      return sql;\n    },\n\n    // columns is array of column names, e.g.\n    // [\"id\"]\n    createIndex: function(tableName, columns, options) {\n      options = options || {};\n      return \"CREATE \"+(options.unique?\"UNIQUE \":\"\")+\"INDEX IF NOT EXISTS `\" + tableName + \"__\" + columns.join(\"_\") + \n             \"` ON `\" + tableName + \"` (\" + \n             columns.map(function(col) { return \"`\" + col + \"`\"; }).join(\", \") + \")\";\n    }\n  };\n\n  // Configure persistence for generic sql persistence, using sqliteDialect\n  persistence.store.sql.config(persistence, persistence.store.websql.sqliteDialect);\n\n  // Make the connection\n  conn = persistence.db.connect(dbname, description, size);\n  if(!conn) {\n    throw new Error(\"No supported database found in this browser.\");\n  }\n};\n\ntry {\n  exports.persistence = persistence;\n} catch(e) {}\n"
  },
  {
    "path": "lib/persistence.sync.js",
    "content": "/**\n * @license\n * Copyright (c) 2010 Zef Hemel <zef@zef.me>\n * \n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n * \n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\nif(!window.persistence) { // persistence.js not loaded!\n  throw new Error(\"persistence.js should be loaded before persistence.sync.js\");\n}\n\npersistence.sync = {};\n\npersistence.sync.getJSON = function(uri, callback, errorCallback) {\n    var xmlHttp = new XMLHttpRequest();\n    xmlHttp.open(\"GET\", uri, true);\n    xmlHttp.send();\n    xmlHttp.onreadystatechange = function() {\n      if(xmlHttp.readyState==4) {\n        if(xmlHttp.status==200) {\n            callback(JSON.parse(xmlHttp.responseText));\n        } else if(typeof errorCallback === 'function') {\n            errorCallback(xmlHttp);\n        }\n      }\n    };\n};\n\npersistence.sync.postJSON = function(uri, data, callback) {\n    var xmlHttp = new XMLHttpRequest();\n    xmlHttp.open(\"POST\", uri, true);\n    xmlHttp.setRequestHeader('Content-Type', 'application/json');\n    xmlHttp.send(data);\n    xmlHttp.onreadystatechange = function() {\n      if(xmlHttp.readyState==4 && xmlHttp.status==200) {\n        callback(JSON.parse(xmlHttp.responseText));\n      }\n    };\n}; \n\n\n(function() {\n\n    var argspec = persistence.argspec;\n\n    persistence.sync.Sync = persistence.define('_Sync', {\n        entity: \"VARCHAR(255)\",\n        localDate: \"BIGINT\",\n        serverDate: \"BIGINT\",\n        serverPushDate: \"BIGINT\"\n      });\n\n    persistence.sync.RemovedObject = persistence.define('_SyncRemovedObject', {\n        entity: \"VARCHAR(255)\",\n        objectId: \"VARCHAR(32)\"\n      });\n\n\n    function getEpoch(date) {\n      return date.getTime();\n    }\n\n    persistence.sync.preferLocalConflictHandler = function(conflicts, updatesToPush, callback) {\n      conflicts.forEach(function(conflict) {\n          var update = {id: conflict.local.id};\n          conflict.properties.forEach(function(p) {\n              update[p] = conflict.local._data[p];\n            });\n          updatesToPush.push(update);\n        });\n      callback();\n    };\n\n    persistence.sync.preferRemoteConflictHandler = function(conflicts, updatesToPush, callback) {\n      conflicts.forEach(function(conflict) {\n          conflict.properties.forEach(function(p) {\n              conflict.local[p] = conflict.remote[p];\n            });\n        });\n      persistence.flush(callback);\n    };\n\n    function encodeUrlObj(obj) {\n      var parts = [];\n      for(var k in obj) {\n        if(obj.hasOwnProperty(k)) {\n          parts.push(encodeURI(k)+\"=\"+encodeURI(obj[k]));\n        }\n      }\n      return \"?\" + parts.join(\"&\");\n    }\n\n    /**\n     * The main caching and updating function, would be nice to refactor this\n     */\n    function cacheAndFindUpdates(session, Entity, objects, lastLocalSyncTime, lastServerPushTime, conflictCallback, callback) {\n      var ids = [];\n      var lookupTbl = {};\n\n      var conflicts = [];\n      var updatesToPush = [];\n      var meta = Entity.meta;\n      var fieldSpec = meta.fields; \n      \n      var objectsToRemove = [];\n\n      objects.forEach(function(item) {\n          if(item._removed) { // special marker\n            objectsToRemove.push(item.id);\n          } else {\n            ids.push(item.id);\n            lookupTbl[item.id] = item;\n          }\n        });\n      // Step 1: Look at local versions of remotely updated entities\n      var existingItems = [], groupedIds = [];\n      for (var i=0,l=Math.floor((ids.length/100)+1);i<l;i++) {\n        groupedIds.push(ids.slice(i*100, i*100+100));\n      }\n      persistence.asyncForEach(groupedIds, function(idGroup, next) {\n        Entity.all(session).filter('id', 'in', idGroup).list(function(groupOfExistingItems) {\n          existingItems.concat(groupOfExistingItems);\n          next();\n        });\n      }, function() {\n          existingItems.forEach(function(localItem) {\n              var remoteItem = lookupTbl[localItem.id];\n              delete remoteItem.id;\n              delete lookupTbl[localItem.id];\n\n              var localChangedSinceSync = lastLocalSyncTime < localItem._lastChange;\n              var itemUpdatedFields = { id: localItem.id };\n              var itemUpdated = false;\n              var conflictingProperties = [];\n              for(var p in remoteItem) {\n                if(remoteItem.hasOwnProperty(p) && p !== '_lastChange') {\n                  if(localItem._data[p] !== remoteItem[p]) {\n                    if(localChangedSinceSync && remoteItem._lastChange === lastServerPushTime) { \n                      // Unchanged at server, but changed locally\n                      itemUpdatedFields[p] = localItem._data[p];\n                      itemUpdated = true;\n                    } else if(localChangedSinceSync) { // Conflict!\n                      conflictingProperties.push(p);\n                    } else {\n                      localItem[p] = persistence.jsonToEntityVal(remoteItem[p], fieldSpec[p]);\n                    }\n                  }\n                } \n              }\n              if(itemUpdated) {\n                updatesToPush.push(itemUpdatedFields);\n              }\n              if(conflictingProperties.length > 0) {\n                conflicts.push({local: localItem, remote: remoteItem, properties: conflictingProperties});\n              }\n            });\n          // Step 2: Remove all remotely removed objects\n          var groupedObjectsToRemove = [];\n          for (var i=0,l=Math.floor((objectsToRemove.length/100)+1);i<l;i++) {\n            groupedObjectsToRemove.push(objectsToRemove.slice(i*100, i*100+100));\n          }\n\n          /* ensure IDs var chars */\n          var idVals = new Array();\n          for(var id=0;id<group.length;id++){\n            idVals.push( persistence.typeMapper.entityIdToDbId(group[id]));\n          }\n\n          persistence.asyncForEach(groupedObjectsToRemove, function(group, next) {\n            Entity.all(session).filter('id', 'in', group).destroyAll(next);\n          }, function() {\n              // Step 3: store new remote items locally\n              // NOTE: all that's left in lookupTbl is new, we deleted the existing items\n              for(var id in lookupTbl) {\n                if(lookupTbl.hasOwnProperty(id)) {\n                  var remoteItem = lookupTbl[id];\n                  delete remoteItem.id;\n                  var localItem = new Entity(remoteItem);\n                  localItem.id = id;\n                  localItem._lastChange = getEpoch(new Date());\n                  session.add(localItem);\n                }\n              }\n              // Step 4: Find local new/updated/removed items (not part of the remote change set)\n              Entity.all(session).filter(\"_lastChange\", \">\", lastLocalSyncTime).list(function(allNewItems) {\n                  var newItems = [];\n                  for (var i=0,l=allNewItems.length;i<l;i++) {\n                    if (ids.indexOf(allNewItems[i].id)===-1) {\n                      newItems.push(allNewItems[i]);\n                    }\n                  }\n                  console.log(\"New items: \", newItems);\n                  newItems.forEach(function(newItem) {\n                      var update = { id: newItem.id };\n                      for(var p in fieldSpec) {\n                        if(fieldSpec.hasOwnProperty(p) && p != '_lastChange') {\n                          update[p] = persistence.entityValToJson(newItem._data[p], fieldSpec[p]);\n                        }\n                      }\n                      for(var p in meta.hasOne) {\n                        if(meta.hasOne.hasOwnProperty(p)) {\n                          update[p] = persistence.entityValToJson(newItem._data[p], fieldSpec[p]);\n                        }\n                      }\n                      updatesToPush.push(update);\n                    });\n                  var removedObjColl = persistence.sync.RemovedObject.all(session).filter(\"entity\", \"=\", meta.name);\n                  removedObjColl.list(function(objs) {\n                      objs.forEach(function(obj) {\n                          updatesToPush.push({id: obj.objectId, _removed: true});\n                        });\n                      function next() {\n                        removedObjColl.destroyAll(function() {\n                            callback(updatesToPush);\n                          });\n                      }\n                      if(conflicts.length > 0) {\n                        conflictCallback(conflicts, updatesToPush, next);\n                      } else {\n                        next();\n                      }\n                    })\n                });\n            });\n        });\n    }\n\n    persistence.sync.serviceSync = function(session, uri, args, conflictCallback, callback) {\n      persistence.sync.Sync.findBy(session, 'service', uri, function(sync) {\n          var lastServerSyncTime = sync ? persistence.get(sync, 'serverDate') : 0;\n          var lastServerPushTime = sync ? persistence.get(sync, 'serverPushDate') : 0;\n          var lastLocalSyncTime = sync ? persistence.get(sync, 'localDate') : 0;\n          if(!sync) {\n            sync = new persistence.sync.Sync(session, {service: uri});\n            session.add(sync);\n          }\n          if(!args.since) args.since = lastServerSyncTime;\n          persistence.sync.getJSON(uri + encodeUrlObj(args), function(result) {\n              var allUpdates = [];\n              cacheAndFindUpdates(session, Entity, result.updates, lastLocalSyncTime, lastServerPushTime, conflictCallback, function(updatesToPush) {\n                  persistence.sync.postJSON(uri, JSON.stringify(updatesToPush), function(pushData) {\n                      session.flush(function() {\n                          sync.localDate = getEpoch(new Date());\n                          sync.serverDate = result.now;\n                          sync.serverPushDate = pushData.now;\n                          session.flush(callback);\n                        });\n                    });\n                });\n            });\n        });\n      session.flush(function() {\n          persistence.sync.getJSON(uri + encodeUrlObj(args), function(result) {\n            });\n        });\n    };\n    \n\n    function synchronize(session, uri, Entity, conflictCallback, callback, errorCallback) {\n      persistence.sync.Sync.findBy(session, 'entity', Entity.meta.name, function(sync) {\n          var lastServerSyncTime = sync ? persistence.get(sync, 'serverDate') : 0;\n          var lastServerPushTime = sync ? persistence.get(sync, 'serverPushDate') : 0;\n          var lastLocalSyncTime = sync ? persistence.get(sync, 'localDate') : 0;\n          if(!sync) {\n            sync = new persistence.sync.Sync(session, {entity: Entity.meta.name});\n            session.add(sync);\n          }\n          persistence.sync.getJSON(uri + '?since=' + lastServerSyncTime, function(result) {\n              cacheAndFindUpdates(session, Entity, result.updates, lastLocalSyncTime, lastServerPushTime, conflictCallback, function(updatesToPush) {\n                  persistence.sync.postJSON(uri, JSON.stringify(updatesToPush), function(pushData) {\n                      session.flush(function() {\n                          sync.localDate = getEpoch(new Date());\n                          sync.serverDate = result.now;\n                          sync.serverPushDate = pushData.now;\n                          session.flush(callback);\n                        });\n                    });\n                });\n            },\n            errorCallback);\n        });\n    }\n\n    persistence.entityDecoratorHooks.push(function(Entity) {\n        /**\n         * Declares an entity to be tracked for changes\n         */\n        Entity.enableSync = function(uri) {\n          Entity.meta.enableSync = true;\n          Entity.meta.syncUri = uri;\n          Entity.meta.fields['_lastChange'] = 'BIGINT';\n        };\n\n        Entity.syncAll = function(session, uri, conflictCallback, callback, errorCallback) {\n          var args = argspec.getArgs(arguments, [\n              { name: 'session', optional: true, check: function(obj) { return obj && obj.flush; }, defaultValue: persistence },\n              { name: 'uri', optional: true, check: argspec.hasType('string'), defaultValue: this.meta.syncUri },\n              { name: 'conflictCallback', check: argspec.isCallback() },\n              { name: 'callback', check: argspec.isCallback() },\n              { name: 'errorCallback', optional: true, check: argspec.isCallback() },\n            ]);\n          synchronize(args.session, args.uri, this, args.conflictCallback, args.callback, args.errorCallback);\n        };\n      });\n\n    /**\n     * Resets _lastChange property if the object has dirty project (i.e. the object has changed)\n     */\n    persistence.flushHooks.push(function(session, tx, callback) {\n        var queries = [];\n        for (var id in session.getTrackedObjects()) {\n          if (session.getTrackedObjects().hasOwnProperty(id)) {\n            var obj = session.getTrackedObjects()[id];\n            var meta = persistence.getEntityMeta()[obj._type];\n            if(meta.enableSync) {\n              var isDirty = obj._new;\n              for ( var p in obj._dirtyProperties) {\n                if (obj._dirtyProperties.hasOwnProperty(p)) {\n                  isDirty = true;\n                }\n              }\n              if(isDirty) {\n                obj._lastChange = getEpoch(new Date());\n              }\n            }\n          }\n        }\n        session.objectsRemoved.forEach(function(rec) {\n            var meta = session.getMeta(rec.entity);\n            if(meta.enableSync) {\n              session.add(new persistence.sync.RemovedObject({entity: rec.entity, objectId: rec.id}));\n            }\n          });\n        session.objectsRemoved=[];\n        callback();\n      });\n\n  }());\n\n"
  },
  {
    "path": "lib/persistence.sync.server.js",
    "content": "/**\n * Copyright (c) 2010 Zef Hemel <zef@zef.me>\n * \n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n * \n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n *\n *\n * USAGE:\n * On first run, be sure to initialize the database first: http://localhost:8888/init\n * otherwise the application will hang (because the select query fails). After that,\n * just visit http://localhost:8888/\n */\nvar sys = require('sys');\n\nfunction log(o) {\n  sys.print(sys.inspect(o) + \"\\n\");\n}\n\nfunction jsonToEntityVal(value, type) {\n  if(type) {\n    switch(type) {\n    case 'DATE': \n      if (value > 1000000000000) {\n        // it's in milliseconds\n        return new Date(value); \n      } else {\n        return new Date(value * 1000); \n      }\n      break;\n    default:\n      return value;\n    }\n  } else {\n    return value;\n  }\n}\n\nfunction getEpoch(date) {\n  return date.getTime(); //Math.round(date.getTime()/1000);\n}\n\nfunction entityValToJson(value, type) {\n  if(type) {\n    switch(type) {\n    case 'DATE': \n      return Math.round(date.getTime() / 1000);\n      break;\n    default:\n      return value;\n    }\n  } else {\n    return value;\n  }\n}\n\n\nexports.pushUpdates = function(session, tx, Entity, since, callback) {\n  var queryCollection;\n  if(typeof(Entity) == \"function\"){\n    queryCollection = Entity.all(session);\n  }else if(typeof(Entity) == \"object\"){\n    queryCollection = Entity;\n  }\n  queryCollection.filter(\"_lastChange\", \">\", since).list(tx, function(items) {\n      var results = [];\n      var meta = Entity.meta;\n      var fieldSpec = meta.fields;\n      for(var i = 0; i < items.length; i++) {\n        var itemData = items[i]._data;\n        var item = {id: items[i].id};\n        for(var p in fieldSpec) {\n          if(fieldSpec.hasOwnProperty(p)) {\n            item[p] = entityValToJson(itemData[p], fieldSpec[p]);\n          }\n        }\n        for(var p in meta.hasOne) {\n          if(meta.hasOne.hasOwnProperty(p)) {\n            item[p] = entityValToJson(itemData[p]);\n          }\n        }\n        results.push(item);\n      }\n      if(since>0){\n        session.sync.RemovedObject.all(session).filter(\"entity\", \"=\", meta.name).filter(\"date\", \">\", since).list(tx, function(items) {\n            for(var i = 0; i < items.length; i++) {\n              results.push({id: items[i].id, _removed: true});\n            }\n            callback({now: getEpoch(new Date()), updates: results});\n          });\n      }else{\n          callback({now: getEpoch(new Date()), updates: results});\n      }\n    });\n};\n\nexports.receiveUpdates = function(session, tx, Entity, updates, callback) {\n  var allIds = [];\n  var updateLookup = {};\n  var now = getEpoch(new Date());\n  var removedIds = [];\n  for(var i = 0; i < updates.length; i++) {\n    if(updates[i]._removed) { // removed\n      removedIds.push(updates[i].id);\n    } else {\n      allIds.push(updates[i].id);\n      updateLookup[updates[i].id] = updates[i];\n    }\n  }\n  Entity.all(session).filter(\"id\", \"in\", removedIds).destroyAll(function() {\n      removedIds.forEach(function(id) {\n          session.add(new session.sync.RemovedObject({objectId: id, entity: Entity.meta.name, date: now}));\n        });\n      Entity.all(session).filter(\"id\", \"in\", allIds).list(tx, function(existingItems) {\n          var fieldSpec = Entity.meta.fields;\n\n          for(var i = 0; i < existingItems.length; i++) {\n            var existingItem = existingItems[i];\n            var updateItem = updateLookup[existingItem.id];\n            for(var p in updateItem) {\n              if(updateItem.hasOwnProperty(p)) {\n                if(updateItem[p] !== existingItem._data[p]) {\n                  existingItem[p] = jsonToEntityVal(updateItem[p], fieldSpec[p]);\n                  existingItem._lastChange = now;\n                }\n              }\n            }\n            delete updateLookup[existingItem.id];\n          }\n          // All new items\n          for(var id in updateLookup) {\n            if(updateLookup.hasOwnProperty(id)) {\n              var update = updateLookup[id];\n              delete update.id;\n              var newItem = new Entity(session);\n              newItem.id = id;\n              for(var p in update) {\n                if(update.hasOwnProperty(p)) {\n                  newItem[p] = jsonToEntityVal(update[p], fieldSpec[p]);\n                }\n              }\n              newItem._lastChange = now;\n              session.add(newItem);\n            }\n          }\n          session.flush(tx, function() {\n              callback({status: 'ok', now: now});\n            });\n        });\n    });\n};\n\nexports.config = function(persistence) {\n  persistence.sync = persistence.sync || {};\n  persistence.sync.RemovedObject = persistence.define('_SyncRemovedObject', {\n      entity: \"VARCHAR(255)\",\n      objectId: \"VARCHAR(32)\",\n      date: \"BIGINT\"\n    });\n\n    persistence.entityDecoratorHooks.push(function(Entity) {\n        /**\n         * Declares an entity to be tracked for changes\n         */\n        Entity.enableSync = function() {\n          Entity.meta.enableSync = true;\n          Entity.meta.fields['_lastChange'] = 'BIGINT';\n        };\n      });\n\n    /**\n     * Resets _lastChange property if the object has dirty project (i.e. the object has changed)\n     */\n    persistence.flushHooks.push(function(session, tx, callback) {\n        var queries = [];\n        for (var id in session.getTrackedObjects()) {\n          if (session.getTrackedObjects().hasOwnProperty(id)) {\n            var obj = session.getTrackedObjects()[id];\n            var meta = persistence.getEntityMeta()[obj._type];\n            if(meta.enableSync) {\n              var isDirty = obj._new;\n              var lastChangeIsDirty = false;\n              for ( var p in obj._dirtyProperties) {\n                if (obj._dirtyProperties.hasOwnProperty(p)) {\n                  isDirty = true;\n                }\n                if(p === '_lastChange') {\n                  lastChangeIsDirty = true;\n                }\n              }\n              if(isDirty && !lastChangeIsDirty) {\n                // Only set _lastChange if it has not been set manually (during a sync)\n                obj._lastChange = getEpoch(new Date());\n              }\n            }\n          }\n        }\n        session.objectsRemoved.forEach(function(rec) {\n            var meta = session.getMeta(rec.entity);\n            if(meta.enableSync) {\n              session.add(new persistence.sync.RemovedObject({entity: rec.entity, objectId: rec.id, date: getEpoch(new Date())}));\n            }\n          });\n        callback();\n      });\n  };\n"
  },
  {
    "path": "lib/persistence.sync.server.php",
    "content": "<?php\n\n/**\n * Copyright (c) 2010 Zef Hemel <zef@zef.me>\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n *\n *\n * USAGE:\n * Before this code can be used to persist data in the database the file persistence.sync.server.php.sql must be run\n *\n * This is NOT intended to be used without modification as it implements only the mimimal set of functionality to\n * get persistence working. It does not include any kind of security model for example.\n */\n\n/**\n * Requires that the database schema be setup by running:\n *\n * persistence.sync.server.php.sql\n */\nclass PersistenceDB {\n    private $db;\n    private $persistence_table;\n\n    function __construct(PDO $db, $persistence_table) {\n        $this->db = $db;\n        $this->persistence_table = $persistence_table;\n    }\n\n    public function getObjectChanges($bucket, $since) {\n        $statement = $this->db->prepare(\"SELECT content FROM {$this->persistence_table} WHERE bucket=:bucket AND lastUpdated > :since\");\n        $statement->execute(array(':bucket' => $bucket, ':since' => $since));\n        $changes = array();\n        foreach ($statement->fetchAll(PDO::FETCH_COLUMN) as $content) {\n            $change = json_decode($content);\n            // Don't bother sending removed items to fresh clients\n            if ($since != 0 || !isset($change->_removed)) {\n                $changes[] = $change;\n            }\n        }\n\n        return $changes;\n    }\n\n    public function applyObjectChanges($bucket, $now, array $changes) {\n        $statement = $this->db->prepare(\"\n            INSERT INTO {$this->persistence_table} (id, bucket, lastUpdated, content)\n            VALUES (:id, :bucket, :lastUpdated, :content)\n            ON DUPLICATE KEY UPDATE lastUpdated=:lastUpdated, content=:content\");\n\n        foreach ($changes as $change) {\n            $change->_lastChanged = $now;\n            $statement->execute(array(':id' => $change->id, ':bucket' => $bucket, ':lastUpdated' => $now, ':content' => json_encode($change)));\n        }\n    }\n}\n\n$db = new PersistenceDB(new PDO('mysql:host=localhost;dbname=persistencejs', 'root', ''), 'persistencejs_objects');\n\nfunction http_400() {\n    header($_SERVER['SERVER_PROTOCOL'] . ' 400 Invalid Request');\n    exit(0);\n}\n\nheader('Content-Type: applicatin/json');\n\nswitch (strtoupper($_SERVER['REQUEST_METHOD'])) {\n    case 'GET':\n        if (!isset($_GET['bucket']) || !isset($_GET['since']))\n            http_400();\n\n        $bucket = $_GET['bucket'];\n        $since = isset($_GET['since']) ? $_GET['since'] : 0;\n\n\n        $changes = $db->getObjectChanges($bucket, $since);\n        echo json_encode(array('now' => round(microtime(true) * 1000), \"updates\" => $changes));\n        break;\n    case 'POST':\n        $body = file_get_contents('php://input');\n        $changes = json_decode($body);\n        $now = floor(microtime(true)*1000);\n        $db->applyObjectChanges($bucket, $now, $changes);\n        echo json_encode(array('now' => $now, \"status\" => 'ok'));\n        break;\n    default:\n        header($_SERVER['SERVER_PROTOCOL'] . ' 405 Invalid Request');\n}\n"
  },
  {
    "path": "lib/persistence.sync.server.php.sql",
    "content": "-- This table must exist in the database for synchronization with the php version of the server to run.\n-- This is definitely not an efficient, but it's about as simple as it gets\n\nCREATE TABLE `persistencejs_objects` (\n  `id` char(32) NOT NULL,\n  `bucket` varchar(50) NOT NULL,\n  `lastUpdated` bigint(20) NOT NULL,\n  `content` text NOT NULL,\n  PRIMARY KEY (`id`),\n  KEY `ix_objects_lastUpdated` (`lastUpdated`),\n  KEY `ix_bucket` (`bucket`)\n)  DEFAULT CHARSET=utf8"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"persistencejs\",\n  \"version\": \"0.3.0\",\n  \"engine\": \"node >=0.2.0\",\n  \"author\": \"Zef Hemel\",\n  \"directories\": {\"lib\": \"./lib\"}\n}\n"
  },
  {
    "path": "test/appengine/test.js",
    "content": "// Run with RingoJS: http://ringojs.org\n// Set path below to AppEngine Java SDK path\nvar appEngineSdkPath = '/Users/zef/Software/appengine-java-sdk';\n\naddToClasspath(appEngineSdkPath + \"/lib/impl/appengine-api-stubs.jar\");\naddToClasspath(appEngineSdkPath + \"/lib/impl/appengine-api.jar\");\naddToClasspath(appEngineSdkPath + \"/lib/impl/appengine-api-labs.jar\");\naddToClasspath(appEngineSdkPath + \"/lib/impl/appengine-api-stubs.jar\");\naddToClasspath(appEngineSdkPath + \"/lib/testing/appengine-testing.jar\");\n\nvar persistence = require('../../lib/persistence').persistence;\nvar persistenceStore = require('../../lib/persistence.store.appengine');\nvar assert = require(\"assert\");\n\nvar JLocalDatastoreServiceTestConfig = com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;\nvar JLocalServiceTestHelper = com.google.appengine.tools.development.testing.LocalServiceTestHelper;\n\nvar helper = new JLocalServiceTestHelper(new JLocalDatastoreServiceTestConfig());\n\n\npersistenceStore.config(persistence);\n\nvar Project = persistence.define('Project', {\n    name: \"TEXT\"\n  });\n\nvar Task = persistence.define('Task', {\n    name: \"TEXT\",\n    done: \"BOOL\",\n    counter: \"INT\",\n    dateAdded: \"DATE\",\n    metaData: \"JSON\"\n  });\n\nvar Tag = persistence.define('Tag', {\n    name: \"TEXT\"\n  });\n\nTask.hasMany('tags', Tag, 'tasks');\nTag.hasMany('tasks', Task, 'tags');\nTask.index('dateAdded');\n\nProject.hasMany('tasks', Task, 'project');\n\nfunction intFilterTests(session, coll, callback) {\n  for(var i = 0; i < 25; i++) {\n    var t = new Task(session, {name: \"Task \" + i, done: false});\n    t.counter = i;\n    coll.add(t);\n  }\n  coll.list(function(results) {\n      assert.equal(results.length, 25, \"Count items in collection\");\n      coll.filter(\"counter\", \">\", 10).list(function(results) {\n          assert.equal(results.length, 14, \"> filter test\");\n          coll.filter(\"counter\", \"in\", [0, 1, 2]).list(function(results) {\n              assert.equal(results.length, 3, \"'in' filter test\");\n              coll.filter(\"counter\", \"!=\", 0).list(function(results) {\n                  assert.equal(results.length, 24, \"'!=' filter test\");\n                  callback();\n                });\n            });\n        });\n    });\n}\n\nfunction textFilterTests(session, coll, callback) {\n  var alphabet = 'abcdefghijklmnopqrstufwxyz';\n  for(var i = 0; i <= 25; i++) {\n    var t = new Task(session, {name: alphabet[i]});\n    coll.add(t);\n  }\n  coll.list(function(results) {\n      assert.equal(results.length, 26, \"Count items in collection\");\n      coll.filter(\"name\", \"=\", 'a').list(function(results) {\n          assert.equal(results.length, 1, \"= filter test\");\n          coll.filter(\"name\", \"!=\", 'a').list(function(results) {\n              assert.equal(results.length, 25, \"!= filter test\");\n              coll.filter(\"name\", \">\", 'm').list(function(results) {\n                  assert.equal(results.length, 12, \"> filter test\");\n                  coll.filter(\"name\", \"in\", [\"a\", \"b\"]).list(function(results) {\n                      assert.equal(results.length, 2, \"'in' filter test\");\n                      callback();\n                    });\n                });\n            });\n        });\n    });\n}\n\nfunction boolFilterTests(session, coll, callback) {\n  for(var i = 0; i < 24; i++) {\n    var t = new Task(session, {name: \"Task \" + i, done: i % 2 === 0});\n    coll.add(t);\n  }\n  coll.list(function(results) {\n      assert.equal(results.length, 24, \"Count items in collection\");\n      coll.filter(\"done\", \"=\", true).list(function(results) {\n          assert.equal(results.length, 12, \"= filter test\");\n          coll.filter(\"done\", \"=\", false).list(function(results) {\n              assert.equal(results.length, 12, \"= filter test\");\n              coll.filter(\"done\", \"!=\", true).list(function(results) {\n                  assert.equal(results.length, 12, \"'!=' filter test\");\n                  coll.filter(\"done\", \"!=\", false).list(function(results) {\n                      assert.equal(results.length, 12, \"'!=' filter test\");\n                      callback();\n                    });\n                });\n            });\n        });\n    });\n}\n\nfunction dateFilterTests(session, coll, callback) {\n  var now = new Date();\n\n  function dateInDays(n) {\n    var newDate = new Date(now.getTime());\n    newDate.setDate(newDate.getDate()+n);\n    return newDate;\n  }\n\n  for(var i = 0; i < 24; i++) {\n    var t = new Task(session, {name: \"Task \" + i, dateAdded: dateInDays(i)});\n    coll.add(t);\n  }\n  coll.list(function(results) {\n      assert.equal(results.length, 24, \"Count items in collection\");\n      coll.filter(\"dateAdded\", \"=\", dateInDays(1)).list(function(results) {\n          assert.equal(results.length, 1, \"= filter test\");\n          coll.filter(\"dateAdded\", \"!=\", dateInDays(1)).list(function(results) {\n              assert.equal(results.length, 23, \"!= filter test\");\n              coll.filter(\"dateAdded\", \">\", dateInDays(12)).list(function(results) {\n                  assert.equal(results.length, 11, \"> filter test\");\n                  callback();\n                });\n            })\n        });\n    });\n}\n\nfunction intOrderTests(session, coll, callback) {\n  var tasks = [];\n  for(var i = 0; i < 24; i++) {\n    var t = new Task(session, {name: \"Task \" + i, counter: i});\n    tasks.push(t);\n    coll.add(t);\n  }\n  coll.order('counter', true).list(function(results) {\n      for(var i = 0; i < 24; i++) {\n        assert.equal(results[i].id, tasks[i].id, \"order check, ascending\");\n      }\n      tasks.reverse();\n      coll.order('counter', false).list(function(results) {\n          for(var i = 0; i < 24; i++) {\n            assert.equal(results[i].id, tasks[i].id, \"order check, descending\");\n          }\n          callback();\n        });\n    });\n}\n\nfunction dateOrderTests(session, coll, callback) {\n  var now = new Date();\n\n  function dateInDays(n) {\n    var newDate = new Date(now.getTime());\n    newDate.setDate(newDate.getDate()+n);\n    return newDate;\n  }\n\n  var tasks = [];\n  for(var i = 0; i < 24; i++) {\n    var t = new Task(session, {name: \"Task \" + i, dateAdded: dateInDays(i)});\n    tasks.push(t);\n    coll.add(t);\n  }\n  coll.order('dateAdded', true).list(function(results) {\n      for(var i = 0; i < 24; i++) {\n        assert.equal(results[i].id, tasks[i].id, \"order check, ascending\");\n      }\n      tasks.reverse();\n      coll.order('dateAdded', false).list(function(results) {\n          for(var i = 0; i < 24; i++) {\n            assert.equal(results[i].id, tasks[i].id, \"order check, descending\");\n          }\n          callback();\n        });\n    });\n}\n\nfunction collectionLimitTests(session, coll, callback) {\n  var tasks = [];\n  for(var i = 0; i < 24; i++) {\n    var t = new Task(session, {name: \"Task \" + i, counter: i});\n    tasks.push(t);\n    coll.add(t);\n  }\n  coll.order(\"counter\", true).limit(5).list(function(results) {\n      assert.equal(results.length, 5, \"Result length check\");\n      for(var i = 0; i < 5; i++) {\n        assert.equal(results[i].id, tasks[i].id, \"limit check\");\n      }\n      callback();\n    });\n}\n\nfunction collectionSkipTests(session, coll, callback) {\n  var tasks = [];\n  for(var i = 0; i < 24; i++) {\n    var t = new Task(session, {name: \"Task \" + i, counter: i});\n    tasks.push(t);\n    coll.add(t);\n  }\n  coll.order(\"counter\", true).skip(5).limit(5).list(function(results) {\n      assert.equal(results.length, 5, \"Result length check\");\n      for(var i = 5; i < 10; i++) {\n        assert.equal(results[i-5].id, tasks[i].id, \"skip check\");\n      }\n      callback();\n    });\n}\n\n\n\nvar tests = {\n  testBasic: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    for(var i = 0; i < 10; i++) {\n      var t = new Task(session, {name: \"user \" + i, done: i % 2 === 0, dateAdded: new Date()});\n      session.add(t);\n    }\n    Task.all(session).filter(\"done\", \"=\", true).order('dateAdded', false).list(function(results) {\n        assert.equal(results.length, 5, \"Correct number of completed tasks\");\n        session.close();\n        helper.tearDown();\n      });\n  },\n  testOneToMany: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    session.schemaSync(function() {\n        var proj = new Project(session);\n        proj.name = \"Main\";\n        for(var i = 0; i < 10; i++) {\n          var t = new Task(session, {name: \"user \" + i, done: i % 2 === 0, dateAdded: new Date()});\n          proj.tasks.add(t);\n        }\n        for(var i = 0; i < 10; i++) {\n          var t = new Task(session, {name: \"non-proj user \" + i, done: i % 2 === 0, dateAdded: new Date()});\n          session.add(t);\n        }\n        proj.tasks.filter(\"done\", \"=\", true).list(function(results) {\n            assert.equal(results.length, 5, \"Correct number of completed tasks\");\n            session.close();\n            helper.tearDown();\n          });\n      });\n  },\n  testFetch: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var proj = new Project(session);\n    proj.name = \"Main\";\n    for(var i = 0; i < 10; i++) {\n      var t = new Task(session, {name: \"user \" + i, done: i % 2 === 0, dateAdded: new Date()});\n      proj.tasks.add(t);\n    }\n    session.flush(function() {\n        session.clean();\n        Task.all(session).list(function(results) {\n            results.forEach(function(r) {\n                r.fetch('project', function(p) {\n                    assert.equal(p.name, \"Main\", \"Correct number of completed tasks\");\n                  });\n              });\n            session.close();\n            helper.tearDown();\n          });\n      });\n  },\n  testDatabaseIntFilter: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    for(var i = 0; i < 25; i++) {\n      var t = new Task(session, {name: \"Root task \" + i, done: false});\n      t.counter = i;\n      session.add(t);\n    }\n\n    var p = new Project(session, {name: \"My project\"});\n    session.add(p);\n    intFilterTests(session, p.tasks, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n  testDatabaseLocalIntFilter: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var coll = new persistence.LocalQueryCollection();\n    intFilterTests(session, coll, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n  testDatabaseTextFilter: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var alphabet = 'abcdefghijklmnopqrstufwxyz';\n    for(var i = 0; i <= 25; i++) {\n      var t = new Task(session, {name: alphabet[i]});\n      session.add(t);\n    }\n    var p = new Project(session, {name: \"My project\"});\n    session.add(p);\n    textFilterTests(session, p.tasks, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n  testDatabaseLocalTextFilter: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var coll = new persistence.LocalQueryCollection();\n    textFilterTests(session, coll, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n  testDatabaseBoolFilter: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    for(var i = 0; i < 25; i++) {\n      var t = new Task(session, {name: \"Root task \" + i, done: false});\n      t.counter = i;\n      session.add(t);\n    }\n\n    var p = new Project(session, {name: \"My project\"});\n    session.add(p);\n\n    boolFilterTests(session, p.tasks, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n\n  testDatabaseDateFilter: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var p = new Project(session, {name: \"My project\"});\n    session.add(p);\n    dateFilterTests(session, p.tasks, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n\n  testDatabaseIntOrder: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var p = new Project(session, {name: \"My project\"});\n    session.add(p);\n    intOrderTests(session, p.tasks, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n\n  testDatabaseDateOrder: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var p = new Project(session, {name: \"My project\"});\n    session.add(p);\n    dateOrderTests(session, p.tasks, function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n\n  testCollectionLimit: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    collectionLimitTests(session, Task.all(session), function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n\n  testCollectionSkip: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    collectionSkipTests(session, Task.all(session), function() {\n        session.close();\n        helper.tearDown();\n      });\n  },\n\n  testJSON: function() {\n    helper.setUp();\n    var session = persistenceStore.getSession();\n    var p = new Project(session, {name: 'A project'});\n    for(var i = 0; i < 10; i++) {\n      p.tasks.add(new Task(session, {name: \"Some task \" + i}));\n    }\n    p.selectJSON(['id', 'name', 'tasks.[id,name]'], function(json) {\n        assert.equal(json.id, p.id, \"id\");\n        assert.equal(json.name, p.name, \"name\");\n        assert.equal(json.tasks.length, 10, \"n tasks\");\n        session.close();\n        helper.tearDown();\n      });\n  }\n};\n\nrequire('test').run(tests);\njava.lang.System.exit(0);\n"
  },
  {
    "path": "test/browser/qunit/jquery.js",
    "content": "/*!\n * jQuery JavaScript Library v1.4.2\n * http://jquery.com/\n *\n * Copyright 2010, John Resig\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n * Copyright 2010, The Dojo Foundation\n * Released under the MIT, BSD, and GPL Licenses.\n *\n * Date: Sat Feb 13 22:33:48 2010 -0500\n */\n(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll(\"left\")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:\"script\"}):c.globalEval(b.text||b.textContent||b.innerHTML||\"\");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b===\"object\"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?\ne(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,\"events\");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type===\"click\")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,\"\")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=\nj.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType===\"mouseenter\"||i.preType===\"mouseleave\")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return\"live.\"+(a&&a!==\"*\"?a+\".\":\"\")+b.replace(/\\./g,\"`\").replace(/ /g,\n\"&\")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]===\"string\"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=\ntrue;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return\"scrollTo\"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\\w\\W]+>)[^>]*$|^#([\\w-]+)$/,Ua=/^.[^:#\\[\\.,]*$/,Va=/\\S/,\nWa=/^(\\s|\\u00A0)+|(\\s|\\u00A0)+$/g,Xa=/^<(\\w+)\\s*\\/?>(?:<\\/\\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a===\"body\"&&!b){this.context=s;this[0]=s.body;this.selector=\"body\";this.length=1;return this}if(typeof a===\"string\")if((d=Ta.exec(a))&&\n(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,\na)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:\"\",jquery:\"1.4.2\",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===\n\"find\")f.selector=this.selector+(this.selector?\" \":\"\")+d;else if(b)f.selector=this.selector+\".\"+b+\"(\"+d+\")\";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),\"slice\",R.call(arguments).join(\",\"))},map:function(a){return this.pushStack(c.map(this,\nfunction(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a===\"boolean\"){f=a;a=arguments[1]||{};b=2}if(typeof a!==\"object\"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||\nc.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler(\"ready\")}},bindReady:function(){if(!xa){xa=true;if(s.readyState===\"complete\")return c.ready();if(s.addEventListener){s.addEventListener(\"DOMContentLoaded\",\nL,false);A.addEventListener(\"load\",c.ready,false)}else if(s.attachEvent){s.attachEvent(\"onreadystatechange\",L);A.attachEvent(\"onload\",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)===\"[object Function]\"},isArray:function(a){return $.call(a)===\"[object Array]\"},isPlainObject:function(a){if(!a||$.call(a)!==\"[object Object]\"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,\"constructor\")&&!aa.call(a.constructor.prototype,\n\"isPrototypeOf\"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!==\"string\"||!a)return null;a=c.trim(a);if(/^[\\],:{}\\s]*$/.test(a.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g,\"@\").replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,\"]\").replace(/(?:^|:|,)(?:\\s*\\[)+/g,\"\")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function(\"return \"+\na))();else c.error(\"Invalid JSON: \"+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName(\"head\")[0]||s.documentElement,d=s.createElement(\"script\");d.type=\"text/javascript\";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],\nd)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||\"\").replace(Wa,\"\")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a===\"string\"||c.isFunction(a)||typeof a!==\"function\"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===\na)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length===\"number\")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b===\"string\"){d=a;a=d[b];b=w}else if(b&&\n!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \\/]([\\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \\/]([\\w.]+)/.exec(a)||/(msie) ([\\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\\w.]+))?/.exec(a)||[];return{browser:a[1]||\"\",version:a[2]||\"0\"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=\ntrue;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener(\"DOMContentLoaded\",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState===\"complete\"){s.detachEvent(\"onreadystatechange\",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement(\"script\"),d=s.createElement(\"div\"),f=\"script\"+J();d.style.display=\"none\";d.innerHTML=\"   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>\";\nvar e=d.getElementsByTagName(\"*\"),j=d.getElementsByTagName(\"a\")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName(\"tbody\").length,htmlSerialize:!!d.getElementsByTagName(\"link\").length,style:/red/.test(j.getAttribute(\"style\")),hrefNormalized:j.getAttribute(\"href\")===\"/a\",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName(\"input\")[0].value===\"on\",optSelected:s.createElement(\"select\").appendChild(s.createElement(\"option\")).selected,\nparentNode:d.removeChild(d.appendChild(s.createElement(\"div\"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type=\"text/javascript\";try{b.appendChild(s.createTextNode(\"window.\"+f+\"=1;\"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent(\"onclick\",function k(){c.support.noCloneEvent=\nfalse;d.detachEvent(\"onclick\",k)});d.cloneNode(true).fireEvent(\"onclick\")}d=s.createElement(\"div\");d.innerHTML=\"<input type='radio' name='radiotest' checked='checked'/>\";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement(\"div\");k.style.width=k.style.paddingLeft=\"1px\";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display=\"none\"});a=function(k){var n=\ns.createElement(\"div\");k=\"on\"+k;var r=k in n;if(!r){n.setAttribute(k,\"return;\");r=typeof n[k]===\"function\"}return r};c.support.submitBubbles=a(\"submit\");c.support.changeBubbles=a(\"change\");a=b=d=e=j=null}})();c.props={\"for\":\"htmlFor\",\"class\":\"className\",readonly:\"readOnly\",maxlength:\"maxLength\",cellspacing:\"cellSpacing\",rowspan:\"rowSpan\",colspan:\"colSpan\",tabindex:\"tabIndex\",usemap:\"useMap\",frameborder:\"frameBorder\"};var G=\"jQuery\"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,\napplet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b===\"string\"&&d===w)return null;f||(f=++Ya);if(typeof b===\"object\"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b===\"string\"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];\nelse a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a===\"undefined\"&&this.length)return c.data(this[0]);else if(typeof a===\"object\")return this.each(function(){c.data(this,a)});var d=a.split(\".\");d[1]=d[1]?\".\"+d[1]:\"\";if(b===w){var f=this.triggerHandler(\"getData\"+d[1]+\"!\",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger(\"setData\"+d[1]+\"!\",[d[0],b]).each(function(){c.data(this,\na,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||\"fx\")+\"queue\";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||\"fx\";var d=c.queue(a,b),f=d.shift();if(f===\"inprogress\")f=d.shift();if(f){b===\"fx\"&&d.unshift(\"inprogress\");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!==\"string\"){b=a;a=\"fx\"}if(b===\nw)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a===\"fx\"&&d[0]!==\"inprogress\"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||\"fx\";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||\"fx\",[])}});var Aa=/[\\n\\t]/g,ca=/\\s+/,Za=/\\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,\ncb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,\"\");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr(\"class\")))});if(a&&typeof a===\"string\")for(var b=(a||\"\").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=\" \"+e.className+\" \",\ni=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(\" \"+b[o]+\" \")<0)i+=\" \"+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr(\"class\")))});if(a&&typeof a===\"string\"||a===w)for(var b=(a||\"\").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(\" \"+e.className+\" \").replace(Aa,\" \"),i=0,o=b.length;i<o;i++)j=j.replace(\" \"+b[i]+\" \",\n\" \");e.className=c.trim(j)}else e.className=\"\"}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b===\"boolean\";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr(\"class\"),b),b)});return this.each(function(){if(d===\"string\")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?\"addClass\":\"removeClass\"](e)}else if(d===\"undefined\"||d===\"boolean\"){this.className&&c.data(this,\"__className__\",this.className);this.className=\nthis.className||a===false?\"\":c.data(this,\"__className__\")||\"\"}})},hasClass:function(a){a=\" \"+a+\" \";for(var b=0,d=this.length;b<d;b++)if((\" \"+this[b].className+\" \").replace(Aa,\" \").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,\"option\"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,\"select\")){var d=b.selectedIndex,f=[],e=b.options;b=b.type===\"select-one\";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=\ne[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute(\"value\")===null?\"on\":b.value;return(b.value||\"\").replace(Za,\"\")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r===\"number\")r+=\"\";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,\"select\")){var u=c.makeArray(r);c(\"option\",this).each(function(){this.selected=\nc.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b===\"type\"&&ab.test(a.nodeName)&&a.parentNode&&c.error(\"type property can't be changed\");\na[b]=d}if(c.nodeName(a,\"form\")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b===\"tabIndex\")return(b=a.getAttributeNode(\"tabIndex\"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b===\"style\"){if(e)a.style.cssText=\"\"+d;return a.style.cssText}e&&a.setAttribute(b,\"\"+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\\.(.*)$/,db=function(a){return a.replace(/[^\\w\\s\\.\\|`]/g,\nfunction(b){return\"\\\\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!==\"undefined\"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(\" \");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(\".\")>-1){r=k.split(\".\");\nk=r.shift();j.namespace=r.slice(0).sort().join(\".\")}else{r=[];j.namespace=\"\"}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent(\"on\"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),\nC=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b===\"string\"&&b.charAt(0)===\".\"){b=b||\"\";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(\" \");e=b[j++];){n=e;i=e.indexOf(\".\")<0;o=[];if(!i){o=e.split(\".\");e=o.shift();k=new RegExp(\"(^|\\\\.)\"+c.map(o.slice(0).sort(),db).join(\"\\\\.(?:.*\\\\.)?\")+\"(\\\\.|$)\")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=\nnull)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a===\"object\"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf(\"!\")>=0){a.type=\ne=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,\"handle\"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d[\"on\"+e]&&d[\"on\"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&\nf)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,\"a\")&&e===\"click\",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f[\"on\"+e])f[\"on\"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f[\"on\"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(\".\")<0&&!a.exclusive;\nif(!b){d=a.type.split(\".\");a.type=d.shift();f=new RegExp(\"(^|\\\\.)\"+d.slice(0).sort().join(\"\\\\.(?:.*\\\\.)?\")+\"(\\\\.|$)\")}e=c.data(this,\"events\");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:\"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which\".split(\" \"),\nfix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||\nd&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,\"\");c.each(c.data(this,\n\"events\").live||[],function(){if(d===this.origType.replace(O,\"\"))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent(\"on\"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=\na;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,\nisImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=\n{setup:function(){if(this.nodeName.toLowerCase()!==\"form\"){c.event.add(this,\"click.specialSubmit\",function(a){var b=a.target,d=b.type;if((d===\"submit\"||d===\"image\")&&c(b).closest(\"form\").length)return na(\"submit\",this,arguments)});c.event.add(this,\"keypress.specialSubmit\",function(a){var b=a.target,d=b.type;if((d===\"text\"||d===\"password\")&&c(b).closest(\"form\").length&&a.keyCode===13)return na(\"submit\",this,arguments)})}else return false},teardown:function(){c.event.remove(this,\".specialSubmit\")}};\nif(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b===\"radio\"||b===\"checkbox\")d=a.checked;else if(b===\"select-multiple\")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join(\"-\"):\"\";else if(a.nodeName.toLowerCase()===\"select\")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,\"_change_data\");e=Fa(d);if(a.type!==\"focusout\"||d.type!==\"radio\")c.data(d,\"_change_data\",\ne);if(!(f===w||e===f))if(f!=null||e){a.type=\"change\";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d===\"radio\"||d===\"checkbox\"||b.nodeName.toLowerCase()===\"select\")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!==\"textarea\"||a.keyCode===32&&(d===\"checkbox\"||d===\"radio\")||d===\"select-multiple\")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,\n\"_change_data\",Fa(a))}},setup:function(){if(this.type===\"file\")return false;for(var a in ea)c.event.add(this,a+\".specialChange\",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,\".specialChange\");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:\"focusin\",blur:\"focusout\"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,\nd,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each([\"bind\",\"one\"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d===\"object\"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b===\"one\"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d===\"unload\"&&b!==\"one\")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a===\"object\"&&\n!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind(\"live\"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},\ntoggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,\"lastToggle\"+a.guid)||0)%d;c.data(this,\"lastToggle\"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:\"focusin\",blur:\"focusout\",mouseenter:\"mouseover\",mouseleave:\"mouseout\"};c.each([\"live\",\"die\"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,\nu=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||\"\").split(\" \");(i=d[o++])!=null;){j=O.exec(i);k=\"\";if(j){k=j[0];i=i.replace(O,\"\")}if(i===\"hover\")d.push(\"mouseenter\"+k,\"mouseleave\"+k);else{n=i;if(i===\"focus\"||i===\"blur\"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b===\"live\"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each(\"blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error\".split(\" \"),\nfunction(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent(\"onunload\",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h=\"\",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];\nif(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!==\"string\"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\\((?:\\([^()]+\\)|[^()]+)+\\)|\\[(?:\\[[^[\\]]*\\]|['\"][^'\"]*['\"]|[^[\\]'\"]+)+\\]|\\\\.|[^ >+~,(\\[\\\\]+)+|[>+~])(\\s*,\\s*)?((?:.|\\r|\\n)*)/g,\ne=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!==\"string\")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(\"\"),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();\nt=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]===\"~\"||p[0]===\"+\")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D=\"\";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||\ng);if(j.call(y)===\"[object Array]\")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];\nfor(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!==\"\\\\\"){q[1]=(q[1]||\"\").replace(/\\\\/g,\"\");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],\"\");break}}}}m||(m=h.getElementsByTagName(\"*\"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-\n1)!==\"\\\\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],\"\");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw\"Syntax error, unrecognized expression: \"+g;};var n=k.selectors={order:[\"ID\",\"NAME\",\"TAG\"],match:{ID:/#((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)/,\nCLASS:/\\.((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)/,NAME:/\\[name=['\"]*((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)['\"]*\\]/,ATTR:/\\[\\s*((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)\\s*(?:(\\S?=)\\s*(['\"]*)(.*?)\\3|)\\s*\\]/,TAG:/^((?:[\\w\\u00c0-\\uFFFF\\*-]|\\\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\\((even|odd|[\\dn+-]*)\\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^-]|$)/,PSEUDO:/:((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)(?:\\((['\"]?)((?:\\([^\\)]+\\)|[^\\(\\)]*)+)\\2\\))?/},leftMatch:{},attrMap:{\"class\":\"className\",\"for\":\"htmlFor\"},attrHandle:{href:function(g){return g.getAttribute(\"href\")}},\nrelative:{\"+\":function(g,h){var l=typeof h===\"string\",m=l&&!/\\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},\">\":function(g,h){var l=typeof h===\"string\";if(l&&!/\\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=\nl?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},\"\":function(g,h,l){var m=e++,q=d;if(typeof h===\"string\"&&!/\\W/.test(h)){var p=h=h.toLowerCase();q=b}q(\"parentNode\",h,m,g,p,l)},\"~\":function(g,h,l){var m=e++,q=d;if(typeof h===\"string\"&&!/\\W/.test(h)){var p=h=h.toLowerCase();q=b}q(\"previousSibling\",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!==\"undefined\"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!==\"undefined\"){var l=[];\nh=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute(\"name\")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=\" \"+g[1].replace(/\\\\/g,\"\")+\" \";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(\" \"+v.className+\" \").replace(/[\\t\\n]/g,\" \").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\\\/g,\"\")},TAG:function(g){return g[1].toLowerCase()},\nCHILD:function(g){if(g[1]===\"nth\"){var h=/(-?)(\\d*)n((?:\\+|-)?\\d*)/.exec(g[2]===\"even\"&&\"2n\"||g[2]===\"odd\"&&\"2n+1\"||!/\\D/.test(g[2])&&\"0n+\"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\\\/g,\"\");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]===\"~=\")g[4]=\" \"+g[4]+\" \";return g},PSEUDO:function(g,h,l,m,q){if(g[1]===\"not\")if((f.exec(g[3])||\"\").length>1||/^\\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,\ng);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!==\"hidden\"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\\d/i.test(g.nodeName)},\ntext:function(g){return\"text\"===g.type},radio:function(g){return\"radio\"===g.type},checkbox:function(g){return\"checkbox\"===g.type},file:function(g){return\"file\"===g.type},password:function(g){return\"password\"===g.type},submit:function(g){return\"submit\"===g.type},image:function(g){return\"image\"===g.type},reset:function(g){return\"reset\"===g.type},button:function(g){return\"button\"===g.type||g.nodeName.toLowerCase()===\"button\"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},\nsetFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q===\"contains\")return(g.textContent||g.innerText||a([g])||\"\").indexOf(h[3])>=0;else if(q===\"not\"){h=\nh[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error(\"Syntax error, unrecognized expression: \"+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case \"only\":case \"first\":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l===\"first\")return true;m=g;case \"last\":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case \"nth\":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=\nm.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute(\"id\")===h},TAG:function(g,h){return h===\"*\"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(\" \"+(g.className||g.getAttribute(\"class\"))+\" \").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+\"\";var m=h[2];h=h[4];return g==null?m===\"!=\":m===\n\"=\"?l===h:m===\"*=\"?l.indexOf(h)>=0:m===\"~=\"?(\" \"+l+\" \").indexOf(h)>=0:!h?l&&g!==false:m===\"!=\"?l!==h:m===\"^=\"?l.indexOf(h)===0:m===\"$=\"?l.substr(l.length-h.length)===h:m===\"|=\"?l===h||l.substr(0,h.length+1)===h+\"-\":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\\[]*\\])(?![^\\(]*\\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\\r|\\n)*?)/.source+n.match[u].source.replace(/\\\\(\\d+)/g,function(g,\nh){return\"\\\\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)===\"[object Array]\")Array.prototype.push.apply(h,g);else if(typeof g.length===\"number\")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||\n!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if(\"sourceIndex\"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=\nh.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement(\"div\"),h=\"script\"+(new Date).getTime();g.innerHTML=\"<a name='\"+h+\"'/>\";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!==\"undefined\"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!==\"undefined\"&&\nq.getAttributeNode(\"id\").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!==\"undefined\"&&m.getAttributeNode(\"id\");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement(\"div\");g.appendChild(s.createComment(\"\"));if(g.getElementsByTagName(\"*\").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]===\"*\"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=\"<a href='#'></a>\";\nif(g.firstChild&&typeof g.firstChild.getAttribute!==\"undefined\"&&g.firstChild.getAttribute(\"href\")!==\"#\")n.attrHandle.href=function(h){return h.getAttribute(\"href\",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement(\"div\");h.innerHTML=\"<p class='TEST'></p>\";if(!(h.querySelectorAll&&h.querySelectorAll(\".TEST\").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();\n(function(){var g=s.createElement(\"div\");g.innerHTML=\"<div class='test e'></div><div class='test'></div>\";if(!(!g.getElementsByClassName||g.getElementsByClassName(\"e\").length===0)){g.lastChild.className=\"e\";if(g.getElementsByClassName(\"e\").length!==1){n.order.splice(1,0,\"CLASS\");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!==\"undefined\"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:\nfunction(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!==\"HTML\":false},ga=function(g,h){var l=[],m=\"\",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,\"\")}g=n.relative[g]?g+\"*\":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[\":\"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,\ngb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b===\"string\"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack(\"\",\"find\",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;\nc.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),\"not\",a)},filter:function(a){return this.pushStack(Ia(this,a,true),\"filter\",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=\n{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===\n\"string\")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a===\"string\"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,\"parentNode\")},parentsUntil:function(a,b,d){return c.dir(a,\"parentNode\",\nd)},next:function(a){return c.nth(a,2,\"nextSibling\")},prev:function(a){return c.nth(a,2,\"previousSibling\")},nextAll:function(a){return c.dir(a,\"nextSibling\")},prevAll:function(a){return c.dir(a,\"previousSibling\")},nextUntil:function(a,b,d){return c.dir(a,\"nextSibling\",d)},prevUntil:function(a,b,d){return c.dir(a,\"previousSibling\",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,\"iframe\")?\na.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f===\"string\")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(\",\"))}});c.extend({filter:function(a,b,d){if(d)a=\":not(\"+a+\")\";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===\n1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\\d+=\"(?:\\d+|null)\"/g,V=/^\\s+/,Ka=/(<([\\w:]+)[^>]*?)\\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\\w:]+)/,ib=/<tbody/i,jb=/<|&#?\\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\\s*(?:[^=]|=\\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?\na:b+\"></\"+d+\">\"},F={option:[1,\"<select multiple='multiple'>\",\"</select>\"],legend:[1,\"<fieldset>\",\"</fieldset>\"],thead:[1,\"<table>\",\"</table>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],td:[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],col:[2,\"<table><tbody></tbody><colgroup>\",\"</colgroup></table>\"],area:[1,\"<map>\",\"</map>\"],_default:[0,\"\",\"\"]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,\"div<div>\",\"</div>\"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=\nc(this);d.text(a.call(this,b,d.text()))});if(typeof a!==\"object\"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},\nwrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,\"body\")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},\nprepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,\"before\",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,\nthis.nextSibling)});else if(arguments.length){var a=this.pushStack(this,\"after\",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName(\"*\"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName(\"*\"));b.firstChild;)b.removeChild(b.firstChild);\nreturn this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement(\"div\");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,\"\").replace(/=([^=\"'>\\s]+\\/)>/g,'=\"$1\">').replace(V,\"\")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find(\"*\"),b.find(\"*\"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,\n\"\"):null;else if(typeof a===\"string\"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||[\"\",\"\"])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName(\"*\"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&\nthis[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!==\"string\")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),\"replaceWith\",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,\"table\")?u.getElementsByTagName(\"tbody\")[0]||\nu.appendChild(u.ownerDocument.createElement(\"tbody\")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i===\"string\"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===\n1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,\"tr\");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);\nreturn this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement===\"undefined\")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i===\"number\")i+=\"\";if(i){if(typeof i===\"string\"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i===\"string\"){i=i.replace(Ka,Ma);var o=(La.exec(i)||[\"\",\n\"\"])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement(\"div\");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o===\"table\"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===\"<table>\"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],\"tbody\")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=\nc.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],\"script\")&&(!e[j].type||e[j].type.toLowerCase()===\"text/javascript\"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName(\"script\"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?\nc.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\\([^)]*\\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\\d+(?:px)?$/i,nb=/^-?\\d/,ob={position:\"absolute\",visibility:\"hidden\",display:\"block\"},pb=[\"Left\",\"Right\"],qb=[\"Top\",\"Bottom\"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?\"cssFloat\":\"styleFloat\",ja=\nfunction(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e===\"number\"&&!kb.test(f))e+=\"px\";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b===\"width\"||b===\"height\")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b===\"opacity\"){if(e){f.zoom=1;b=parseInt(d,10)+\"\"===\"NaN\"?\"\":\"alpha(opacity=\"+d*100+\")\";a=f.filter||c.curCSS(a,\"filter\")||\"\";f.filter=\nNa.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf(\"opacity=\")>=0?parseFloat(Oa.exec(f.filter)[1])/100+\"\":\"\"}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b===\"width\"||b===\"height\"){var e,j=b===\"width\"?pb:qb;function i(){e=b===\"width\"?a.offsetWidth:a.offsetHeight;f!==\"border\"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,\"padding\"+this,true))||0);if(f===\"margin\")e+=parseFloat(c.curCSS(a,\"margin\"+this,true))||0;else e-=parseFloat(c.curCSS(a,\n\"border\"+this+\"Width\",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b===\"opacity\"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||\"\")?parseFloat(RegExp.$1)/100+\"\":\"\";return f===\"\"?\"1\":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b=\"float\";b=b.replace(lb,\"-$1\").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=\na.getPropertyValue(b);if(b===\"opacity\"&&f===\"\")f=\"1\"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d===\"fontSize\"?\"1em\":f||0;f=e.pixelLeft+\"px\";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=\na.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()===\"tr\";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,\"display\")===\"none\"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\\s)*?\\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\\?(&|$)/,ka=/\\?/,wb=/(\\?|&)_=.*?(&|$)/,xb=/^(\\w+:)?\\/\\/([^\\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==\n\"string\")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(\" \");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f=\"GET\";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===\"object\"){b=c.param(b,c.ajaxSettings.traditional);f=\"POST\"}var j=this;c.ajax({url:a,type:f,dataType:\"html\",data:b,complete:function(i,o){if(o===\"success\"||o===\"notmodified\")j.html(e?c(\"<div />\").append(i.responseText.replace(tb,\"\")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},\nserialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each(\"ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend\".split(\" \"),\nfunction(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:\"GET\",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,\"script\")},getJSON:function(a,b,d){return c.get(a,b,d,\"json\")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:\"POST\",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,\nglobal:true,type:\"GET\",contentType:\"application/x-www-form-urlencoded\",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!==\"file:\"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject(\"Microsoft.XMLHTTP\")}catch(a){}},accepts:{xml:\"application/xml, text/xml\",html:\"text/html\",script:\"text/javascript, application/javascript\",json:\"application/json, text/javascript\",text:\"text/plain\",_default:\"*/*\"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&\ne.success.call(k,o,i,x);e.global&&f(\"ajaxSuccess\",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f(\"ajaxComplete\",[x,e]);e.global&&!--c.active&&c.event.trigger(\"ajaxStop\")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!==\"string\")e.data=c.param(e.data,e.traditional);if(e.dataType===\"jsonp\"){if(n===\"GET\")N.test(e.url)||(e.url+=(ka.test(e.url)?\n\"&\":\"?\")+(e.jsonp||\"callback\")+\"=?\");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+\"&\":\"\")+(e.jsonp||\"callback\")+\"=?\";e.dataType=\"json\"}if(e.dataType===\"json\"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||\"jsonp\"+sb++;if(e.data)e.data=(e.data+\"\").replace(N,\"=\"+j+\"$1\");e.url=e.url.replace(N,\"=\"+j+\"$1\");e.dataType=\"script\";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType===\"script\"&&e.cache===null)e.cache=false;if(e.cache===\nfalse&&n===\"GET\"){var r=J(),u=e.url.replace(wb,\"$1_=\"+r+\"$2\");e.url=u+(u===e.url?(ka.test(e.url)?\"&\":\"?\")+\"_=\"+r:\"\")}if(e.data&&n===\"GET\")e.url+=(ka.test(e.url)?\"&\":\"?\")+e.data;e.global&&!c.active++&&c.event.trigger(\"ajaxStart\");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType===\"script\"&&n===\"GET\"&&r){var z=s.getElementsByTagName(\"head\")[0]||s.documentElement,C=s.createElement(\"script\");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=\nfalse;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState===\"loaded\"||this.readyState===\"complete\")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader(\"Content-Type\",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader(\"If-Modified-Since\",\nc.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader(\"If-None-Match\",c.etag[e.url])}r||x.setRequestHeader(\"X-Requested-With\",\"XMLHttpRequest\");x.setRequestHeader(\"Accept\",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+\", */*\":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger(\"ajaxStop\");x.abort();return false}e.global&&f(\"ajaxSend\",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q===\"abort\"){E||\nd();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q===\"timeout\")){E=true;x.onreadystatechange=c.noop;i=q===\"timeout\"?\"timeout\":!c.httpSuccess(x)?\"error\":e.ifModified&&c.httpNotModified(x,e.url)?\"notmodified\":\"success\";var p;if(i===\"success\")try{o=c.httpData(x,e.dataType,e)}catch(v){i=\"parsererror\";p=v}if(i===\"success\"||i===\"notmodified\")j||b();else c.handleError(e,x,i,p);d();q===\"timeout\"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);\ng(\"abort\")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g(\"timeout\")},e.timeout);try{x.send(n===\"POST\"||n===\"PUT\"||n===\"DELETE\"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger(\"ajaxError\",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol===\"file:\"||a.status>=200&&a.status<300||a.status===304||a.status===\n1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader(\"Last-Modified\"),f=a.getResponseHeader(\"Etag\");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader(\"content-type\")||\"\",e=b===\"xml\"||!b&&f.indexOf(\"xml\")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName===\"parsererror\"&&c.error(\"parsererror\");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a===\"string\")if(b===\n\"json\"||!b&&f.indexOf(\"json\")>=0)a=c.parseJSON(a);else if(b===\"script\"||!b&&f.indexOf(\"javascript\")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\\[\\]$/.test(i)?f(i,n):d(i+\"[\"+(typeof n===\"object\"||c.isArray(n)?k:\"\")+\"]\",n)});else!b&&o!=null&&typeof o===\"object\"?c.each(o,function(k,n){d(i+\"[\"+k+\"]\",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+\"=\"+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;\nif(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join(\"&\").replace(yb,\"+\")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\\d+-.]+)(.*)$/,W,va=[[\"height\",\"marginTop\",\"marginBottom\",\"paddingTop\",\"paddingBottom\"],[\"width\",\"marginLeft\",\"marginRight\",\"paddingLeft\",\"paddingRight\"],[\"opacity\"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K(\"show\",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],\"olddisplay\");\nthis[a].style.display=d||\"\";if(c.css(this[a],\"display\")===\"none\"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c(\"<\"+d+\" />\").appendTo(\"body\");f=e.css(\"display\");if(f===\"none\")f=\"block\";e.remove();la[d]=f}c.data(this[a],\"olddisplay\",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],\"olddisplay\")||\"\";return this}},hide:function(a,b){if(a||a===0)return this.animate(K(\"hide\",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],\"olddisplay\");!d&&d!==\"none\"&&c.data(this[a],\n\"olddisplay\",c.css(this[a],\"display\"))}a=0;for(b=this.length;a<b;a++)this[a].style.display=\"none\";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a===\"boolean\";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(\":hidden\");c(this)[f?\"show\":\"hide\"]()}):this.animate(K(\"toggle\",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(\":hidden\").css(\"opacity\",0).show().end().animate({opacity:b},a,d)},\nanimate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?\"each\":\"queue\"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(\":hidden\"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]===\"hide\"&&o||a[i]===\"show\"&&!o)return j.complete.call(this);if((i===\"height\"||i===\"width\")&&this.style){j.display=c.css(this,\"display\");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=\nj.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow=\"hidden\";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u===\"toggle\"?o?\"show\":\"hide\":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||\"px\";if(E!==\"px\"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]===\"-=\"?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,\"\")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);\nthis.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K(\"show\",1),slideUp:K(\"hide\",1),slideToggle:K(\"toggle\",1),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a===\"object\"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===\n\"number\"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||\nc.fx.step._default)(this);if((this.prop===\"height\"||this.prop===\"width\")&&this.elem.style)this.elem.style.display=\"block\"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||\"px\";this.now=this.start;\nthis.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop===\"width\"||this.prop===\"height\"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=\nthis.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,\"olddisplay\");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,\"display\")===\"none\")this.elem.style.display=\"block\"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,\ne,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?\"swing\":\"linear\");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||\nc.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,\"opacity\",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop===\"width\"||a.prop===\"height\"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset=\"getBoundingClientRect\"in s.documentElement?\nfunction(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=\nthis[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position===\"fixed\")break;j=e?e.getComputedStyle(b,null):b.currentStyle;\nk-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!==\"visible\"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position===\"relative\"||f.position===\"static\"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&\nf.position===\"fixed\"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement(\"div\"),d,f,e,j=parseFloat(c.curCSS(a,\"marginTop\",true))||0;c.extend(b.style,{position:\"absolute\",top:0,left:0,margin:0,border:0,width:\"1px\",height:\"1px\",visibility:\"hidden\"});b.innerHTML=\"<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>\";\na.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position=\"fixed\";f.style.top=\"20px\";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top=\"\";d.style.overflow=\"hidden\";d.style.position=\"relative\";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);\nc.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,\"marginTop\",true))||0;d+=parseFloat(c.curCSS(a,\"marginLeft\",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,\"position\")))a.style.position=\"relative\";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,\"top\",true),10)||0,i=parseInt(c.curCSS(a,\"left\",true),10)||0;if(c.isFunction(b))b=b.call(a,\nd,e);d={top:b.top-e.top+j,left:b.left-e.left+i};\"using\"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,\"marginTop\",true))||0;d.left-=parseFloat(c.curCSS(a,\"marginLeft\",true))||0;f.top+=parseFloat(c.curCSS(b[0],\"borderTopWidth\",true))||0;f.left+=parseFloat(c.curCSS(b[0],\"borderLeftWidth\",true))||0;return{top:d.top-\nf.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,\"position\")===\"static\";)a=a.offsetParent;return a})}});c.each([\"Left\",\"Top\"],function(a,b){var d=\"scroll\"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?\"pageXOffset\"in j?j[a?\"pageYOffset\":\n\"pageXOffset\"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each([\"Height\",\"Width\"],function(a,b){var d=b.toLowerCase();c.fn[\"inner\"+b]=function(){return this[0]?c.css(this[0],d,false,\"padding\"):null};c.fn[\"outer\"+b]=function(f){return this[0]?c.css(this[0],d,false,f?\"margin\":\"border\"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return\"scrollTo\"in\ne&&e.document?e.document.compatMode===\"CSS1Compat\"&&e.document.documentElement[\"client\"+b]||e.document.body[\"client\"+b]:e.nodeType===9?Math.max(e.documentElement[\"client\"+b],e.body[\"scroll\"+b],e.documentElement[\"scroll\"+b],e.body[\"offset\"+b],e.documentElement[\"offset\"+b]):f===w?c.css(e,d):this.css(d,typeof f===\"string\"?f:f+\"px\")}});A.jQuery=A.$=c})(window);\n"
  },
  {
    "path": "test/browser/qunit/qunit.css",
    "content": "\nol#qunit-tests {\n\tfont-family:\"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial;\n\tmargin:0;\n\tpadding:0;\n\tlist-style-position:inside;\n\n\tfont-size: smaller;\n}\nol#qunit-tests li{\n\tpadding:0.4em 0.5em 0.4em 2.5em;\n\tborder-bottom:1px solid #fff;\n\tfont-size:small;\n\tlist-style-position:inside;\n}\nol#qunit-tests li ol{\n\tbox-shadow: inset 0px 2px 13px #999;\n\t-moz-box-shadow: inset 0px 2px 13px #999;\n\t-webkit-box-shadow: inset 0px 2px 13px #999;\n\tmargin-top:0.5em;\n\tmargin-left:0;\n\tpadding:0.5em;\n\tbackground-color:#fff;\n\tborder-radius:15px;\n\t-moz-border-radius: 15px;\n\t-webkit-border-radius: 15px;\n}\nol#qunit-tests li li{\n\tborder-bottom:none;\n\tmargin:0.5em;\n\tbackground-color:#fff;\n\tlist-style-position: inside;\n\tpadding:0.4em 0.5em 0.4em 0.5em;\n}\n\nol#qunit-tests li li.pass{\n\tborder-left:26px solid #C6E746;\n\tbackground-color:#fff;\n\tcolor:#5E740B;\n\t}\nol#qunit-tests li li.fail{\n\tborder-left:26px solid #EE5757;\n\tbackground-color:#fff;\n\tcolor:#710909;\n}\nol#qunit-tests li.pass{\n\tbackground-color:#D2E0E6;\n\tcolor:#528CE0;\n}\nol#qunit-tests li.fail{\n\tbackground-color:#EE5757;\n\tcolor:#000;\n}\nol#qunit-tests li strong {\n\tcursor:pointer;\n}\nh1#qunit-header{\n\tbackground-color:#0d3349;\n\tmargin:0;\n\tpadding:0.5em 0 0.5em 1em;\n\tcolor:#fff;\n\tfont-family:\"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial;\n\tborder-top-right-radius:15px;\n\tborder-top-left-radius:15px;\n\t-moz-border-radius-topright:15px;\n\t-moz-border-radius-topleft:15px;\n\t-webkit-border-top-right-radius:15px;\n\t-webkit-border-top-left-radius:15px;\n\ttext-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;\n}\nh2#qunit-banner{\n\tfont-family:\"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial;\n\theight:5px;\n\tmargin:0;\n\tpadding:0;\n}\nh2#qunit-banner.qunit-pass{\n\tbackground-color:#C6E746;\n}\nh2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {\n\tbackground-color:#EE5757;\n}\n#qunit-testrunner-toolbar {\n\tfont-family:\"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial;\n\tpadding:0;\n\t/*width:80%;*/\n\tpadding:0em 0 0.5em 2em;\n\tfont-size: small;\n}\nh2#qunit-userAgent {\n\tfont-family:\"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial;\n\tbackground-color:#2b81af;\n\tmargin:0;\n\tpadding:0;\n\tcolor:#fff;\n\tfont-size: small;\n\tpadding:0.5em 0 0.5em 2.5em;\n\ttext-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;\n}\np#qunit-testresult{\n\tfont-family:\"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial;\n\tmargin:0;\n\tfont-size: small;\n\tcolor:#2b81af;\n\tborder-bottom-right-radius:15px;\n\tborder-bottom-left-radius:15px;\n\t-moz-border-radius-bottomright:15px;\n\t-moz-border-radius-bottomleft:15px;\n\t-webkit-border-bottom-right-radius:15px;\n\t-webkit-border-bottom-left-radius:15px;\n\tbackground-color:#D2E0E6;\n\tpadding:0.5em 0.5em 0.5em 2.5em;\n}\nstrong b.fail{\n\tcolor:#710909;\n\t}\nstrong b.pass{\n\tcolor:#5E740B;\n\t}\n"
  },
  {
    "path": "test/browser/qunit/qunit.js",
    "content": "/*\n * QUnit - A JavaScript Unit Testing Framework\n * \n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2009 John Resig, Jörn Zaefferer\n * Dual licensed under the MIT (MIT-LICENSE.txt)\n * and GPL (GPL-LICENSE.txt) licenses.\n */\n\n(function(window) {\n\nvar QUnit = {\n\n\t// Initialize the configuration options\n\tinit: function() {\n\t\tconfig = {\n\t\t\tstats: { all: 0, bad: 0 },\n\t\t\tmoduleStats: { all: 0, bad: 0 },\n\t\t\tstarted: +new Date,\n\t\t\tupdateRate: 1000,\n\t\t\tblocking: false,\n\t\t\tautorun: false,\n\t\t\tassertions: [],\n\t\t\tfilters: [],\n\t\t\tqueue: []\n\t\t};\n\n\t\tvar tests = id(\"qunit-tests\"),\n\t\t\tbanner = id(\"qunit-banner\"),\n\t\t\tresult = id(\"qunit-testresult\");\n\n\t\tif ( tests ) {\n\t\t\ttests.innerHTML = \"\";\n\t\t}\n\n\t\tif ( banner ) {\n\t\t\tbanner.className = \"\";\n\t\t}\n\n\t\tif ( result ) {\n\t\t\tresult.parentNode.removeChild( result );\n\t\t}\n\t},\n\t\n\t// call on start of module test to prepend name to all tests\n\tmodule: function(name, testEnvironment) {\n\t\tconfig.currentModule = name;\n\n\t\tsynchronize(function() {\n\t\t\tif ( config.currentModule ) {\n\t\t\t\tQUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );\n\t\t\t}\n\n\t\t\tconfig.currentModule = name;\n\t\t\tconfig.moduleTestEnvironment = testEnvironment;\n\t\t\tconfig.moduleStats = { all: 0, bad: 0 };\n\n\t\t\tQUnit.moduleStart( name, testEnvironment );\n\t\t});\n\t},\n\n\tasyncTest: function(testName, expected, callback) {\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = 0;\n\t\t}\n\n\t\tQUnit.test(testName, expected, callback, true);\n\t},\n\t\n\ttest: function(testName, expected, callback, async) {\n\t\tvar name = testName, testEnvironment, testEnvironmentArg;\n\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = null;\n\t\t}\n\t\t// is 2nd argument a testEnvironment?\n\t\tif ( expected && typeof expected === 'object') {\n\t\t\ttestEnvironmentArg =  expected;\n\t\t\texpected = null;\n\t\t}\n\n\t\tif ( config.currentModule ) {\n\t\t\tname = config.currentModule + \" module: \" + name;\n\t\t}\n\n\t\tif ( !validTest(name) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tsynchronize(function() {\n\t\t\tQUnit.testStart( testName );\n\n\t\t\ttestEnvironment = extend({\n\t\t\t\tsetup: function() {},\n\t\t\t\tteardown: function() {}\n\t\t\t}, config.moduleTestEnvironment);\n\t\t\tif (testEnvironmentArg) {\n\t\t\t\textend(testEnvironment,testEnvironmentArg);\n\t\t\t}\n\n\t\t\t// allow utility functions to access the current test environment\n\t\t\tQUnit.current_testEnvironment = testEnvironment;\n\t\t\t\n\t\t\tconfig.assertions = [];\n\t\t\tconfig.expected = expected;\n\n\t\t\ttry {\n\t\t\t\tif ( !config.pollution ) {\n\t\t\t\t\tsaveGlobal();\n\t\t\t\t}\n\n\t\t\t\ttestEnvironment.setup.call(testEnvironment);\n\t\t\t} catch(e) {\n\t\t\t\tQUnit.ok( false, \"Setup failed on \" + name + \": \" + e.message );\n\t\t\t}\n    });\n\n    synchronize(function() {\n\t\t\tif ( async ) {\n\t\t\t\tQUnit.stop();\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tcallback.call(testEnvironment);\n\t\t\t} catch(e) {\n\t\t\t\tfail(\"Test \" + name + \" died, exception and test follows\", e, callback);\n\t\t\t\tQUnit.ok( false, \"Died on test #\" + (config.assertions.length + 1) + \": \" + e.message );\n\t\t\t\t// else next test will carry the responsibility\n\t\t\t\tsaveGlobal();\n\n\t\t\t\t// Restart the tests if they're blocking\n\t\t\t\tif ( config.blocking ) {\n\t\t\t\t\tstart();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tsynchronize(function() {\n\t\t\ttry {\n\t\t\t\tcheckPollution();\n\t\t\t\ttestEnvironment.teardown.call(testEnvironment);\n\t\t\t} catch(e) {\n\t\t\t\tQUnit.ok( false, \"Teardown failed on \" + name + \": \" + e.message );\n\t\t\t}\n    });\n\n    synchronize(function() {\n\t\t\ttry {\n\t\t\t\tQUnit.reset();\n\t\t\t} catch(e) {\n\t\t\t\tfail(\"reset() failed, following Test \" + name + \", exception and reset fn follows\", e, reset);\n\t\t\t}\n\n\t\t\tif ( config.expected && config.expected != config.assertions.length ) {\n\t\t\t\tQUnit.ok( false, \"Expected \" + config.expected + \" assertions, but \" + config.assertions.length + \" were run\" );\n\t\t\t}\n\n\t\t\tvar good = 0, bad = 0,\n\t\t\t\ttests = id(\"qunit-tests\");\n\n\t\t\tconfig.stats.all += config.assertions.length;\n\t\t\tconfig.moduleStats.all += config.assertions.length;\n\n\t\t\tif ( tests ) {\n\t\t\t\tvar ol  = document.createElement(\"ol\");\n\t\t\t\tol.style.display = \"none\";\n\n\t\t\t\tfor ( var i = 0; i < config.assertions.length; i++ ) {\n\t\t\t\t\tvar assertion = config.assertions[i];\n\n\t\t\t\t\tvar li = document.createElement(\"li\");\n\t\t\t\t\tli.className = assertion.result ? \"pass\" : \"fail\";\n\t\t\t\t\tli.appendChild(document.createTextNode(assertion.message || \"(no message)\"));\n\t\t\t\t\tol.appendChild( li );\n\n\t\t\t\t\tif ( assertion.result ) {\n\t\t\t\t\t\tgood++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbad++;\n\t\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tvar b = document.createElement(\"strong\");\n\t\t\t\tb.innerHTML = name + \" <b style='color:black;'>(<b class='fail'>\" + bad + \"</b>, <b class='pass'>\" + good + \"</b>, \" + config.assertions.length + \")</b>\";\n\t\t\t\t\n\t\t\t\taddEvent(b, \"click\", function() {\n\t\t\t\t\tvar next = b.nextSibling, display = next.style.display;\n\t\t\t\t\tnext.style.display = display === \"none\" ? \"block\" : \"none\";\n\t\t\t\t});\n\t\t\t\t\n\t\t\t\taddEvent(b, \"dblclick\", function(e) {\n\t\t\t\t\tvar target = e && e.target ? e.target : window.event.srcElement;\n\t\t\t\t\tif ( target.nodeName.toLowerCase() === \"strong\" ) {\n\t\t\t\t\t\tvar text = \"\", node = target.firstChild;\n\n\t\t\t\t\t\twhile ( node.nodeType === 3 ) {\n\t\t\t\t\t\t\ttext += node.nodeValue;\n\t\t\t\t\t\t\tnode = node.nextSibling;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttext = text.replace(/(^\\s*|\\s*$)/g, \"\");\n\n\t\t\t\t\t\tif ( window.location ) {\n\t\t\t\t\t\t\twindow.location.href = window.location.href.match(/^(.+?)(\\?.*)?$/)[1] + \"?\" + encodeURIComponent(text);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tvar li = document.createElement(\"li\");\n\t\t\t\tli.className = bad ? \"fail\" : \"pass\";\n\t\t\t\tli.appendChild( b );\n\t\t\t\tli.appendChild( ol );\n\t\t\t\ttests.appendChild( li );\n\n\t\t\t\tif ( bad ) {\n\t\t\t\t\tvar toolbar = id(\"qunit-testrunner-toolbar\");\n\t\t\t\t\tif ( toolbar ) {\n\t\t\t\t\t\ttoolbar.style.display = \"block\";\n\t\t\t\t\t\tid(\"qunit-filter-pass\").disabled = null;\n\t\t\t\t\t\tid(\"qunit-filter-missing\").disabled = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tfor ( var i = 0; i < config.assertions.length; i++ ) {\n\t\t\t\t\tif ( !config.assertions[i].result ) {\n\t\t\t\t\t\tbad++;\n\t\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tQUnit.testDone( testName, bad, config.assertions.length );\n\n\t\t\tif ( !window.setTimeout && !config.queue.length ) {\n\t\t\t\tdone();\n\t\t\t}\n\t\t});\n\n\t\tif ( window.setTimeout && !config.doneTimer ) {\n\t\t\tconfig.doneTimer = window.setTimeout(function(){\n\t\t\t\tif ( !config.queue.length ) {\n\t\t\t\t\tdone();\n\t\t\t\t} else {\n\t\t\t\t\tsynchronize( done );\n\t\t\t\t}\n\t\t\t}, 13);\n\t\t}\n\t},\n\t\n\t/**\n\t * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.\n\t */\n\texpect: function(asserts) {\n\t\tconfig.expected = asserts;\n\t},\n\n\t/**\n\t * Asserts true.\n\t * @example ok( \"asdfasdf\".length > 5, \"There must be at least 5 chars\" );\n\t */\n\tok: function(a, msg) {\n\t\tQUnit.log(a, msg);\n\n\t\tconfig.assertions.push({\n\t\t\tresult: !!a,\n\t\t\tmessage: msg\n\t\t});\n\t},\n\n\t/**\n\t * Checks that the first two arguments are equal, with an optional message.\n\t * Prints out both actual and expected values.\n\t *\n\t * Prefered to ok( actual == expected, message )\n\t *\n\t * @example equal( format(\"Received {0} bytes.\", 2), \"Received 2 bytes.\" );\n\t *\n\t * @param Object actual\n\t * @param Object expected\n\t * @param String message (optional)\n\t */\n\tequal: function(actual, expected, message) {\n\t\tpush(expected == actual, actual, expected, message);\n\t},\n\n\tnotEqual: function(actual, expected, message) {\n\t\tpush(expected != actual, actual, expected, message);\n\t},\n\t\n\tdeepEqual: function(a, b, message) {\n\t\tpush(QUnit.equiv(a, b), a, b, message);\n\t},\n\n\tnotDeepEqual: function(a, b, message) {\n\t\tpush(!QUnit.equiv(a, b), a, b, message);\n\t},\n\n\tstrictEqual: function(actual, expected, message) {\n\t\tpush(expected === actual, actual, expected, message);\n\t},\n\n\tnotStrictEqual: function(actual, expected, message) {\n\t\tpush(expected !== actual, actual, expected, message);\n\t},\n\t\n\tstart: function() {\n\t\t// A slight delay, to avoid any current callbacks\n\t\tif ( window.setTimeout ) {\n\t\t\twindow.setTimeout(function() {\n\t\t\t\tif ( config.timeout ) {\n\t\t\t\t\tclearTimeout(config.timeout);\n\t\t\t\t}\n\n\t\t\t\tconfig.blocking = false;\n\t\t\t\tprocess();\n\t\t\t}, 13);\n\t\t} else {\n\t\t\tconfig.blocking = false;\n\t\t\tprocess();\n\t\t}\n\t},\n\t\n\tstop: function(timeout) {\n\t\tconfig.blocking = true;\n\n\t\tif ( timeout && window.setTimeout ) {\n\t\t\tconfig.timeout = window.setTimeout(function() {\n\t\t\t\tQUnit.ok( false, \"Test timed out\" );\n\t\t\t\tQUnit.start();\n\t\t\t}, timeout);\n\t\t}\n\t},\n\t\n\t/**\n\t * Resets the test setup. Useful for tests that modify the DOM.\n\t */\n\treset: function() {\n\t\tif ( window.jQuery ) {\n\t\t\tjQuery(\"#main\").html( config.fixture );\n\t\t\tjQuery.event.global = {};\n\t\t\tjQuery.ajaxSettings = extend({}, config.ajaxSettings);\n\t\t}\n\t},\n\t\n\t/**\n\t * Trigger an event on an element.\n\t *\n\t * @example triggerEvent( document.body, \"click\" );\n\t *\n\t * @param DOMElement elem\n\t * @param String type\n\t */\n\ttriggerEvent: function( elem, type, event ) {\n\t\tif ( document.createEvent ) {\n\t\t\tevent = document.createEvent(\"MouseEvents\");\n\t\t\tevent.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,\n\t\t\t\t0, 0, 0, 0, 0, false, false, false, false, 0, null);\n\t\t\telem.dispatchEvent( event );\n\n\t\t} else if ( elem.fireEvent ) {\n\t\t\telem.fireEvent(\"on\"+type);\n\t\t}\n\t},\n\t\n\t// Safe object type checking\n\tis: function( type, obj ) {\n\t\treturn Object.prototype.toString.call( obj ) === \"[object \"+ type +\"]\";\n\t},\n\t\n\t// Logging callbacks\n\tdone: function(failures, total) {},\n\tlog: function(result, message) {},\n\ttestStart: function(name) {},\n\ttestDone: function(name, failures, total) {},\n\tmoduleStart: function(name, testEnvironment) {},\n\tmoduleDone: function(name, failures, total) {}\n};\n\n// Backwards compatibility, deprecated\nQUnit.equals = QUnit.equal;\nQUnit.same = QUnit.deepEqual;\n\n// Maintain internal state\nvar config = {\n\t// The queue of tests to run\n\tqueue: [],\n\n\t// block until document ready\n\tblocking: true\n};\n\n// Load paramaters\n(function() {\n\tvar location = window.location || { search: \"\", protocol: \"file:\" },\n\t\tGETParams = location.search.slice(1).split('&');\n\n\tfor ( var i = 0; i < GETParams.length; i++ ) {\n\t\tGETParams[i] = decodeURIComponent( GETParams[i] );\n\t\tif ( GETParams[i] === \"noglobals\" ) {\n\t\t\tGETParams.splice( i, 1 );\n\t\t\ti--;\n\t\t\tconfig.noglobals = true;\n\t\t} else if ( GETParams[i].search('=') > -1 ) {\n\t\t\tGETParams.splice( i, 1 );\n\t\t\ti--;\n\t\t}\n\t}\n\t\n\t// restrict modules/tests by get parameters\n\tconfig.filters = GETParams;\n\t\n\t// Figure out if we're running the tests from a server or not\n\tQUnit.isLocal = !!(location.protocol === 'file:');\n})();\n\n// Expose the API as global variables, unless an 'exports'\n// object exists, in that case we assume we're in CommonJS\nif ( typeof exports === \"undefined\" || typeof require === \"undefined\" ) {\n\textend(window, QUnit);\n\twindow.QUnit = QUnit;\n} else {\n\textend(exports, QUnit);\n\texports.QUnit = QUnit;\n}\n\nif ( typeof document === \"undefined\" || document.readyState === \"complete\" ) {\n\tconfig.autorun = true;\n}\n\naddEvent(window, \"load\", function() {\n\t// Initialize the config, saving the execution queue\n\tvar oldconfig = extend({}, config);\n\tQUnit.init();\n\textend(config, oldconfig);\n\n\tconfig.blocking = false;\n\n\tvar userAgent = id(\"qunit-userAgent\");\n\tif ( userAgent ) {\n\t\tuserAgent.innerHTML = navigator.userAgent;\n\t}\n\t\n\tvar toolbar = id(\"qunit-testrunner-toolbar\");\n\tif ( toolbar ) {\n\t\ttoolbar.style.display = \"none\";\n\t\t\n\t\tvar filter = document.createElement(\"input\");\n\t\tfilter.type = \"checkbox\";\n\t\tfilter.id = \"qunit-filter-pass\";\n\t\tfilter.disabled = true;\n\t\taddEvent( filter, \"click\", function() {\n\t\t\tvar li = document.getElementsByTagName(\"li\");\n\t\t\tfor ( var i = 0; i < li.length; i++ ) {\n\t\t\t\tif ( li[i].className.indexOf(\"pass\") > -1 ) {\n\t\t\t\t\tli[i].style.display = filter.checked ? \"none\" : \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttoolbar.appendChild( filter );\n\n\t\tvar label = document.createElement(\"label\");\n\t\tlabel.setAttribute(\"for\", \"qunit-filter-pass\");\n\t\tlabel.innerHTML = \"Hide passed tests\";\n\t\ttoolbar.appendChild( label );\n\n\t\tvar missing = document.createElement(\"input\");\n\t\tmissing.type = \"checkbox\";\n\t\tmissing.id = \"qunit-filter-missing\";\n\t\tmissing.disabled = true;\n\t\taddEvent( missing, \"click\", function() {\n\t\t\tvar li = document.getElementsByTagName(\"li\");\n\t\t\tfor ( var i = 0; i < li.length; i++ ) {\n\t\t\t\tif ( li[i].className.indexOf(\"fail\") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {\n\t\t\t\t\tli[i].parentNode.parentNode.style.display = missing.checked ? \"none\" : \"block\";\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttoolbar.appendChild( missing );\n\n\t\tlabel = document.createElement(\"label\");\n\t\tlabel.setAttribute(\"for\", \"qunit-filter-missing\");\n\t\tlabel.innerHTML = \"Hide missing tests (untested code is broken code)\";\n\t\ttoolbar.appendChild( label );\n\t}\n\n\tvar main = id('main');\n\tif ( main ) {\n\t\tconfig.fixture = main.innerHTML;\n\t}\n\n\tif ( window.jQuery ) {\n\t\tconfig.ajaxSettings = window.jQuery.ajaxSettings;\n\t}\n\n\tQUnit.start();\n});\n\nfunction done() {\n\tif ( config.doneTimer && window.clearTimeout ) {\n\t\twindow.clearTimeout( config.doneTimer );\n\t\tconfig.doneTimer = null;\n\t}\n\n\tif ( config.queue.length ) {\n\t\tconfig.doneTimer = window.setTimeout(function(){\n\t\t\tif ( !config.queue.length ) {\n\t\t\t\tdone();\n\t\t\t} else {\n\t\t\t\tsynchronize( done );\n\t\t\t}\n\t\t}, 13);\n\n\t\treturn;\n\t}\n\n\tconfig.autorun = true;\n\n\t// Log the last module results\n\tif ( config.currentModule ) {\n\t\tQUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );\n\t}\n\n\tvar banner = id(\"qunit-banner\"),\n\t\ttests = id(\"qunit-tests\"),\n\t\thtml = ['Tests completed in ',\n\t\t+new Date - config.started, ' milliseconds.<br/>',\n\t\t'<span class=\"passed\">', config.stats.all - config.stats.bad, '</span> tests of <span class=\"total\">', config.stats.all, '</span> passed, <span class=\"failed\">', config.stats.bad,'</span> failed.'].join('');\n\n\tif ( banner ) {\n\t\tbanner.className = (config.stats.bad ? \"qunit-fail\" : \"qunit-pass\");\n\t}\n\n\tif ( tests ) {\t\n\t\tvar result = id(\"qunit-testresult\");\n\n\t\tif ( !result ) {\n\t\t\tresult = document.createElement(\"p\");\n\t\t\tresult.id = \"qunit-testresult\";\n\t\t\tresult.className = \"result\";\n\t\t\ttests.parentNode.insertBefore( result, tests.nextSibling );\n\t\t}\n\n\t\tresult.innerHTML = html;\n\t}\n\n\tQUnit.done( config.stats.bad, config.stats.all );\n}\n\nfunction validTest( name ) {\n\tvar i = config.filters.length,\n\t\trun = false;\n\n\tif ( !i ) {\n\t\treturn true;\n\t}\n\t\n\twhile ( i-- ) {\n\t\tvar filter = config.filters[i],\n\t\t\tnot = filter.charAt(0) == '!';\n\n\t\tif ( not ) {\n\t\t\tfilter = filter.slice(1);\n\t\t}\n\n\t\tif ( name.indexOf(filter) !== -1 ) {\n\t\t\treturn !not;\n\t\t}\n\n\t\tif ( not ) {\n\t\t\trun = true;\n\t\t}\n\t}\n\n\treturn run;\n}\n\nfunction push(result, actual, expected, message) {\n\tmessage = message || (result ? \"okay\" : \"failed\");\n\tQUnit.ok( result, result ? message + \": \" + QUnit.jsDump.parse(expected) : message + \", expected: \" + QUnit.jsDump.parse(expected) + \" result: \" + QUnit.jsDump.parse(actual) );\n}\n\nfunction synchronize( callback ) {\n\tconfig.queue.push( callback );\n\n\tif ( config.autorun && !config.blocking ) {\n\t\tprocess();\n\t}\n}\n\nfunction process() {\n\tvar start = (new Date()).getTime();\n\n\twhile ( config.queue.length && !config.blocking ) {\n\t\tif ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {\n\t\t\tconfig.queue.shift()();\n\n\t\t} else {\n\t\t\tsetTimeout( process, 13 );\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction saveGlobal() {\n\tconfig.pollution = [];\n\t\n\tif ( config.noglobals ) {\n\t\tfor ( var key in window ) {\n\t\t\tconfig.pollution.push( key );\n\t\t}\n\t}\n}\n\nfunction checkPollution( name ) {\n\tvar old = config.pollution;\n\tsaveGlobal();\n\t\n\tvar newGlobals = diff( old, config.pollution );\n\tif ( newGlobals.length > 0 ) {\n\t\tok( false, \"Introduced global variable(s): \" + newGlobals.join(\", \") );\n\t\tconfig.expected++;\n\t}\n\n\tvar deletedGlobals = diff( config.pollution, old );\n\tif ( deletedGlobals.length > 0 ) {\n\t\tok( false, \"Deleted global variable(s): \" + deletedGlobals.join(\", \") );\n\t\tconfig.expected++;\n\t}\n}\n\n// returns a new Array with the elements that are in a but not in b\nfunction diff( a, b ) {\n\tvar result = a.slice();\n\tfor ( var i = 0; i < result.length; i++ ) {\n\t\tfor ( var j = 0; j < b.length; j++ ) {\n\t\t\tif ( result[i] === b[j] ) {\n\t\t\t\tresult.splice(i, 1);\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction fail(message, exception, callback) {\n\tif ( typeof console !== \"undefined\" && console.error && console.warn ) {\n\t\tconsole.error(message);\n\t\tconsole.error(exception);\n\t\tconsole.warn(callback.toString());\n\n\t} else if ( window.opera && opera.postError ) {\n\t\topera.postError(message, exception, callback.toString);\n\t}\n}\n\nfunction extend(a, b) {\n\tfor ( var prop in b ) {\n\t\ta[prop] = b[prop];\n\t}\n\n\treturn a;\n}\n\nfunction addEvent(elem, type, fn) {\n\tif ( elem.addEventListener ) {\n\t\telem.addEventListener( type, fn, false );\n\t} else if ( elem.attachEvent ) {\n\t\telem.attachEvent( \"on\" + type, fn );\n\t} else {\n\t\tfn();\n\t}\n}\n\nfunction id(name) {\n\treturn !!(typeof document !== \"undefined\" && document && document.getElementById) &&\n\t\tdocument.getElementById( name );\n}\n\n// Test for equality any JavaScript type.\n// Discussions and reference: http://philrathe.com/articles/equiv\n// Test suites: http://philrathe.com/tests/equiv\n// Author: Philippe Rathé <prathe@gmail.com>\nQUnit.equiv = function () {\n\n    var innerEquiv; // the real equiv function\n    var callers = []; // stack to decide between skip/abort functions\n    var parents = []; // stack to avoiding loops from circular referencing\n\n\n    // Determine what is o.\n    function hoozit(o) {\n        if (QUnit.is(\"String\", o)) {\n            return \"string\";\n            \n        } else if (QUnit.is(\"Boolean\", o)) {\n            return \"boolean\";\n\n        } else if (QUnit.is(\"Number\", o)) {\n\n            if (isNaN(o)) {\n                return \"nan\";\n            } else {\n                return \"number\";\n            }\n\n        } else if (typeof o === \"undefined\") {\n            return \"undefined\";\n\n        // consider: typeof null === object\n        } else if (o === null) {\n            return \"null\";\n\n        // consider: typeof [] === object\n        } else if (QUnit.is( \"Array\", o)) {\n            return \"array\";\n        \n        // consider: typeof new Date() === object\n        } else if (QUnit.is( \"Date\", o)) {\n            return \"date\";\n\n        // consider: /./ instanceof Object;\n        //           /./ instanceof RegExp;\n        //          typeof /./ === \"function\"; // => false in IE and Opera,\n        //                                          true in FF and Safari\n        } else if (QUnit.is( \"RegExp\", o)) {\n            return \"regexp\";\n\n        } else if (typeof o === \"object\") {\n            return \"object\";\n\n        } else if (QUnit.is( \"Function\", o)) {\n            return \"function\";\n        } else {\n            return undefined;\n        }\n    }\n\n    // Call the o related callback with the given arguments.\n    function bindCallbacks(o, callbacks, args) {\n        var prop = hoozit(o);\n        if (prop) {\n            if (hoozit(callbacks[prop]) === \"function\") {\n                return callbacks[prop].apply(callbacks, args);\n            } else {\n                return callbacks[prop]; // or undefined\n            }\n        }\n    }\n    \n    var callbacks = function () {\n\n        // for string, boolean, number and null\n        function useStrictEquality(b, a) {\n            if (b instanceof a.constructor || a instanceof b.constructor) {\n                // to catch short annotaion VS 'new' annotation of a declaration\n                // e.g. var i = 1;\n                //      var j = new Number(1);\n                return a == b;\n            } else {\n                return a === b;\n            }\n        }\n\n        return {\n            \"string\": useStrictEquality,\n            \"boolean\": useStrictEquality,\n            \"number\": useStrictEquality,\n            \"null\": useStrictEquality,\n            \"undefined\": useStrictEquality,\n\n            \"nan\": function (b) {\n                return isNaN(b);\n            },\n\n            \"date\": function (b, a) {\n                return hoozit(b) === \"date\" && a.valueOf() === b.valueOf();\n            },\n\n            \"regexp\": function (b, a) {\n                return hoozit(b) === \"regexp\" &&\n                    a.source === b.source && // the regex itself\n                    a.global === b.global && // and its modifers (gmi) ...\n                    a.ignoreCase === b.ignoreCase &&\n                    a.multiline === b.multiline;\n            },\n\n            // - skip when the property is a method of an instance (OOP)\n            // - abort otherwise,\n            //   initial === would have catch identical references anyway\n            \"function\": function () {\n                var caller = callers[callers.length - 1];\n                return caller !== Object &&\n                        typeof caller !== \"undefined\";\n            },\n\n            \"array\": function (b, a) {\n                var i, j, loop;\n                var len;\n\n                // b could be an object literal here\n                if ( ! (hoozit(b) === \"array\")) {\n                    return false;\n                }   \n                \n                len = a.length;\n                if (len !== b.length) { // safe and faster\n                    return false;\n                }\n                \n                //track reference to avoid circular references\n                parents.push(a);\n                for (i = 0; i < len; i++) {\n                    loop = false;\n                    for(j=0;j<parents.length;j++){\n                        if(parents[j] === a[i]){\n                            loop = true;//dont rewalk array\n                        }\n                    }\n                    if (!loop && ! innerEquiv(a[i], b[i])) {\n                        parents.pop();\n                        return false;\n                    }\n                }\n                parents.pop();\n                return true;\n            },\n\n            \"object\": function (b, a) {\n                var i, j, loop;\n                var eq = true; // unless we can proove it\n                var aProperties = [], bProperties = []; // collection of strings\n\n                // comparing constructors is more strict than using instanceof\n                if ( a.constructor !== b.constructor) {\n                    return false;\n                }\n\n                // stack constructor before traversing properties\n                callers.push(a.constructor);\n                //track reference to avoid circular references\n                parents.push(a);\n                \n                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep\n                    loop = false;\n                    for(j=0;j<parents.length;j++){\n                        if(parents[j] === a[i])\n                            loop = true; //don't go down the same path twice\n                    }\n                    aProperties.push(i); // collect a's properties\n\n                    if (!loop && ! innerEquiv(a[i], b[i])) {\n                        eq = false;\n                        break;\n                    }\n                }\n\n                callers.pop(); // unstack, we are done\n                parents.pop();\n\n                for (i in b) {\n                    bProperties.push(i); // collect b's properties\n                }\n\n                // Ensures identical properties name\n                return eq && innerEquiv(aProperties.sort(), bProperties.sort());\n            }\n        };\n    }();\n\n    innerEquiv = function () { // can take multiple arguments\n        var args = Array.prototype.slice.apply(arguments);\n        if (args.length < 2) {\n            return true; // end transition\n        }\n\n        return (function (a, b) {\n            if (a === b) {\n                return true; // catch the most you can\n            } else if (a === null || b === null || typeof a === \"undefined\" || typeof b === \"undefined\" || hoozit(a) !== hoozit(b)) {\n                return false; // don't lose time with error prone cases\n            } else {\n                return bindCallbacks(a, callbacks, [b, a]);\n            }\n\n        // apply transition with (1..n) arguments\n        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));\n    };\n\n    return innerEquiv;\n\n}();\n\n/**\n * jsDump\n * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com\n * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)\n * Date: 5/15/2008\n * @projectDescription Advanced and extensible data dumping for Javascript.\n * @version 1.0.0\n * @author Ariel Flesler\n * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}\n */\nQUnit.jsDump = (function() {\n\tfunction quote( str ) {\n\t\treturn '\"' + str.toString().replace(/\"/g, '\\\\\"') + '\"';\n\t};\n\tfunction literal( o ) {\n\t\treturn o + '';\t\n\t};\n\tfunction join( pre, arr, post ) {\n\t\tvar s = jsDump.separator(),\n\t\t\tbase = jsDump.indent(),\n\t\t\tinner = jsDump.indent(1);\n\t\tif ( arr.join )\n\t\t\tarr = arr.join( ',' + s + inner );\n\t\tif ( !arr )\n\t\t\treturn pre + post;\n\t\treturn [ pre, inner + arr, base + post ].join(s);\n\t};\n\tfunction array( arr ) {\n\t\tvar i = arr.length,\tret = Array(i);\t\t\t\t\t\n\t\tthis.up();\n\t\twhile ( i-- )\n\t\t\tret[i] = this.parse( arr[i] );\t\t\t\t\n\t\tthis.down();\n\t\treturn join( '[', ret, ']' );\n\t};\n\t\n\tvar reName = /^function (\\w+)/;\n\t\n\tvar jsDump = {\n\t\tparse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance\n\t\t\tvar\tparser = this.parsers[ type || this.typeOf(obj) ];\n\t\t\ttype = typeof parser;\t\t\t\n\t\t\t\n\t\t\treturn type == 'function' ? parser.call( this, obj ) :\n\t\t\t\t   type == 'string' ? parser :\n\t\t\t\t   this.parsers.error;\n\t\t},\n\t\ttypeOf:function( obj ) {\n\t\t\tvar type;\n\t\t\tif ( obj === null ) {\n\t\t\t\ttype = \"null\";\n\t\t\t} else if (typeof obj === \"undefined\") {\n\t\t\t\ttype = \"undefined\";\n\t\t\t} else if (QUnit.is(\"RegExp\", obj)) {\n\t\t\t\ttype = \"regexp\";\n\t\t\t} else if (QUnit.is(\"Date\", obj)) {\n\t\t\t\ttype = \"date\";\n\t\t\t} else if (QUnit.is(\"Function\", obj)) {\n\t\t\t\ttype = \"function\";\n\t\t\t} else if (obj.setInterval && obj.document && !obj.nodeType) {\n\t\t\t\ttype = \"window\";\n\t\t\t} else if (obj.nodeType === 9) {\n\t\t\t\ttype = \"document\";\n\t\t\t} else if (obj.nodeType) {\n\t\t\t\ttype = \"node\";\n\t\t\t} else if (typeof obj === \"object\" && typeof obj.length === \"number\" && obj.length >= 0) {\n\t\t\t\ttype = \"array\";\n\t\t\t} else {\n\t\t\t\ttype = typeof obj;\n\t\t\t}\n\t\t\treturn type;\n\t\t},\n\t\tseparator:function() {\n\t\t\treturn this.multiline ?\tthis.HTML ? '<br />' : '\\n' : this.HTML ? '&nbsp;' : ' ';\n\t\t},\n\t\tindent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing\n\t\t\tif ( !this.multiline )\n\t\t\t\treturn '';\n\t\t\tvar chr = this.indentChar;\n\t\t\tif ( this.HTML )\n\t\t\t\tchr = chr.replace(/\\t/g,'   ').replace(/ /g,'&nbsp;');\n\t\t\treturn Array( this._depth_ + (extra||0) ).join(chr);\n\t\t},\n\t\tup:function( a ) {\n\t\t\tthis._depth_ += a || 1;\n\t\t},\n\t\tdown:function( a ) {\n\t\t\tthis._depth_ -= a || 1;\n\t\t},\n\t\tsetParser:function( name, parser ) {\n\t\t\tthis.parsers[name] = parser;\n\t\t},\n\t\t// The next 3 are exposed so you can use them\n\t\tquote:quote, \n\t\tliteral:literal,\n\t\tjoin:join,\n\t\t//\n\t\t_depth_: 1,\n\t\t// This is the list of parsers, to modify them, use jsDump.setParser\n\t\tparsers:{\n\t\t\twindow: '[Window]',\n\t\t\tdocument: '[Document]',\n\t\t\terror:'[ERROR]', //when no parser is found, shouldn't happen\n\t\t\tunknown: '[Unknown]',\n\t\t\t'null':'null',\n\t\t\tundefined:'undefined',\n\t\t\t'function':function( fn ) {\n\t\t\t\tvar ret = 'function',\n\t\t\t\t\tname = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE\n\t\t\t\tif ( name )\n\t\t\t\t\tret += ' ' + name;\n\t\t\t\tret += '(';\n\t\t\t\t\n\t\t\t\tret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');\n\t\t\t\treturn join( ret, this.parse(fn,'functionCode'), '}' );\n\t\t\t},\n\t\t\tarray: array,\n\t\t\tnodelist: array,\n\t\t\targuments: array,\n\t\t\tobject:function( map ) {\n\t\t\t\tvar ret = [ ];\n\t\t\t\tthis.up();\n\t\t\t\tfor ( var key in map )\n\t\t\t\t\tret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );\n\t\t\t\tthis.down();\n\t\t\t\treturn join( '{', ret, '}' );\n\t\t\t},\n\t\t\tnode:function( node ) {\n\t\t\t\tvar open = this.HTML ? '&lt;' : '<',\n\t\t\t\t\tclose = this.HTML ? '&gt;' : '>';\n\t\t\t\t\t\n\t\t\t\tvar tag = node.nodeName.toLowerCase(),\n\t\t\t\t\tret = open + tag;\n\t\t\t\t\t\n\t\t\t\tfor ( var a in this.DOMAttrs ) {\n\t\t\t\t\tvar val = node[this.DOMAttrs[a]];\n\t\t\t\t\tif ( val )\n\t\t\t\t\t\tret += ' ' + a + '=' + this.parse( val, 'attribute' );\n\t\t\t\t}\n\t\t\t\treturn ret + close + open + '/' + tag + close;\n\t\t\t},\n\t\t\tfunctionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function\n\t\t\t\tvar l = fn.length;\n\t\t\t\tif ( !l ) return '';\t\t\t\t\n\t\t\t\t\n\t\t\t\tvar args = Array(l);\n\t\t\t\twhile ( l-- )\n\t\t\t\t\targs[l] = String.fromCharCode(97+l);//97 is 'a'\n\t\t\t\treturn ' ' + args.join(', ') + ' ';\n\t\t\t},\n\t\t\tkey:quote, //object calls it internally, the key part of an item in a map\n\t\t\tfunctionCode:'[code]', //function calls it internally, it's the content of the function\n\t\t\tattribute:quote, //node calls it internally, it's an html attribute value\n\t\t\tstring:quote,\n\t\t\tdate:quote,\n\t\t\tregexp:literal, //regex\n\t\t\tnumber:literal,\n\t\t\t'boolean':literal\n\t\t},\n\t\tDOMAttrs:{//attributes to dump from nodes, name=>realName\n\t\t\tid:'id',\n\t\t\tname:'name',\n\t\t\t'class':'className'\n\t\t},\n\t\tHTML:false,//if true, entities are escaped ( <, >, \\t, space and \\n )\n\t\tindentChar:'   ',//indentation unit\n\t\tmultiline:false //if true, items in a collection, are separated by a \\n, else just a space.\n\t};\n\n\treturn jsDump;\n})();\n\n})(this);\n"
  },
  {
    "path": "test/browser/tasks.client.js",
    "content": "\n// Data model\nvar Task = persistence.define('Task', {\n    name: \"TEXT\",\n    done: \"BOOL\",\n    lastChange: \"DATE\"\n});\n\npersistence.connect('taskdemo', 'database', 5 * 1024 * 1024, '1.0');\npersistence.schemaSync();\n\nfunction syncAll() {\n  persistence.sync.synchronize('/recentChanges', Task, function(conflicts, updatesToPush, callback) {\n      console.log(conflicts);\n      callback();\n    });\n}\n\n\nfunction addTask() {\n  var t = new Task();\n  t.name = \"Some new local task\";\n  t.done = false;\n  t.lastChange = new Date();\n  persistence.add(t);\n  persistence.flush();\n}\n"
  },
  {
    "path": "test/browser/tasks.html",
    "content": "<html>\n  <head>\n    <script src=\"http://code.jquery.com/jquery-1.4.2.min.js\" type=\"application/x-javascript\" charset=\"utf-8\"></script>\n    <script src=\"persistence.js\" type=\"application/x-javascript\" charset=\"utf-8\"></script>\n    <script src=\"persistence.sync.js\" type=\"application/x-javascript\" charset=\"utf-8\"></script>\n    <script src=\"tasks.client.js\" type=\"application/x-javascript\" charset=\"utf-8\"></script>\n  </head>\n  <body>\n    <h1>Tasks</h1>\n    <p>Hello!</p>\n    <button onclick=\"syncAll()\">Sync</button>\n    <button onclick=\"addTask()\">Add task</button>\n  </body>\n</html>\n"
  },
  {
    "path": "test/browser/test.jquery-persistence.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \n                    \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n  <script src=\"http://code.jquery.com/jquery-1.4.3.min.js\" type=\"application/javascript\"></script>\n  <link rel=\"stylesheet\" href=\"qunit/qunit.css\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\" src=\"qunit/qunit.js\"></script>\n\n  <script src=\"http://code.google.com/apis/gears/gears_init.js\"></script>\n  <script src=\"../lib/persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"../lib/persistence.store.sql.js\" type=\"application/javascript\"></script>\n  <script src=\"../lib/persistence.store.websql.js\" type=\"application/javascript\"></script>\n  <script src=\"../lib/persistence.store.memory.js\" type=\"application/javascript\"></script>\n  <script src=\"../lib/jquery.persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"util.js\" type=\"application/javascript\"></script>\n  <script src=\"test.jquery-persistence.js\" type=\"application/javascript\"></script>\n</head>\n<body>\n  <h1 id=\"qunit-header\">persistence.js core tests with jquery-plugin</h1>\n  <h2 id=\"qunit-banner\"></h2>\n  <h2 id=\"qunit-userAgent\"></h2>\n  <ol id=\"qunit-tests\"></ol>\n</body>\n</html>"
  },
  {
    "path": "test/browser/test.jquery-persistence.js",
    "content": "$(document).ready(function(){\n  persistence.store.websql.config(persistence, 'persistencetest', 'My db', 5 * 1024 * 1024);\n  //persistence.store.memory.config(persistence);\n  persistence.debug = true;\n\n  var Project = persistence.define('Project', {\n      name: \"TEXT\"\n    });\n\n  var Task = persistence.define('Task', {\n      name: \"TEXT\",\n      done: \"BOOL\",\n      counter: \"INT\",\n      dateAdded: \"DATE\",\n      metaData: \"JSON\"\n    });\n\n  var Tag = persistence.define('Tag', {\n      name: \"TEXT\"\n    });\n\n  Task.hasMany('tags', Tag, 'tasks');\n  Tag.hasMany('tasks', Task, 'tags');\n\n  Project.hasMany('tasks', Task, 'project');\n\n  window.Project = Project;\n  window.Task = Task\n  window.Project = Project;\n\n  module(\"Setup\");\n  \n  asyncTest(\"setting up database\", 1, function() {\n    persistence.schemaSync(function(tx){\n      ok(true, 'schemaSync called callback function');\n      start();\n    });\n  });\n\n  module(\"Entity manipulation\", {\n    setup: function() {\n      stop();\n      persistence.reset(function() {\n          persistence.schemaSync(start);\n        });\n    }\n  });\n\n  test(\"Property default values\", 5, function() {\n    var t1 = new Task();\n    QUnit.strictEqual($(t1).data('name'), \"\", \"TEXT properties default to ''\");\n    QUnit.strictEqual($(t1).data('done'), false, \"BOOL properties default to false\");\n    QUnit.strictEqual($(t1).data('counter'), 0, \"INT properties default to 0\");\n    QUnit.strictEqual($(t1).data('dateAdded'), null, \"DATE properties default to null\");\n    QUnit.strictEqual($(t1).data('metaData'), null, \"JSON properties default to null\");\n  });\n\n  test(\"Property value assignment\", 5, function() {\n    var t1 = new Task();\n    var now = new Date();\n    var meta = {starRating: 5};\n    $(t1).data('name', \"Task 1\");\n    $(t1).data('done', false);\n    $(t1).data('counter', 7);\n    $(t1).data('dateAdded', now);\n    $(t1).data('metaData', meta);\n    QUnit.strictEqual($(t1).data('name'), 'Task 1', \"Assignment for TEXT properties\");\n    QUnit.strictEqual($(t1).data('done'), false, \"Assignment for BOOL properties\");\n    QUnit.strictEqual($(t1).data('counter'), 7, \"Assignment for INT properties\");\n    QUnit.strictEqual($(t1).data('dateAdded'), now, \"Assignment for DATE properties\");\n    QUnit.strictEqual($(t1).data('metaData'), meta, \"Assignment for JSON properties\");\n  }); \n\n  test(\"Property contructor property value assignment\", 5, function() {\n    var now = new Date();\n    var meta = {starRating: 5};\n    var t1 = new Task({\n        name: \"Task 1\",\n        done: false,\n        counter: 7,\n        dateAdded: now,\n        metaData: meta\n      });\n    QUnit.strictEqual($(t1).data('name'), 'Task 1', \"Assignment for TEXT properties\");\n    QUnit.strictEqual($(t1).data('done'), false, \"Assignment for BOOL properties\");\n    QUnit.strictEqual($(t1).data('counter'), 7, \"Assignment for INT properties\");\n    QUnit.strictEqual($(t1).data('dateAdded'), now, \"Assignment for DATE properties\");\n    QUnit.strictEqual($(t1).data('metaData'), meta, \"Assignment for JSON properties\");\n  });\n\n  asyncTest(\"Empty object persistence\", function() {\n    var t1 = new Task();\n    persistence.add(t1);\n    persistence.flush(function() {\n      Task.all().one(function(t1db) {\n        equals(t1db.id, t1.id, \"TEXT properties default to ''\");\n        equals($(t1db).data('name'), \"\", \"TEXT properties default to ''\");\n        equals($(t1db).data('done'), false, \"BOOL properties default to false\");\n        equals($(t1db).data('counter'), 0, \"INT properties default to 0\");\n        equals($(t1db).data('dateAdded'), null, \"DATE properties default to null\");\n        equals($(t1db).data('metaData'), null, \"JSON properties default to null\");\n        start();\n      });\n    });\n  });\n\n  asyncTest(\"Object persistence\", function() {\n    var now = new Date();\n    var meta = {starRating: 5};\n    var t1 = new Task({\n      name: \"Task 1\",\n      done: false,\n      counter: 7,\n      dateAdded: now,\n      metaData: meta\n    });\n    persistence.add(t1);\n    persistence.flush(function() {\n      //persistence.clean();\n      Task.all().one(function(t1db) {\n        equals($(t1db).data('name'), 'Task 1', \"Persistence of TEXT properties\");\n        equals($(t1db).data('done'), false, \"Persistence of BOOL properties\");\n        equals($(t1db).data('counter'), 7, \"Persistence of INT properties\");\n        equals(Math.round($(t1db).data('dateAdded').getTime()/1000)*1000, Math.round(now.getTime()/1000)*1000, \"Persistence of DATE properties\");\n        same($(t1db).data('metaData'), meta, \"Persistence of JSON properties\");\n        start();\n      });\n    });\n  });    \n      \n  asyncTest(\"Multiple objects\", function() {\n    var objs = [];\n    var counter = 0;\n    for(var i = 0; i < 25; i++) {\n      var t = new Task({name: \"Task \" + i});\n      $(t).data('counter', counter);\n      objs.push(t);\n      persistence.add(t);\n      counter++;\n    }\n    persistence.flush(function() {\n      Task.all().order('counter', true).list(function(results) {\n        for(var i = 0; i < 25; i++) {\n          ok(results[i] === objs[i], 'Cache works OK');\n        }\n        //persistence.clean(); // Clean out local cache\n        Task.all().order('counter', true).list(function(results) {\n          for(var i = 0; i < 25; i++) {\n            ok(results[i].id === objs[i].id, 'Retrieving from DB ok');\n          }\n          start();\n        });\n      });\n    });\n  });  \n\n  asyncTest(\"One-to-many\", function() {\n    var p = new Project({name: \"Some project\"});\n    persistence.add(p);\n    $(p).data('tasks').list(function(tasks) {\n      equals(tasks.length, 0, \"Initially, no tasks\");\n      var task1 = new Task({name: \"Do dishes\"});\n      var task2 = new Task({name: \"Laundry\"});\n    \n      // Adding in two ways\n      $(p).data('tasks').add(task1);    \n      $(task2).data('project', p);\n    \n      $(p).data('tasks').order('name', true).list(function(tasks) {\n        equals(tasks.length, 2, \"Now two tasks\");\n        equals(tasks[0].id, task1.id, \"Right tasks\");\n        equals(tasks[1].id, task2.id, \"Right tasks\");\n        start();\n      });\n    });\n  }); \n\n  asyncTest(\"Many-to-many\", function() {\n    var t = new Task({name: \"Some task\"});\n    persistence.add(t);\n    $(t).data('tags').list(function(tags) {\n      equals(tags.length, 0, \"Initially, no tags\");\n      var tag1 = new Tag({name: \"important\"});\n      var tag2 = new Tag({name: \"today\"});\n      $(t).data('tags').add(tag1);\n      $(t).data('tags').add(tag2);\n      $(t).data('tags').list(function(tags) {\n        equals(tags.length, 2, \"2 tags added\");\n        var oneTag = tags[0];\n        $(oneTag).data('tasks').list(function(tagTasks) {\n          equals(tagTasks.length, 1, \"Tag has one task\");\n          equals(tagTasks[0].id, t.id, \"Correct task\");\n          $(oneTag).data('tasks').remove(tagTasks[0]);\n          $(t).data('tags').list(function(newTags) {\n            equals(newTags.length, 1, \"Tag removed task, task has only one tag left\");\n            start();\n          });\n        });\n      });\n    });\n  });\n  \n  module(\"Query collections\", {\n    setup: function() {\n      stop();\n      persistence.reset(function() {\n        persistence.schemaSync(start);\n      });\n    }\n  });\n\n  function intFilterTests(coll, callback) {\n    for(var i = 0; i < 25; i++) {\n      var t = new Task({name: \"Task \" + i, done: false});\n      $(t).data('counter', i);\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 25, \"Count items in collection\");\n        coll.filter(\"counter\", \">\", 10).list(function(results) {\n            equals(results.length, 14, \"> filter test\");\n            coll.filter(\"counter\", \"in\", [0, 1, 2]).list(function(results) {\n                equals(results.length, 3, \"'in' filter test\");\n                coll.filter(\"counter\", \"not in\", [0, 1]).list(function(results) {\n                    equals(results.length, 23, \"'not in' filter test\");\n                    coll.filter(\"counter\", \"!=\", 0).list(function(results) {\n                        equals(results.length, 24, \"'!=' filter test\");\n                        callback();\n                      });\n                  });\n              });\n          });\n      });\n  }\n  \n  function textFilterTests(coll, callback) {\n    var alphabet = 'abcdefghijklmnopqrstufwxyz';\n    for(var i = 0; i <= 25; i++) {\n      var t = new Task({name: alphabet[i]});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 26, \"Count items in collection\");\n        coll.filter(\"name\", \"=\", 'a').list(function(results) {\n            equals(results.length, 1, \"= filter test\");\n            coll.filter(\"name\", \"!=\", 'a').list(function(results) {\n                equals(results.length, 25, \"!= filter test\");\n                coll.filter(\"name\", \">\", 'm').list(function(results) {\n                    equals(results.length, 12, \"> filter test\");\n                    coll.filter(\"name\", \"in\", [\"a\", \"b\"]).list(function(results) {\n                        equals(results.length, 2, \"'in' filter test\");\n                        coll.filter(\"name\", \"not in\", [\"q\", \"x\"]).list(function(results) {\n                            equals(results.length, 24, \"'not in' filter test\");\n                            callback();\n                          });\n                      });\n                  });\n              });\n          });\n      });\n  }\n  \n  function boolFilterTests(coll, callback) {\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, done: i % 2 === 0});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 24, \"Count items in collection\");\n        coll.filter(\"done\", \"=\", true).list(function(results) {\n            equals(results.length, 12, \"= filter test\");\n            coll.filter(\"done\", \"=\", false).list(function(results) {\n                equals(results.length, 12, \"= filter test\");\n                coll.filter(\"done\", \"!=\", true).list(function(results) {\n                    equals(results.length, 12, \"'!=' filter test\");\n                    coll.filter(\"done\", \"!=\", false).list(function(results) {\n                        equals(results.length, 12, \"'!=' filter test\");\n                        callback();\n                      });\n                  });\n              });\n          });\n      });\n  }\n  \n  function dateFilterTests(coll, callback) {\n    var now = new Date();\n  \n    function dateInDays(n) {\n      var newDate = new Date(now.getTime());\n      newDate.setDate(newDate.getDate()+n);\n      return newDate;\n    }\n  \n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, dateAdded: dateInDays(i)});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 24, \"Count items in collection\");\n        coll.filter(\"dateAdded\", \"=\", dateInDays(1)).list(function(results) {\n            equals(results.length, 1, \"= filter test\");\n            coll.filter(\"dateAdded\", \"!=\", dateInDays(1)).list(function(results) {\n                equals(results.length, 23, \"= filter test\");\n                coll.filter(\"dateAdded\", \">\", dateInDays(12)).list(function(results) {\n                    equals(results.length, 11, \"> filter test\");\n                    start();\n                  });\n              })\n          });\n      });\n  }\n  \n   asyncTest(\"Database INT filters\", function() {\n       for(var i = 0; i < 25; i++) {\n         var t = new Task({name: \"Root task \" + i, done: false});\n         $(t).data('counter', i);\n         persistence.add(t);\n       }\n  \n       var p = new Project({name: \"My project\"});\n       persistence.add(p);\n       intFilterTests($(p).data('tasks'), start);\n     }); \n  \n  asyncTest(\"Local INT filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      intFilterTests(coll, start);\n    });\n   \n  asyncTest(\"Database TEXT filters\", function() {\n      var alphabet = 'abcdefghijklmnopqrstufwxyz';\n      for(var i = 0; i <= 25; i++) {\n        var t = new Task({name: alphabet[i]});\n        persistence.add(t);\n      }\n  \n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      textFilterTests($(p).data('tasks'), start);\n    });\n  \n  asyncTest(\"Local TEXT filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      textFilterTests(coll, start);\n    });\n  \n  asyncTest(\"Database BOOL filters\", function() {\n      for(var i = 0; i < 25; i++) {\n        var t = new Task({name: \"Root task \" + i, done: false});\n        $(t).data('counter', i);\n        persistence.add(t);\n      }\n  \n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      boolFilterTests($(p).data('tasks'), start);\n    });\n  \n  asyncTest(\"Local BOOL filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      boolFilterTests(coll, start);\n    });\n  \n  asyncTest(\"Database DATE filters\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      dateFilterTests($(p).data('tasks'), start);\n    });\n  \n  asyncTest(\"Local DATE filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      dateFilterTests(coll, start);\n    });\n   \n  \n   function intOrderTests(coll, callback) {\n     var tasks = [];\n     for(var i = 0; i < 24; i++) {\n       var t = new Task({name: \"Task \" + i, counter: i});\n       tasks.push(t);\n       coll.add(t);\n     }\n     coll.order('counter', true).list(function(results) {\n         for(var i = 0; i < 24; i++) {\n           equals(results[i].id, tasks[i].id, \"order check, ascending\");\n         }\n         tasks.reverse();\n         coll.order('counter', false).list(function(results) {\n             for(var i = 0; i < 24; i++) {\n               equals(results[i].id, tasks[i].id, \"order check, descending\");\n             }\n             callback();\n           });\n       });\n   }\n   \n   function dateOrderTests(coll, callback) {\n     var now = new Date();\n   \n     function dateInDays(n) {\n       var newDate = new Date(now.getTime());\n       newDate.setDate(newDate.getDate()+n);\n       return newDate;\n     }\n   \n     var tasks = [];\n     for(var i = 0; i < 24; i++) {\n       var t = new Task({name: \"Task \" + i, dateAdded: dateInDays(i)});\n       tasks.push(t);\n       coll.add(t);\n     }\n     coll.order('dateAdded', true).list(function(results) {\n         for(var i = 0; i < 24; i++) {\n           equals(results[i].id, tasks[i].id, \"order check, ascending\");\n         }\n         tasks.reverse();\n         coll.order('dateAdded', false).list(function(results) {\n             for(var i = 0; i < 24; i++) {\n               equals(results[i].id, tasks[i].id, \"order check, descending\");\n             }\n             callback();\n           });\n       });\n   }\n   \n   asyncTest(\"Database INT order\", function() {\n       var p = new Project({name: \"My project\"});\n       persistence.add(p);\n       intOrderTests($(p).data('tasks'), start);\n     });\n   \n   asyncTest(\"Local INT order\", function() {\n       var coll = new persistence.LocalQueryCollection();\n       intOrderTests(coll, start);\n     });         \n  \n  asyncTest(\"Database DATE order\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      dateOrderTests($(p).data('tasks'), start);\n    });\n  \n  asyncTest(\"Local DATE order\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      dateOrderTests(coll, start);\n    });\n   \n  function collectionLimitTests(coll, callback) {\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, counter: i});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order(\"counter\", true).limit(5).list(function(results) {\n        equals(results.length, 5, \"Result length check\");\n        for(var i = 0; i < 5; i++) {\n          equals(results[i].id, tasks[i].id, \"limit check\");\n        }\n        start();\n      });\n  }\n  \n  function collectionSkipTests(coll, callback) {\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, counter: i});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order(\"counter\", true).skip(5).limit(5).list(function(results) {\n        equals(results.length, 5, \"Result length check\");\n        for(var i = 5; i < 10; i++) {\n          equals(results[i-5].id, tasks[i].id, \"skip check\");\n        }\n        start();\n      });\n  }\n  \n  asyncTest(\"Database limit\", function() {\n      collectionLimitTests(Task.all(), start);\n    });\n  \n  asyncTest(\"Local limit\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      collectionLimitTests(coll, start);\n    });\n  \n  asyncTest(\"Database skip\", function() {\n      collectionSkipTests(Task.all(), start);\n    });\n  \n  asyncTest(\"Local skip\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      collectionSkipTests(coll, start);\n    });   \n});"
  },
  {
    "path": "test/browser/test.migrations.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \n                    \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n  <script src=\"qunit/jquery.js\"></script>\n  <link rel=\"stylesheet\" href=\"qunit/qunit.css\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\" src=\"qunit/qunit.js\"></script>\n\n  <script src=\"../../lib/persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.sql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.websql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.migrations.js\" type=\"application/javascript\"></script>\n  <script type=\"text/javascript\" src=\"util.js\"></script>\n  <script type=\"text/javascript\" src='test.migrations.js'></script>\n</head>\n<body>\n  <h1 id=\"qunit-header\">persistence.js migrations plugin</h1>\n  <h2 id=\"qunit-banner\"></h2>\n  <h2 id=\"qunit-userAgent\"></h2>\n  <ol id=\"qunit-tests\"></ol>\n</body>\n</html>\n\n"
  },
  {
    "path": "test/browser/test.migrations.js",
    "content": "function createMigrations(starting, amount, actions){\n  var amount = starting+amount;\n  \n  for (var i = starting; i < amount; i++) {\n    var newActions = {\n     up: actions.up,\n      down: actions.down\n    };\n    \n    if (actions.createDown)\n      newActions.down = actions.createDown(i);\n    \n    if (actions.createUp)\n      newActions.up = actions.createUp(i);\n    \n    persistence.defineMigration(i, newActions);\n  }\n}\n\nvar Migrator = persistence.migrations.Migrator;\n\n$(document).ready(function(){\n  persistence.store.websql.config(persistence, 'migrationstest', 'My db', 5 * 1024 * 1024);\n  persistence.debug = true;\n  \n  persistence.migrations.init(function() {\n    \nmodule(\"Migrator\", {\n  setup: function() {\n    \n  },\n  teardown: function() {\n    stop();\n    Migrator.reset(start);\n  }\n});\n\nasyncTest(\"getting and setting db version\", 2, function() {\n  Migrator.version(function(v){\n    equals(v, 0, 'initial db version');\n  });\n  \n  var newVersion = 100;\n  \n  Migrator.setVersion(newVersion, function() {\n    Migrator.version(function(v){\n      equals(v, newVersion, 'checking if version was set');\n      start();\n    });\n  });\n});\n\nasyncTest(\"migrations scope\", 2, function(){\n  var migration = Migrator.migration(1, {\n    up: function() {\n      same(this, migration, 'up');\n    },\n    down: function() {\n      same(this, migration, 'down');\n    }\n  });\n  \n  migration.up(function(){\n    migration.down(function(){\n      start();\n    });\n  });\n});\n\nasyncTest(\"migrating up to some version\", 7, function(){\n  var actionsRan = 0;\n  var totalActions = 5;\n  \n  createMigrations(1, totalActions, {\n    up: function() {\n      actionsRan++;\n      equals(this.version, actionsRan, 'running migration in order');\n    }\n  });\n  \n  Migrator.migrate(totalActions, function(){\n    equals(actionsRan, totalActions, 'actions ran');\n    Migrator.version(function(v){\n      equals(v, totalActions, 'version changed to');\n      start();\n    });\n  });\n});\n\nasyncTest(\"migrating down to some version\", 7, function(){\n  var actionsRan = 0;\n  var totalActions = 5;\n  \n  createMigrations(1, totalActions, {\n    createDown: function(i) {\n      var position = Math.abs(actionsRan - i);\n      return function () {\n        actionsRan++;\n        equals(this.version, position, 'running migration in order');\n      };\n    }\n  });\n  \n  Migrator.setVersion(totalActions, function(){\n    Migrator.migrate(0, function(){\n      equals(actionsRan, totalActions, 'actions ran');\n      Migrator.version(function(v){\n        equals(v, 0, 'version changed to');\n        start();\n      });\n    });\n  });\n});\n\nasyncTest(\"migrate to latest\", 1, function(){\n  var totalActions = 3;\n  \n  createMigrations(1, totalActions, { up: function() { } });\n  \n  Migrator.migrate(function() {\n    Migrator.version(function(v){\n      equals(v, totalActions, 'latest version');\n      start();\n    });\n  });\n});\n\nmodule(\"Migration\", {\n  setup: function() {\n    \n  },\n  teardown: function() {\n    stop();\n    // DROPS ALL TABLES\n    var query = \"select 'drop table ' || name || ';' AS dropTable from sqlite_master where type = 'table' and name not in ('__WebKitDatabaseInfoTable__', 'schema_version')\";\n    \n    persistence.transaction(function(tx){\n      tx.executeSql(query, null, function(result){\n        var dropTablesSql = [];\n        for (var i = 0; i < result.length; i++)\n          dropTablesSql.push([result[i].dropTable, null]);\n        \n        persistence.executeQueriesSeq(tx, dropTablesSql, function(){\n          Migrator.setVersion(0, function(){Migrator.reset(start);});\n        });\n      });\n    });\n  }\n});\n\nasyncTest(\"API\", 12, function(){\n  var m = Migrator.migration(1, {\n    up: function() {\n      ok(typeof(this.addColumn) == \"function\", 'addColumn');\n      ok(typeof(this.removeColumn) == \"function\", 'removeColumn');\n      ok(typeof(this.addIndex) == \"function\", 'addIndex');\n      ok(typeof(this.removeIndex) == \"function\", 'removeIndex');\n      ok(typeof(this.executeSql) == \"function\", 'execute');\n      ok(typeof(this.dropTable) == \"function\", 'dropTable');\n      ok(typeof(this.createTable) == \"function\", 'createTable');\n      \n      this.createTable('posts', function(table){\n        ok(typeof(table.text) == \"function\", 'text column');\n        ok(typeof(table.integer) == \"function\", 'integer column');\n        ok(typeof(table.boolean) == \"function\", 'boolean column');\n        ok(typeof(table.json) == \"function\", 'json column');\n        ok(typeof(table.date) == \"function\", 'date column');\n      });\n    }\n  });\n  \n  m.up(start);\n});\n\nasyncTest(\"execute\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.executeSql('CREATE TABLE test (id INTEGER)');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    var sql = 'select name from sqlite_master where type = \"table\" and name == \"test\"';\n    persistence.transaction(function(tx){\n      tx.executeSql(sql, null, function(result){\n        ok(result.length == 1, 'sql command ran');\n        start();\n      });\n    });\n  });\n});\n\nasyncTest(\"createTable\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('testing');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    tableExists('testing', start)\n  });\n});\n\nasyncTest(\"createTable adds id by default\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('testing');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('testing', 'id', 'VARCHAR(32) PRIMARY KEY', start);\n  });\n});\n\nasyncTest(\"createTable with text column\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.text('name');\n      });\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('customer', 'name', 'TEXT', start);\n  });\n});\n\nasyncTest(\"createTable with integer column\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.integer('age');\n      });\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('customer', 'age', 'INT', start);\n  });\n});\n\nasyncTest(\"createTable with boolean column\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.boolean('married');\n      });\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('customer', 'married', 'BOOL', start);\n  });\n});\n\nasyncTest(\"createTable with date column\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.date('birth');\n      });\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('customer', 'birth', 'DATE', start);\n  });\n});\n\nasyncTest(\"createTable with json column\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.json('sample_json');\n      });\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('customer', 'sample_json', 'TEXT', start);\n  });\n});\n\nasyncTest(\"addColumn\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer');\n      this.addColumn('customer', 'name', 'TEXT');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('customer', 'name', 'TEXT', start);\n  });\n});\n\nasyncTest(\"removeColumn\", 2, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.json('sample_json');\n      });\n      this.removeColumn('customer', 'sample_json');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    columnExists('customer', 'id', 'VARCHAR(32) PRIMARY KEY');\n    columnNotExists('customer', 'sample_json', 'TEXT', start);\n  });\n});\n\nasyncTest(\"dropTable\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer');\n      this.dropTable('customer');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    tableNotExists('customer', start);\n  });\n});\n\nasyncTest(\"addIndex\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.integer('age');\n      });\n      this.addIndex('customer', 'age');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    indexExists('customer', 'age', start);\n  });\n});\n\nasyncTest(\"removeIndex\", 1, function(){\n  Migrator.migration(1, {\n    up: function() {\n      this.createTable('customer', function(t){\n        t.integer('age');\n      });\n      this.addIndex('customer', 'age');\n      this.removeIndex('customer', 'age');\n    }\n  });\n  \n  Migrator.migrate(function(){\n    indexNotExists('customer', 'age', start);\n  });\n});\n\nmodule(\"Models\", {\n  setup: function() {\n    \n    stop();\n    \n    this.Task = persistence.define('Task', {\n      name: \"TEXT\",\n      description: \"TEXT\",\n      done: \"BOOL\"\n    });\n    \n    Migrator.migration(1, {\n      up: function() {\n        this.createTable('Task', function(t){\n          t.text('name');\n          t.text('description');\n          t.boolean('done');\n        });\n      },\n      down: function() {\n        this.dropTable('Task');\n      }\n    });\n    \n    Migrator.migrate(function(){\n      start();\n    });\n  },\n  teardown: function() {\n    stop();\n    \n    Migrator.migrate(0, function(){\n      start();\n    });\n  }\n});\n\nasyncTest(\"Adding and retrieving Entity after migration\", 1, function(){\n  var task = new this.Task({name: 'test'});\n  var allTasks = this.Task.all();\n  \n  persistence.add(task).flush(function() {\n    persistence.clean(); delete task;\n    allTasks.list(function(result){\n      equals(result.length, 1, 'task found');\n      start();\n    });\n  });\n});\n\nmodule(\"Custom actions\", {\n  setup: function() {\n    stop();\n    \n    this.User = persistence.define('User', {\n      userName: \"TEXT\",\n      email: \"TEXT\"\n    });\n    \n    Migrator.migration(1, {\n      up: function() {\n        this.createTable('User', function(t){\n          t.text('userName');\n        });\n      },\n      down: function() {\n        this.dropTable('User');\n      }\n    });\n    \n    Migrator.migrate(function(){\n      start();\n    });\n  },\n  teardown: function() {\n    stop();\n    \n    Migrator.migrate(0, function(){\n      start();\n    });\n  }\n});\n\n\nasyncTest(\"Running custom actions\", 1, function(){\n  var user1 = new this.User({userName: 'user1'});\n  var user2 = new this.User({userName: 'user2'});\n  var allUsers = this.User.all();\n  \n  function addUsers() {\n    persistence.add(user1).add(user2).flush(createAndRunMigration);\n  }\n\n  function createAndRunMigration() {\n    Migrator.migration(2, {\n      up: function() {\n        this.addColumn('User', 'email', 'TEXT');\n        this.action(function(tx, nextAction){\n          ok(true);\n          nextAction();\n        });\n      }\n    });\n    Migrator.migrate(start);\n  }\n  \n  addUsers();\n});\n    \n  }); // end persistence.migrations.init()\n});\n"
  },
  {
    "path": "test/browser/test.mixin.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \n                    \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n  <script src=\"qunit/jquery.js\"></script>\n  <link rel=\"stylesheet\" href=\"qunit/qunit.css\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\" src=\"qunit/qunit.js\"></script>\n\n  <script src=\"http://code.google.com/apis/gears/gears_init.js\"></script>\n  <script src=\"../../lib/persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.sql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.websql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.memory.js\" type=\"application/javascript\"></script>\n  <script type=\"text/javascript\" src='util.js'></script>\n  <script type=\"text/javascript\" src='test.mixin.js'></script>\n</head>\n<body>\n  <h1 id=\"qunit-header\">persistence.js mixin tests</h1>\n  <h2 id=\"qunit-banner\"></h2>\n  <h2 id=\"qunit-userAgent\"></h2>\n  <ol id=\"qunit-tests\"></ol>\n</body>\n</html>\n\n"
  },
  {
    "path": "test/browser/test.mixin.js",
    "content": "$(document).ready(function(){\n  persistence.store.websql.config(persistence, 'persistencetest', 'My db', 5 * 1024 * 1024);\n  //persistence.store.memory.config(persistence);\n  persistence.debug = true;\n  //persistence.debug = false;\n  \n  var startTime = new Date().getTime();\n  \n  var Project = persistence.define('Project', {\n    name: \"TEXT\"\n  });\n  \n  var Task = persistence.define('Task', {\n    name: \"TEXT\"\n  });\n  \n  var Tag = persistence.define('Tag', {\n    name: \"TEXT\"\n  });\n  \n  Task.hasMany('tags', Tag, 'tasks');\n  Tag.hasMany('tasks', Task, 'tags');\n  \n  Project.hasMany('tasks', Task, 'project');\n  \n  var Note = persistence.define('Note', {\n    text: \"TEXT\"\n  });\n  \n  var Annotatable = persistence.defineMixin('Annotatable', {\n    lastAnnotated: \"DATE\"\n  });\n  \n  Annotatable.hasMany('notes', Note, 'annotated');\n  \n  Task.hasMany('tags', Tag, 'tasks');\n  Tag.hasMany('tasks', Task, 'tags');\n  \n  Project.hasMany('tasks', Task, 'project');\n  \n  Task.is(Annotatable);\n  Project.is(Annotatable);\n  \n  var M1 = persistence.defineMixin('M1', {\n    seq: \"INT\",\n    m1: \"TEXT\"\n  });\n  \n  var M2 = persistence.defineMixin('M2', {\n    seq: \"INT\",\n    m2: \"TEXT\"\n  });\n  \n  M1.hasOne('oneM2', M2);\n  M1.hasMany('manyM2', M2, 'oneM1');\n  M1.hasMany('manyManyM2', M2, 'manyManyM1');\n  M2.hasMany('manyManyM1', M1, 'manyManyM2');\n  \n  var A1 = persistence.define('A1', {\n    a1: 'TEXT'\n  });\n  var A2 = persistence.define('A2', {\n    a2: 'TEXT'\n  });\n  var B1 = persistence.define('B1', {\n    b1: 'TEXT'\n  });\n  var B2 = persistence.define('B2', {\n    b2: 'TEXT'\n  });\n  \n  A1.is(M1);\n  A2.is(M2);\n  B1.is(M1);\n  B2.is(M2);\n  \n  window.Project = Project;\n  window.Task = Task\n  window.Project = Project;\n  \n  module(\"Setup\");\n  \n  asyncTest(\"setting up database\", 1, function(){\n    persistence.schemaSync(function(tx){\n      ok(true, 'schemaSync called callback function');\n      start();\n    });\n  });\n  \n  module(\"Annotatable mixin\", {\n      setup: function() {\n        stop();\n        persistence.reset(function() {\n            persistence.schemaSync(start);\n          });\n      }\n    });\n\n  \n    asyncTest(\"basic mixin\", 7, function(){\n      var now = new Date();\n      now.setMilliseconds(0);\n      \n      var p = new Project({\n        name: \"project p\"\n      });\n      persistence.add(p);\n      var n1 = new Note({\n        text: \"note 1\"\n      });\n      var n2 = new Note({\n        text: \"note 2\"\n      });\n      p.notes.add(n1);\n      n2.annotated = p;\n      p.lastAnnotated = now;\n      persistence.flush(function(){\n        Project.all().list(function(projects){\n          persistence.clean();\n          equals(projects.length, 1)\n          var p = projects[0];\n          p.notes.order('text', true).list(function(notes){\n            equals(notes.length, 2);\n            equals(notes[0].text, \"note 1\");\n            equals(notes[1].text, \"note 2\");\n            notes[0].fetch(\"annotated\", function(source){\n              equals(p.id, source.id);\n              equals(typeof source.lastAnnotated, typeof now);\n              equals(source.lastAnnotated.getTime(), now.getTime());\n              start();\n            });\n          });     \n        });\n      });\n    });\n  \n    asyncTest(\"many to many with mixins\", 17, function(){\n      var a1 = new A1({\n        seq: 1,\n        a1: \"a1\"\n      });\n      var b1 = new B1({\n        seq: 2,\n        b1: \"b1\"\n      });\n      var a2 = new A2({\n        seq: 3,\n        a2: \"a2\"\n      });\n      var a2x = new A2({\n        seq: 4,\n        a2: \"a2x\"\n      });\n      var a2y = new A2({\n        seq: 5,\n        a2: \"a2y\"\n      });\n      var b2x = new B2({\n        seq: 6,\n        b2: \"b2x\"\n      });\n      var b2y = new B2({\n        seq: 7,\n        b2: \"b2y\"\n      });\n      persistence.add(a1);\n      a1.oneM2 = b2x;\n      a1.manyM2.add(a2x);\n      a1.manyM2.add(b2x);\n      persistence.flush(function(){\n        persistence.clean();\n        A1.all().list(function(a1s){\n          equals(a1s.length, 1, \"A1 list ok\")\n          var a1 = a1s[0];\n          a1.fetch(\"oneM2\", function(m2){\n            ok(m2 != null, \"oneM2 not null\");\n            equals(m2.b2, \"b2x\", \"oneM2 ok\");\n            a1.manyM2.order('seq', true).list(function(m2s){\n              equals(m2s.length, 2, \"manyM2 length ok\");\n              equals(m2s[0].a2, \"a2x\", \"manyM2[0] ok\");\n              equals(m2s[1].b2, \"b2x\", \"manyM2[1] ok\");\n              m2s[1].fetch(\"oneM1\", function(m1){\n                ok(m1 != null, \"manyM2[1].oneM1 not null\");\n                ok(m1.a1, \"a1\", \"manyM2[1].oneM1 ok\");\n                a1.manyManyM2.add(a2x);\n                a1.manyManyM2.add(b2x);\n                persistence.add(b2y);\n                b2y.manyManyM1.add(a1);\n                b2y.manyManyM1.add(b1);\n                persistence.flush(function(){\n                  persistence.clean();\n                  A1.all().list(function(a1s){\n                    equals(a1s.length, 1, \"A1 list ok\")\n                    var a1 = a1s[0];\n                    a1.manyManyM2.order('seq', true).list(function(m2s){\n                      equals(m2s.length, 3, \"manyManyM2 length ok\");\n                      equals(m2s[0].a2, \"a2x\", \"manyManyM2[0] ok\");\n                      equals(m2s[1].b2, \"b2x\", \"manyManyM2[1] ok\");\n                      equals(m2s[2].b2, \"b2y\", \"manyManyM2[2] ok\");\n                      m2s[2].manyManyM1.order('seq', true).list(function(m1s){\n                        equals(m1s.length, 2, \"manyManyM1 length ok\");\n                        equals(m1s[0].a1, \"a1\", \"manyManyM1[0] ok\");\n                        equals(m1s[1].b1, \"b1\", \"manyManyM1[1] ok\");\n                        a1.manyManyM2.count(function(count){\n                          equals(count, 3, \"count ok on polymorphic list\");\n                          //a1.manyManyM2.destroyAll(function(){\n                          start();\n                        //})\n                        });\n                      })\n                    });\n                  });\n                })\n              });\n            });\n          });\n        });\n      });\n    });\n  \n\n});\n"
  },
  {
    "path": "test/browser/test.persistence.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \n                    \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n  <script src=\"qunit/jquery.js\"></script>\n  <link rel=\"stylesheet\" href=\"qunit/qunit.css\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\" src=\"qunit/qunit.js\"></script>\n\n  <script src=\"http://code.google.com/apis/gears/gears_init.js\"></script>\n  <script src=\"../../lib/persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.sql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.websql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.memory.js\" type=\"application/javascript\"></script>\n  <script type=\"text/javascript\" src='util.js'></script>\n  <script type=\"text/javascript\" src='test.persistence.js'></script>\n</head>\n<body>\n  <h1 id=\"qunit-header\">persistence.js core tests</h1>\n  <h2 id=\"qunit-banner\"></h2>\n  <h2 id=\"qunit-userAgent\"></h2>\n  <ol id=\"qunit-tests\"></ol>\n</body>\n</html>\n\n"
  },
  {
    "path": "test/browser/test.persistence.js",
    "content": "$(document).ready(function(){\n  persistence.store.websql.config(persistence, 'persistencetest', 'My db', 5 * 1024 * 1024);\n  //persistence.store.memory.config(persistence);\n  persistence.debug = true;\n  //persistence.debug = false;\n\n  var startTime = new Date().getTime();\n\n  var Project = persistence.define('Project', {\n      name: \"TEXT\"\n    });\n\n  var Task = persistence.define('Task', {\n      name: \"TEXT\",\n      done: \"BOOL\",\n      counter: \"INT\",\n      dateAdded: \"DATE\",\n      dateAddedInMillis: \"BIGINT\",\n      metaData: \"JSON\"\n    });\n\n  var Tag = persistence.define('Tag', {\n      name: \"TEXT\"\n    });\n    \n  var UniqueIndexTest = persistence.define('UniqueIndexTest', {\n      id1: \"INT\",\n      id2: \"INT\",\n      id3p1: \"INT\",\n      id3p2: \"INT\"\n    });\n  \n  UniqueIndexTest.index('id1');\n  UniqueIndexTest.index('id2',{unique:true});\n  UniqueIndexTest.index(['id3p1','id3p2'],{unique:true});\n\n\n  Task.hasMany('tags', Tag, 'tasks');\n  Tag.hasMany('tasks', Task, 'tags');\n  Task.index('dateAdded');\n\n  Project.hasMany('tasks', Task, 'project');\n\n  window.Project = Project;\n  window.Task = Task\n  window.Project = Project;\n  window.UniqueIndexTest = UniqueIndexTest;\n\n\n  module(\"Setup\");\n\n  asyncTest(\"setting up database\", 1, function() {\n      persistence.schemaSync(function(tx){\n          ok(true, 'schemaSync called callback function');\n          start();\n        });\n    });\n\n  module(\"Entity manipulation\", {\n      setup: function() {\n        stop();\n        persistence.reset(function() {\n            persistence.schemaSync(start);\n          });\n      }\n    });\n\n  test(\"Property default values\", 6, function() {\n      var t1 = new Task();\n      QUnit.strictEqual(t1.name, \"\", \"TEXT properties default to ''\");\n      QUnit.strictEqual(t1.done, false, \"BOOL properties default to false\");\n      QUnit.strictEqual(t1.counter, 0, \"INT properties default to 0\");\n      QUnit.strictEqual(t1.dateAdded, null, \"DATE properties default to null\");\n      QUnit.strictEqual(t1.dateAddedInMillis, 0, \"BIGINT properties default to 0\");\n      QUnit.strictEqual(t1.metaData, null, \"JSON properties default to null\");\n    });\n\n  test(\"Property value assignment\", 6, function() {\n      var t1 = new Task();\n      var now = new Date();\n      var meta = {starRating: 5};\n      t1.name = \"Task 1\";\n      t1.done = false;\n      t1.counter = 7;\n      t1.dateAdded = now;\n      t1.dateAddedInMillis = now.getTime();\n      t1.metaData = meta;\n      QUnit.strictEqual(t1.name, 'Task 1', \"Assignment for TEXT properties\");\n      QUnit.strictEqual(t1.done, false, \"Assignment for BOOL properties\");\n      QUnit.strictEqual(t1.counter, 7, \"Assignment for INT properties\");\n      QUnit.strictEqual(t1.dateAdded, now, \"Assignment for DATE properties\");\n      QUnit.strictEqual(t1.dateAddedInMillis, now.getTime(), \"Assignment for BIGINT properties\");\n      QUnit.strictEqual(t1.metaData, meta, \"Assignment for JSON properties\");\n    });\n\n  test(\"Property constructor property value assignment\", 6, function() {\n      var now = new Date();\n      var meta = {starRating: 5};\n      var t1 = new Task({\n          name: \"Task 1\",\n          done: false,\n          counter: 7,\n          dateAdded: now,\n          dateAddedInMillis: now.getTime(),\n          metaData: meta\n        });\n      QUnit.strictEqual(t1.name, 'Task 1', \"Assignment for TEXT properties\");\n      QUnit.strictEqual(t1.done, false, \"Assignment for BOOL properties\");\n      QUnit.strictEqual(t1.counter, 7, \"Assignment for INT properties\");\n      QUnit.strictEqual(t1.dateAdded, now, \"Assignment for DATE properties\");\n      QUnit.strictEqual(t1.dateAddedInMillis, now.getTime(), \"Assignment for BIGINT properties\");\n      QUnit.strictEqual(t1.metaData, meta, \"Assignment for JSON properties\");\n    });\n\n  asyncTest(\"Empty object persistence\", function() {\n      var t1 = new Task();\n      persistence.add(t1);\n      persistence.flush(function() {\n          //persistence.clean();\n          Task.all().one(function(t1db) {\n              equals(t1db.id, t1.id, \"TEXT properties default to ''\");\n              equals(t1db.name, \"\", \"TEXT properties default to ''\");\n              equals(t1db.done, false, \"BOOL properties default to false\");\n              equals(t1db.counter, 0, \"INT properties default to 0\");\n              equals(t1db.dateAdded, null, \"DATE properties default to null\");\n              equals(t1db.dateAddedInMillis, 0, \"BIGINT properties default to 0\");\n              equals(t1db.metaData, null, \"JSON properties default to null\");\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"Object persistence\", function() {\n      var now = new Date();\n      var meta = {starRating: 5};\n      var t1 = new Task({\n          name: \"Task 1\",\n          done: false,\n          counter: 7,\n          dateAdded: now,\n          dateAddedInMillis: 1296802544867,\n          metaData: meta\n        });\n      persistence.add(t1);\n      persistence.flush(function() {\n          persistence.clean();\n          Task.all().one(function(t1db) {\n              equals(t1db.name, 'Task 1', \"Persistence of TEXT properties\");\n              equals(t1db.done, false, \"Persistence of BOOL properties\");\n              equals(t1db.counter, 7, \"Persistence of INT properties\");\n              equals(Math.round(t1db.dateAdded.getTime()/1000)*1000, Math.round(now.getTime()/1000)*1000, \"Persistence of DATE properties\");\n              equals(t1db.dateAddedInMillis, 1296802544867, \"Persistence of BIGINT properties\");\n              same(t1db.metaData, meta, \"Persistence of JSON properties\");\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"Multiple objects\", function() {\n      var objs = [];\n      var counter = 0;\n      for(var i = 0; i < 25; i++) {\n        var t = new Task({name: \"Task \" + i});\n        t.counter = counter;\n        objs.push(t);\n        persistence.add(t);\n        counter++;\n      }\n      persistence.flush(function() {\n          Task.all().order('counter', true).list(function(results) {\n              for(var i = 0; i < 25; i++) {\n                ok(results[i] === objs[i], 'Cache works OK');\n              }\n              //persistence.clean(); // Clean out local cache\n              Task.all().order('counter', true).list(function(results) {\n                  for(var i = 0; i < 25; i++) {\n                    ok(results[i].id === objs[i].id, 'Retrieving from DB ok');\n                  }\n                  start();\n                });\n            });\n        });\n    });\n\n  asyncTest(\"Removing objects\", function() {\n      var originalTasks = [];\n      var counter = 0;\n      for(var i = 0; i < 25; i++) {\n        var t = new Task({name: \"Task \" + i});\n        t.counter = counter;\n        originalTasks.push(t);\n        persistence.add(t);\n        counter++;\n      }\n      for(var i = 0; i < 5; i++) {\n        persistence.remove(originalTasks[i]);\n      }\n      Task.all().order('counter', true).list(function(tasks) {\n          equals(tasks.length, 20, 'Correct count after removing local objects');\n          for(var i = 5; i < 25; i++) {\n            equals(tasks[i - 5].id, originalTasks[i].id, 'Correct objects after removing local objects');\n          }\n\n          for(var i = 5; i < 10; i++) {\n            persistence.remove(originalTasks[i]);\n          }\n          Task.all().order('counter', true).list(function(tasks) {\n              equals(tasks.length, 15, 'Correct count after removing persisted objects');\n              for(var i = 10; i < 25; i++) {\n                equals(tasks[i - 10].id, originalTasks[i].id, 'Correct objects after removing persisted objects');\n              }\n\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"One-to-many\", function() {\n      var p = new Project({name: \"Some project\"});\n      persistence.add(p);\n      p.tasks.list(function(tasks) {\n          equals(tasks.length, 0, \"Initially, no tasks\");\n          var task1 = new Task({name: \"Do dishes\"});\n          var task2 = new Task({name: \"Laundry\"});\n\n          // Adding in two ways\n          p.tasks.add(task1);\n          task2.project = p;\n\n          p.tasks.order('name', true).list(function(tasks) {\n              equals(tasks.length, 2, \"Now two tasks\");\n              equals(tasks[0].id, task1.id, \"Right tasks\");\n              equals(tasks[1].id, task2.id, \"Right tasks\");\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"Many-to-many\", function() {\n      var t = new Task({name: \"Some task\"});\n      persistence.add(t);\n      t.tags.list(function(tags) {\n          equals(tags.length, 0, \"Initially, no tags\");\n          var tag1 = new Tag({name: \"important\"});\n          var tag2 = new Tag({name: \"today\"});\n          t.tags.add(tag1);\n          t.tags.add(tag2);\n          t.tags.list(function(tags) {\n              equals(tags.length, 2, \"2 tags added\");\n              var oneTag = tags[0];\n              oneTag.tasks.list(function(tagTasks) {\n                  equals(tagTasks.length, 1, \"Tag has one task\");\n                  equals(tagTasks[0].id, t.id, \"Correct task\");\n                  oneTag.tasks.remove(tagTasks[0]);\n                  t.tags.count(function(cnt) {\n                      equals(cnt, 1, \"Tag removed task, task has only one tag left\");\n                      start();\n                    });\n                });\n            });\n        });\n    });\n\n  asyncTest(\"Many-to-many with local changes\", function() {\n      var t = new Task({name: \"Some task\"});\n      persistence.add(t);\n      t.tags.list(function(tags) {\n          equals(tags.length, 0, \"Initially, no tags\");\n          var tag1 = new Tag({name: \"important\"});\n          var tag2 = new Tag({name: \"today\"});\n          t.tags.add(tag1);\n          t.tags.add(tag2);\n          t.tags.remove(tag1);\n          t.tags.list(function(tags) {\n              equals(tags.length, 1, \"2 tags added, 1 removed\");\n              equals(tags[0].id, tag2.id, \"Correct tag left\");\n              start();\n            });\n        });\n    });\n\n  module(\"Query collections\", {\n      setup: function() {\n        stop();\n        persistence.reset(function() {\n            persistence.schemaSync(start);\n          });\n      }\n    });\n\n  function intFilterTests(coll, callback) {\n    for(var i = 0; i < 25; i++) {\n      var t = new Task({name: \"Task \" + i, done: false});\n      t.counter = i;\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 25, \"Count items in collection\");\n        coll.filter(\"counter\", \">\", 10).list(function(results) {\n            equals(results.length, 14, \"> filter test\");\n            coll.filter(\"counter\", \"in\", [0, 1, 2]).list(function(results) {\n                equals(results.length, 3, \"'in' filter test\");\n                coll.filter(\"counter\", \"not in\", [0, 1]).list(function(results) {\n                    equals(results.length, 23, \"'not in' filter test\");\n                    coll.filter(\"counter\", \"!=\", 0).list(function(results) {\n                        equals(results.length, 24, \"'!=' filter test\");\n                        callback();\n                      });\n                  });\n              });\n          });\n      });\n  }\n\n  function textFilterTests(coll, callback) {\n    var alphabet = 'abcdefghijklmnopqrstufwxyz';\n    for(var i = 0; i <= 25; i++) {\n      var t = new Task({name: alphabet[i]});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 26, \"Count items in collection\");\n        coll.filter(\"name\", \"=\", 'a').list(function(results) {\n            equals(results.length, 1, \"= filter test\");\n            coll.filter(\"name\", \"!=\", 'a').list(function(results) {\n                equals(results.length, 25, \"!= filter test\");\n                coll.filter(\"name\", \">\", 'm').list(function(results) {\n                    equals(results.length, 12, \"> filter test\");\n                    coll.filter(\"name\", \"in\", [\"a\", \"b\"]).list(function(results) {\n                        equals(results.length, 2, \"'in' filter test\");\n                        coll.filter(\"name\", \"not in\", [\"q\", \"x\"]).list(function(results) {\n                            equals(results.length, 24, \"'not in' filter test\");\n                            callback();\n                          });\n                      });\n                  });\n              });\n          });\n      });\n  }\n\n  function boolFilterTests(coll, callback) {\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, done: i % 2 === 0});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 24, \"Count items in collection\");\n        coll.filter(\"done\", \"=\", true).list(function(results) {\n            equals(results.length, 12, \"= filter test\");\n            coll.filter(\"done\", \"=\", false).list(function(results) {\n                equals(results.length, 12, \"= filter test\");\n                coll.filter(\"done\", \"!=\", true).list(function(results) {\n                    equals(results.length, 12, \"'!=' filter test\");\n                    coll.filter(\"done\", \"!=\", false).list(function(results) {\n                        equals(results.length, 12, \"'!=' filter test\");\n                        callback();\n                      });\n                  });\n              });\n          });\n      });\n  }\n\n  function dateFilterTests(coll, callback) {\n    var now = new Date();\n\n    function dateInDays(n) {\n      var newDate = new Date(now.getTime());\n      newDate.setDate(newDate.getDate()+n);\n      return newDate;\n    }\n\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, dateAdded: dateInDays(i)});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 24, \"Count items in collection\");\n        coll.filter(\"dateAdded\", \"=\", dateInDays(1)).list(function(results) {\n            equals(results.length, 1, \"= filter test\");\n            coll.filter(\"dateAdded\", \"!=\", dateInDays(1)).list(function(results) {\n                equals(results.length, 23, \"!= filter test\");\n                coll.filter(\"dateAdded\", \">\", dateInDays(12)).list(function(results) {\n                    equals(results.length, 11, \"> filter test\");\n                    start();\n                  });\n              })\n          });\n      });\n  }\n\n  asyncTest(\"Database INT filters\", function() {\n      for(var i = 0; i < 25; i++) {\n        var t = new Task({name: \"Root task \" + i, done: false});\n        t.counter = i;\n        persistence.add(t);\n      }\n\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      intFilterTests(p.tasks, start);\n    });\n\n  asyncTest(\"Local INT filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      intFilterTests(coll, start);\n    });\n\n  asyncTest(\"Database TEXT filters\", function() {\n      var alphabet = 'abcdefghijklmnopqrstufwxyz';\n      for(var i = 0; i <= 25; i++) {\n        var t = new Task({name: alphabet[i]});\n        persistence.add(t);\n      }\n\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      textFilterTests(p.tasks, start);\n    });\n\n  asyncTest(\"Local TEXT filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      textFilterTests(coll, start);\n    });\n\n  asyncTest(\"Database BOOL filters\", function() {\n      for(var i = 0; i < 25; i++) {\n        var t = new Task({name: \"Root task \" + i, done: false});\n        t.counter = i;\n        persistence.add(t);\n      }\n\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      boolFilterTests(p.tasks, start);\n    });\n\n  asyncTest(\"Local BOOL filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      boolFilterTests(coll, start);\n    });\n\n  asyncTest(\"Database DATE filters\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      dateFilterTests(p.tasks, start);\n    });\n\n  asyncTest(\"Local DATE filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      dateFilterTests(coll, start);\n    });\n\n\n  function intOrderTests(coll, callback) {\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, counter: i});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order('counter', true).list(function(results) {\n        for(var i = 0; i < 24; i++) {\n          equals(results[i].id, tasks[i].id, \"order check, ascending\");\n        }\n        tasks.reverse();\n        coll.order('counter', false).list(function(results) {\n            for(var i = 0; i < 24; i++) {\n              equals(results[i].id, tasks[i].id, \"order check, descending\");\n            }\n            callback();\n          });\n      });\n  }\n\n  function textOrderTests(coll, callback) {\n    var taskNames = ['Task A', 'task b', 'Task C', 'task d'];\n    var tasks = [];\n    for(var i = 0; i < taskNames.length; i++) {\n      var t = new Task({name: taskNames[i]});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order('name', true, true).list(function(results) {\n        var expectedIndices = [0, 2, 1, 3];\n        for(var i = 0; i < tasks.length; i++) {\n          equals(results[i].id, tasks[expectedIndices[i]].id, \"order check, ascending, case sensitive\");\n        }\n        coll.order('name', true, false).list(function(results) {\n            for(var i = 0; i < tasks.length; i++) {\n              equals(results[i].id, tasks[i].id, \"order check, ascending, case insensitive\");\n            }\n            callback();\n          });\n      });\n  }\n\n  function dateOrderTests(coll, callback) {\n    var now = new Date();\n\n    function dateInDays(n) {\n      var newDate = new Date(now.getTime());\n      newDate.setDate(newDate.getDate()+n);\n      return newDate;\n    }\n\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, dateAdded: dateInDays(i)});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order('dateAdded', true).list(function(results) {\n        for(var i = 0; i < 24; i++) {\n          equals(results[i].id, tasks[i].id, \"order check, ascending\");\n        }\n        tasks.reverse();\n        coll.order('dateAdded', false).list(function(results) {\n            for(var i = 0; i < 24; i++) {\n              equals(results[i].id, tasks[i].id, \"order check, descending\");\n            }\n            callback();\n          });\n      });\n  }\n\n  asyncTest(\"Database INT order\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      intOrderTests(p.tasks, start);\n    });\n\n  asyncTest(\"Local INT order\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      intOrderTests(coll, start);\n    });\n\n  asyncTest(\"Database TEXT order\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      textOrderTests(p.tasks, start);\n    });\n\n  asyncTest(\"Local TEXT order\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      textOrderTests(coll, start);\n    });\n\n  asyncTest(\"Database DATE order\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      dateOrderTests(p.tasks, start);\n    });\n\n  asyncTest(\"Local DATE order\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      dateOrderTests(coll, start);\n    });\n\n  function collectionLimitTests(coll, callback) {\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, counter: i});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order(\"counter\", true).limit(5).list(function(results) {\n        equals(results.length, 5, \"Result length check\");\n        for(var i = 0; i < 5; i++) {\n          equals(results[i].id, tasks[i].id, \"limit check\");\n        }\n        start();\n      });\n  }\n\n  function collectionSkipTests(coll, callback) {\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, counter: i});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order(\"counter\", true).skip(5).limit(5).list(function(results) {\n        equals(results.length, 5, \"Result length check\");\n        for(var i = 5; i < 10; i++) {\n          equals(results[i-5].id, tasks[i].id, \"skip check\");\n        }\n        start();\n      });\n  }\n\n  asyncTest(\"Database limit\", function() {\n      collectionLimitTests(Task.all(), start);\n    });\n\n  asyncTest(\"Local limit\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      collectionLimitTests(coll, start);\n    });\n\n  asyncTest(\"Database skip\", function() {\n      collectionSkipTests(Task.all(), start);\n    });\n\n  asyncTest(\"Local skip\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      collectionSkipTests(coll, start);\n    });\n\n  module(\"Dumping/restoring\");\n\n  asyncTest(\"Full dump/restore\", function() {\n      persistence.reset(function() {\n          persistence.schemaSync(function() {\n              for(var i = 0; i < 10; i++) {\n                var t = new Task({name: \"Task \" + i, dateAdded: new Date()});\n                t.tags.add(new Tag({name: \"Some tag: \" + i}));\n                t.tags.add(new Tag({name: \"Another tag: \" + i}));\n                persistence.add(t);\n              }\n              persistence.flush(function() {\n                  persistence.dumpToJson(function(dumps) {\n                      persistence.reset(function() {\n                          persistence.schemaSync(function() {\n                              persistence.loadFromJson(dumps, function() {\n                                  Task.all().list(function(tasks) {\n                                      equals(tasks.length, 10, \"tasks restored successfully\");\n                                      tasks[0].tags.list(function(tags) {\n                                          equals(tags.length, 2, \"tags restored successfully\");\n                                          start();\n                                        });\n                                    });\n                                });\n                            });\n                        });\n                    });\n                });\n            });\n        });\n    });\n\n  asyncTest(\"Select dump/restore\", function() {\n      persistence.reset(function() {\n          persistence.schemaSync(function() {\n              var project = new Project({name: \"My project\"});\n              persistence.add(project);\n              var tags = [];\n              for(var i = 0; i < 5; i++) {\n                var tag = new Tag({name: \"Tag \" + i});\n                persistence.add(tag);\n                tags.push(tag);\n              }\n              for(var i = 0; i < 10; i++) {\n                var task = new Task({name: \"Task \" + i});\n                task.done = true;\n                task.tags = new persistence.LocalQueryCollection(tags);\n                project.tasks.add(task);\n              }\n              Project.all().selectJSON(['id', 'name', 'tasks.[id,name]', 'tasks.tags.[id, name]'], function(result) {\n                  persistence.reset(function() {\n                      persistence.schemaSync(function() {\n                          Project.fromSelectJSON(result[0], function(obj) {\n                              persistence.add(obj);\n                              Task.all().list(function(tasks) {\n                                  equals(tasks.length, 10, \"number of restored tasks ok\");\n                                  tasks.forEach(function(task) {\n                                      equals(task.done, false, \"done still default value\");\n                                    });\n                                  start();\n                                  console.log(new Date().getTime() - startTime);\n                                });\n                            });\n                        });\n                    });\n                });\n            });\n        });\n    });\n\n  asyncTest(\"AND and OR filters\", function() {\n      persistence.reset(function() {\n          persistence.schemaSync(function() {\n              for(var i = 0; i < 10; i++) {\n                var task = new Task({name: \"Task \" + i});\n                task.done = i % 2 === 0;\n                persistence.add(task);\n              }\n              Task.all().filter(\"done\", \"=\", true).or(new persistence.PropertyFilter(\"done\", \"=\", false)).list(function(results) {\n                  equals(results.length, 10, \"right number of results\");\n                  Task.all().filter(\"done\", \"=\", true).and(new persistence.PropertyFilter(\"done\", \"=\", false)).list(function(results) {\n                      equals(results.length, 0, \"right number of results\");\n                      start();\n                    });\n                });\n            });\n        });\n    });\n\n  module(\"Events\");\n\n  asyncTest(\"all collection\", function() {\n      persistence.reset(function() {\n          persistence.schemaSync(function() {\n              var allTasks = Task.all();\n              var changesDetected = 0;\n              allTasks.addEventListener('change', function() {\n                  changesDetected++;\n                });\n              for(var i = 0; i < 10; i++) {\n                var task = new Task({name: \"Task \" + i});\n                task.done = i % 2 === 0;\n                Task.all().add(task);\n              }\n              equals(10, changesDetected, \"detected all changes\");\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"filter collection\", function() {\n      persistence.reset(function() {\n          persistence.schemaSync(function() {\n              var allTasks = Task.all().filter(\"done\", \"=\", true);\n              var changesDetected = 0;\n              allTasks.addEventListener('change', function() {\n                  changesDetected++;\n                });\n              for(var i = 0; i < 10; i++) {\n                var task = new Task({name: \"Task \" + i});\n                task.done = i % 2 === 0;\n                Task.all().add(task);\n              }\n              equals(5, changesDetected, \"detected all changes\");\n              changesDetected = 0;\n              Task.all().filter(\"done\", \"=\", true).list(function(results) {\n                  results.forEach(function(r) {\n                      r.done = false;\n                    });\n                  equals(5, changesDetected, \"detected filter changes\");\n                  start();\n                });\n            });\n        });\n    });\n    \n    \n    \n    module(\"Indexes\");\n    \n    \n    asyncTest(\"unique indexes\", function() {\n        \n        persistence.reset(function() {\n            \n            persistence.schemaSync(function() {\n                \n                var o1 = new UniqueIndexTest({\"id1\":101,\"id2\":102,\"id3p1\":103,\"id3p2\":104});\n                \n                // id1 is not unique\n                var o2 = new UniqueIndexTest({\"id1\":101,\"id2\":202,\"id3p1\":203,\"id3p2\":204});\n                \n                //shouldn't work, id2 is unique\n                var o3 = new UniqueIndexTest({\"id1\":301,\"id2\":102,\"id3p1\":303,\"id3p2\":304});\n                \n                // id3p1 itself is not unique\n                var o4 = new UniqueIndexTest({\"id1\":401,\"id2\":402,\"id3p1\":103,\"id3p2\":404});\n                \n                //shouldn't work, id3p1+id3p2 are unique\n                var o5 = new UniqueIndexTest({\"id1\":501,\"id2\":502,\"id3p1\":103,\"id3p2\":104});\n                \n                \n                persistence.add(o1);\n                persistence.add(o2);\n                try {\n                    //persistence.add(o3);\n                } catch (e) {\n                    console.log(\"err\",e);\n                }\n                \n                persistence.add(o4);\n                try {\n                    //persistence.add(o5);\n                } catch (e) {\n                    console.log(\"err\",e);\n                }\n                \n                \n                UniqueIndexTest.all().order(\"id2\",true).list(function(results) {\n                    equals(3,results.length,\"skipped 2 duplicate rows\");\n                    if (results.length==3) {\n                        equals(102,results[0].id2);\n                        equals(202,results[1].id2);\n                        equals(402,results[2].id2);\n                    }\n                    start();\n                });\n              });\n          });\n      });\n    \n});\n"
  },
  {
    "path": "test/browser/test.search.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \n                    \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n  <script src=\"qunit/jquery.js\"></script>\n  <link rel=\"stylesheet\" href=\"qunit/qunit.css\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\" src=\"qunit/qunit.js\"></script>\n\n  <script src=\"http://code.google.com/apis/gears/gears_init.js\"></script>\n  <script src=\"../../lib/persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.sql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.websql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.search.js\" type=\"application/javascript\"></script>\n  <script type=\"text/javascript\" src='util.js'></script>\n  <script type=\"text/javascript\" src='test.search.js'></script>\n</head>\n<body>\n  <h1 id=\"qunit-header\">persistence.js search tests</h1>\n  <h2 id=\"qunit-banner\"></h2>\n  <h2 id=\"qunit-userAgent\"></h2>\n  <ol id=\"qunit-tests\"></ol>\n</body>\n</html>\n\n"
  },
  {
    "path": "test/browser/test.search.js",
    "content": "$(document).ready(function(){\n  persistence.store.websql.config(persistence, 'searchtest', 'My db', 5 * 1024 * 1024);\n  persistence.search.config(persistence, persistence.store.websql.sqliteDialect);\n  persistence.debug = true;\n\n  var Note = persistence.define('Note', {\n      title: \"TEXT\",\n      text: \"TEXT\"\n    });\n\n  Note.textIndex('title');\n  Note.textIndex('text');\n\n  module(\"Setup\");\n\n  asyncTest(\"setting up database\", 1, function() {\n      persistence.reset(function() {\n          persistence.schemaSync(function(tx){\n              ok(tx.executeSql, 'schemaSync passed transaction as argument to callback');\n              start();\n            });\n        });\n    });\n\n  module(\"Search test\");\n\n  asyncTest(\"Create some sample data\", function() {\n      persistence.add(new Note({title: \"My first note\", text: \"This is my first note. It has a rather high duplication quotient, or whatever.\"}));\n      persistence.add(new Note({title: \"My second note\", text: \"This is my second note. Isn't it a cool note? Third, fourth.\"}));\n      persistence.add(new Note({title: \"My third note\", text: \"Nothing here yet\"}));\n      persistence.add(new Note({title: \"Unrelated\", text: \"Under contruction.\"}));\n      persistence.flush(function() {\n          start();\n        });\n    });\n\n  asyncTest(\"Searching\", function() {\n      Note.search(\"note\").list(function(results) {\n          equals(results.length, 3, \"returned correct number of results\");\n          equals(results[0].title, \"My second note\", \"Found most relevant result\");\n          Note.search(\"title: third\").list(function(results) {\n              equals(results.length, 1, \"returned correct number of results\");\n              equals(results[0].title, \"My third note\", \"Searched in only title\");\n              Note.search(\"thi*\").list(function(results) {\n                  equals(results.length, 3, \"wildcard search\");\n                  Note.search(\"thi*\").limit(1).list(function(results) {\n                      equals(results.length, 1, \"limit number of search results\");\n                      start();\n                    });\n                });\n            });\n        });\n    });\n\n});\n"
  },
  {
    "path": "test/browser/test.sync.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \n                    \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n  <script src=\"qunit/jquery.js\"></script>\n  <link rel=\"stylesheet\" href=\"qunit/qunit.css\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\" src=\"qunit/qunit.js\"></script>\n\n  <script src=\"persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"persistence.store.sql.js\" type=\"application/javascript\"></script>\n  <script src=\"persistence.store.websql.js\" type=\"application/javascript\"></script>\n  <script src=\"persistence.store.memory.js\" type=\"application/javascript\"></script>\n  <script src=\"persistence.sync.js\" type=\"application/javascript\"></script>\n  <script type=\"text/javascript\" src='util.js'></script>\n  <script type=\"text/javascript\" src='test.sync.js'></script>\n</head>\n<body>\n  <h1 id=\"qunit-header\">persistence.js sync tests</h1>\n  <h2 id=\"qunit-banner\"></h2>\n  <h2 id=\"qunit-userAgent\"></h2>\n  <ol id=\"qunit-tests\"></ol>\n</body>\n</html>\n\n"
  },
  {
    "path": "test/browser/test.sync.js",
    "content": "$(document).ready(function(){\n  persistence.store.websql.config(persistence, 'persistencetest', 'My db', 5 * 1024 * 1024);\n  //persistence.store.memory.config(persistence);\n  persistence.debug = true;\n\n  var Project = persistence.define('Project', {\n      name: \"TEXT\"\n    });\n\n  var Task = persistence.define('Task', {\n      name: \"TEXT\",\n      done: \"BOOL\"\n    });\n\n  var Tag = persistence.define('Tag', {\n      name: \"TEXT\"\n    });\n\n  Task.hasMany('tags', Tag, 'tasks');\n  Tag.hasMany('tasks', Task, 'tags');\n\n  Project.hasMany('tasks', Task, 'project');\n\n  Task.enableSync('/taskUpdates');\n  Project.enableSync('/projectUpdates');\n  Tag.enableSync('/tagUpdates');\n\n  module(\"Setup\");\n\n  asyncTest(\"setting up local database\", function() {\n      persistence.reset(function() {\n          persistence.schemaSync(function(){\n              ok(true, 'came back from schemaSync');\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"setting up remote database\", 1, function() {\n      persistence.sync.getJSON('/reset', function(data) {\n          same(data, {status: 'ok'}, \"Remote reset\");\n          start();\n        });\n    });\n\n  module(\"Sync\");\n\n  function noConflictsHandler(conflicts, updatesToPush, callback) {\n    ok(false, \"Should not go to conflict resolving\");\n    console.log(\"Conflicts: \", conflicts);\n    callback();\n  }\n\n  asyncTest(\"initial sync of project\", function() {\n      Project.syncAll(noConflictsHandler, function() {\n          ok(true, \"Came back from sync\");\n          Project.syncAll(noConflictsHandler, function() {\n              ok(true, \"Came back from second sync\");\n              Project.all().list(function(projects) {\n                  equals(projects.length, 1, \"1 project synced\");\n                  var p = projects[0];\n                  equals(p.name, \"Main project\", \"project name\");\n                  start();\n                });\n            });\n        });\n    });\n\n  asyncTest(\"initial sync of tasks\", function() {\n      Task.syncAll(noConflictsHandler, function() {\n          ok(true, \"Came back from sync\");\n          Task.syncAll(noConflictsHandler, function() {\n              ok(true, \"Came back from second sync\");\n              Task.all().list(function(tasks) {\n                  equals(tasks.length, 25, \"25 tasks synced\");\n                  tasks.forEach(function(task) {\n                      equals(false, task.done, \"task not done\");\n                    });\n                  start();\n                });\n            });\n        });\n    });\n\n  asyncTest(\"setting some tasks to done and syncing again\", function() {\n      Task.all().list(function(tasks) {\n          for(var i = 0; i < tasks.length; i++) {\n            if(i % 2 === 0) {\n              tasks[i].done = true;\n            }\n          }\n          Task.syncAll(noConflictsHandler, function() {\n              ok(true, \"Came back from sync\");\n              start();\n            });\n        });\n    });\n\n  function resetResync(callback) {\n    persistence.reset(function() {\n        persistence.schemaSync(function() {\n            ok(true, \"Database reset\");\n\n            Project.syncAll(noConflictsHandler, function() {\n                ok(true, \"Came back from project sync\");\n                Task.syncAll(noConflictsHandler, function() {\n                    ok(true, \"Came back from task sync\");\n                    callback();\n                  });\n              });\n          });\n      });\n  }\n\n  asyncTest(\"resetting local db and resyncing\", function() {\n      resetResync(function() {\n          Task.all().filter(\"done\", \"=\", true).count(function(n) {\n              equals(n, 13, \"right number of tasks done\");\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"creating some new objects\", function() {\n      var p = new Project({name: \"Locally created project\"});\n      persistence.add(p);\n      for(var i = 0; i < 10; i++) {\n        var t = new Task({name: \"Local task \" + i});\n        p.tasks.add(t);\n      }\n      persistence.flush(function() {\n          ok(true, \"project and tasks added locally\");\n          Project.syncAll(noConflictsHandler, function() {\n              ok(true, \"returned from project sync\");\n              Task.syncAll(noConflictsHandler, function() {\n                  ok(true, \"returned from task sync\");\n                  p.tasks.list(function(tasks) {\n                      equals(tasks.length, 10, 'check collection size');\n                      tasks.forEach(function(task) {\n                          task.done = true;\n                        });\n                      Task.syncAll(noConflictsHandler, function() {\n                          start();\n                        });\n                    });\n                });\n            });\n        });\n    });\n\n  asyncTest(\"resetting local db and resyncing\", function() {\n      resetResync(function() {\n          Task.all().filter(\"done\", \"=\", true).count(function(n) {\n              equals(n, 23, \"right number of tasks done.\");\n              start();\n            });\n        });\n    });\n\n  asyncTest(\"marking all tasks done remotely\", function() {\n      persistence.sync.getJSON('/markAllDone', function(data) {\n          same(data, {status: 'ok'}, \"Remote reset\");\n          Task.syncAll(noConflictsHandler, function() {\n              ok(true, \"Came back from sync\");\n              Task.all().filter(\"done\", \"=\", true).count(function(n) {\n                  equals(35, n, \"all tasks were marked done and synced correctly\");\n                  start();\n                });\n            });\n        });\n    });\n\n  module(\"Conflicts\");\n\n  asyncTest(\"prefer local conflict handler\", 8, function() {\n      persistence.sync.getJSON('/markAllUndone', function(data) {\n          same(data, {status: 'ok'}, \"Remote marking undone\");\n          Task.all().list(function(tasks) {\n              for(var i = 0; i < tasks.length; i++) {\n                if(i % 2 === 0) {\n                  // Force a dirty flag\n                  tasks[i].done = true;\n                  tasks[i].done = false;\n                  tasks[i].done = true;\n                }\n              }\n              persistence.flush(function() {\n                  Task.syncAll(function(conflicts, updatesToPush, callback) {\n                      ok(true, \"Conflict resolver called\");\n                      equals(conflicts.length, 18, \"Number of conflicts\");\n                      console.log(\"Conflicts: \", conflicts);\n                      persistence.sync.preferLocalConflictHandler(conflicts, updatesToPush, callback);\n                    }, function() {\n                      ok(true, \"Came back from sync\");\n                      resetResync(function() {\n                          Task.all().filter(\"done\", \"=\", true).list(function(tasks) {\n                              equals(tasks.length, 18, \"Conflicts were properly resolved towards the server\");\n                              start();\n                            });\n                        });\n                    });\n                });\n            });\n        });\n    });\n\n  asyncTest(\"prefer remote conflict handler\", 5, function() {\n      persistence.sync.getJSON('/markAllUndone', function(data) {\n          same(data, {status: 'ok'}, \"Remote marking undone\");\n          Task.all().list(function(tasks) {\n              for(var i = 0; i < tasks.length; i++) {\n                if(i % 2 === 0) {\n                  // Force a dirty flag\n                  tasks[i].done = true;\n                  tasks[i].done = false;\n                  tasks[i].done = true;\n                }\n              }\n              persistence.flush(function() {\n                  Task.syncAll(function(conflicts, updatesToPush, callback) {\n                      ok(true, \"Conflict resolver called\");\n                      equals(conflicts.length, 18, \"Number of conflicts\");\n                      console.log(\"Conflicts: \", conflicts);\n                      persistence.sync.preferRemoteConflictHandler(conflicts, updatesToPush, callback);\n                    }, function() {\n                      ok(true, \"Came back from sync\");\n                      Task.all().filter(\"done\", \"=\", true).list(function(tasks) {\n                          equals(tasks.length, 0, \"Conflicts were properly resolved\");\n                          start();\n                        });\n                    });\n                });\n            });\n        });\n    });\n\n  asyncTest(\"Object removal\", function() {\n      Task.all().list(function(tasks) {\n          for(var i = 0; i < tasks.length; i++) {\n            if(i % 2 === 0) {\n              persistence.remove(tasks[i]);\n            }\n          }\n\n          persistence.flush(function() {\n              console.log(\"Now going to sync\");\n              Task.syncAll(noConflictsHandler, function() {\n                  //Task.syncAll(noConflictsHandler, function() {\n                      start();\n                    //});\n                });\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/browser/test.uki-persistence.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \n                    \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n<head>\n  <script src=\"qunit/jquery.js\"></script>\n  <link rel=\"stylesheet\" href=\"qunit/qunit.css\" type=\"text/css\" media=\"screen\" />\n  <script type=\"text/javascript\" src=\"qunit/qunit.js\"></script>\n\n  <script src=\"http://code.google.com/apis/gears/gears_init.js\"></script>\n  <script src=\"../../lib/persistence.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.sql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.websql.js\" type=\"application/javascript\"></script>\n  <script src=\"../../lib/persistence.store.memory.js\" type=\"application/javascript\"></script>\n  <script type=\"text/javascript\" src='util.js'></script>           \n  <script type=\"text/javascript\" src='http://static.ukijs.org/pkg/0.3.5/uki.js'></script>\n  <script type=\"text/javascript\" src='uki/uki-persistence.js'></script>\n  <script type=\"text/javascript\" src='test.uki-persistence.js'></script>\n</head>\n<body>\n  <h1 id=\"qunit-header\">persistence.js core tests with uki MVC framework</h1>\n  <h2 id=\"qunit-banner\"></h2>\n  <h2 id=\"qunit-userAgent\"></h2>\n  <ol id=\"qunit-tests\"></ol>\n</body>\n</html>\n\n"
  },
  {
    "path": "test/browser/test.uki-persistence.js",
    "content": "$(document).ready(function(){\n  persistence.store.websql.config(persistence, 'persistencetest', 'My db', 5 * 1024 * 1024);\n  //persistence.store.memory.config(persistence);\n  persistence.debug = true;\n\n  var Project = persistence.define('Project', {\n      name: \"TEXT\"\n    });\n\n  var Task = persistence.define('Task', {\n      name: \"TEXT\",\n      done: \"BOOL\",\n      counter: \"INT\",\n      dateAdded: \"DATE\",\n      metaData: \"JSON\"\n    });\n\n  var Tag = persistence.define('Tag', {\n      name: \"TEXT\"\n    });\n\n  Task.hasMany('tags', Tag, 'tasks');\n  Tag.hasMany('tasks', Task, 'tags');\n\n  Project.hasMany('tasks', Task, 'project');\n\n  window.Project = Project;\n  window.Task = Task\n  window.Project = Project;\n\n  module(\"Setup\");\n  \n  asyncTest(\"setting up database\", 1, function() {\n    persistence.schemaSync(function(tx){\n      ok(true, 'schemaSync called callback function');\n      start();\n    });\n  });\n\n  module(\"Entity manipulation\", {\n    setup: function() {\n      stop();\n      persistence.reset(function() {\n          persistence.schemaSync(start);\n        });\n    }\n  });\n\n  test(\"Property default values\", 5, function() {\n    var t1 = new Task();\n    QUnit.strictEqual(t1.name(), \"\", \"TEXT properties default to ''\");\n    QUnit.strictEqual(t1.done(), false, \"BOOL properties default to false\");\n    QUnit.strictEqual(t1.counter(), 0, \"INT properties default to 0\");\n    QUnit.strictEqual(t1.dateAdded(), null, \"DATE properties default to null\");\n    QUnit.strictEqual(t1.metaData(), null, \"JSON properties default to null\");\n  });\n\n  test(\"Property value assignment\", 5, function() {\n    var t1 = new Task();\n    var now = new Date();\n    var meta = {starRating: 5};\n    t1.name(\"Task 1\");\n    t1.done(false);\n    t1.counter(7);\n    t1.dateAdded(now);\n    t1.metaData(meta);\n    QUnit.strictEqual(t1.name(), 'Task 1', \"Assignment for TEXT properties\");\n    QUnit.strictEqual(t1.done(), false, \"Assignment for BOOL properties\");\n    QUnit.strictEqual(t1.counter(), 7, \"Assignment for INT properties\");\n    QUnit.strictEqual(t1.dateAdded(), now, \"Assignment for DATE properties\");\n    QUnit.strictEqual(t1.metaData(), meta, \"Assignment for JSON properties\");\n  }); \n\n  test(\"Property contructor property value assignment\", 5, function() {\n    var now = new Date();\n    var meta = {starRating: 5};\n    var t1 = new Task({\n        name: \"Task 1\",\n        done: false,\n        counter: 7,\n        dateAdded: now,\n        metaData: meta\n      });\n    QUnit.strictEqual(t1.name(), 'Task 1', \"Assignment for TEXT properties\");\n    QUnit.strictEqual(t1.done(), false, \"Assignment for BOOL properties\");\n    QUnit.strictEqual(t1.counter(), 7, \"Assignment for INT properties\");\n    QUnit.strictEqual(t1.dateAdded(), now, \"Assignment for DATE properties\");\n    QUnit.strictEqual(t1.metaData(), meta, \"Assignment for JSON properties\");\n  });\n\n  asyncTest(\"Empty object persistence\", function() {\n    var t1 = new Task();\n    persistence.add(t1);\n    persistence.flush(function() {\n      Task.all().one(function(t1db) {\n        equals(t1db.id, t1.id, \"TEXT properties default to ''\");\n        equals(t1db.name(), \"\", \"TEXT properties default to ''\");\n        equals(t1db.done(), false, \"BOOL properties default to false\");\n        equals(t1db.counter(), 0, \"INT properties default to 0\");\n        equals(t1db.dateAdded(), null, \"DATE properties default to null\");\n        equals(t1db.metaData(), null, \"JSON properties default to null\");\n        start();\n      });\n    });\n  });\n\n  asyncTest(\"Object persistence\", function() {\n    var now = new Date();\n    var meta = {starRating: 5};\n    var t1 = new Task({\n      name: \"Task 1\",\n      done: false,\n      counter: 7,\n      dateAdded: now,\n      metaData: meta\n    });\n    persistence.add(t1);\n    persistence.flush(function() {\n      //persistence.clean();\n      Task.all().one(function(t1db) {\n        equals(t1db.name(), 'Task 1', \"Persistence of TEXT properties\");\n        equals(t1db.done(), false, \"Persistence of BOOL properties\");\n        equals(t1db.counter(), 7, \"Persistence of INT properties\");\n        equals(Math.round(t1db.dateAdded().getTime()/1000)*1000, Math.round(now.getTime()/1000)*1000, \"Persistence of DATE properties\");\n        same(t1db.metaData(), meta, \"Persistence of JSON properties\");\n        start();\n      });\n    });\n  });    \n      \n  asyncTest(\"Multiple objects\", function() {\n    var objs = [];\n    var counter = 0;\n    for(var i = 0; i < 25; i++) {\n      var t = new Task({name: \"Task \" + i});\n      t.counter(counter);\n      objs.push(t);\n      persistence.add(t);\n      counter++;\n    }\n    persistence.flush(function() {\n      Task.all().order('counter', true).list(function(results) {\n        for(var i = 0; i < 25; i++) {\n          ok(results[i] === objs[i], 'Cache works OK');\n        }\n        //persistence.clean(); // Clean out local cache\n        Task.all().order('counter', true).list(function(results) {\n          for(var i = 0; i < 25; i++) {\n            ok(results[i].id === objs[i].id, 'Retrieving from DB ok');\n          }\n          start();\n        });\n      });\n    });\n  });  \n\n  asyncTest(\"One-to-many\", function() {\n    var p = new Project({name: \"Some project\"});\n    persistence.add(p);\n    p.tasks().list(function(tasks) {\n      equals(tasks.length, 0, \"Initially, no tasks\");\n      var task1 = new Task({name: \"Do dishes\"});\n      var task2 = new Task({name: \"Laundry\"});\n    \n      // Adding in two ways\n      p.tasks().add(task1);\n      task2.project(p);\n    \n      p.tasks().order('name', true).list(function(tasks) {\n        equals(tasks.length, 2, \"Now two tasks\");\n        equals(tasks[0].id, task1.id, \"Right tasks\");\n        equals(tasks[1].id, task2.id, \"Right tasks\");\n        start();\n      });\n    });\n  }); \n\n  asyncTest(\"Many-to-many\", function() {\n    var t = new Task({name: \"Some task\"});\n    persistence.add(t);\n    t.tags().list(function(tags) {\n      equals(tags.length, 0, \"Initially, no tags\");\n      var tag1 = new Tag({name: \"important\"});\n      var tag2 = new Tag({name: \"today\"});\n      t.tags().add(tag1);\n      t.tags().add(tag2);\n      t.tags().list(function(tags) {\n        equals(tags.length, 2, \"2 tags added\");\n        var oneTag = tags[0];\n        oneTag.tasks().list(function(tagTasks) {\n          equals(tagTasks.length, 1, \"Tag has one task\");\n          equals(tagTasks[0].id, t.id, \"Correct task\");\n          oneTag.tasks().remove(tagTasks[0]);\n          t.tags().list(function(newTags) {\n            equals(newTags.length, 1, \"Tag removed task, task has only one tag left\");\n            start();\n          });\n        });\n      });\n    });\n  });\n  \n  module(\"Query collections\", {\n    setup: function() {\n      stop();\n      persistence.reset(function() {\n        persistence.schemaSync(start);\n      });\n    }\n  });\n\n  function intFilterTests(coll, callback) {\n    for(var i = 0; i < 25; i++) {\n      var t = new Task({name: \"Task \" + i, done: false});\n      t.counter(i);\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 25, \"Count items in collection\");\n        coll.filter(\"counter\", \">\", 10).list(function(results) {\n            equals(results.length, 14, \"> filter test\");\n            coll.filter(\"counter\", \"in\", [0, 1, 2]).list(function(results) {\n                equals(results.length, 3, \"'in' filter test\");\n                coll.filter(\"counter\", \"not in\", [0, 1]).list(function(results) {\n                    equals(results.length, 23, \"'not in' filter test\");\n                    coll.filter(\"counter\", \"!=\", 0).list(function(results) {\n                        equals(results.length, 24, \"'!=' filter test\");\n                        callback();\n                      });\n                  });\n              });\n          });\n      });\n  }\n  \n  function textFilterTests(coll, callback) {\n    var alphabet = 'abcdefghijklmnopqrstufwxyz';\n    for(var i = 0; i <= 25; i++) {\n      var t = new Task({name: alphabet[i]});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 26, \"Count items in collection\");\n        coll.filter(\"name\", \"=\", 'a').list(function(results) {\n            equals(results.length, 1, \"= filter test\");\n            coll.filter(\"name\", \"!=\", 'a').list(function(results) {\n                equals(results.length, 25, \"!= filter test\");\n                coll.filter(\"name\", \">\", 'm').list(function(results) {\n                    equals(results.length, 12, \"> filter test\");\n                    coll.filter(\"name\", \"in\", [\"a\", \"b\"]).list(function(results) {\n                        equals(results.length, 2, \"'in' filter test\");\n                        coll.filter(\"name\", \"not in\", [\"q\", \"x\"]).list(function(results) {\n                            equals(results.length, 24, \"'not in' filter test\");\n                            callback();\n                          });\n                      });\n                  });\n              });\n          });\n      });\n  }\n  \n  function boolFilterTests(coll, callback) {\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, done: i % 2 === 0});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 24, \"Count items in collection\");\n        coll.filter(\"done\", \"=\", true).list(function(results) {\n            equals(results.length, 12, \"= filter test\");\n            coll.filter(\"done\", \"=\", false).list(function(results) {\n                equals(results.length, 12, \"= filter test\");\n                coll.filter(\"done\", \"!=\", true).list(function(results) {\n                    equals(results.length, 12, \"'!=' filter test\");\n                    coll.filter(\"done\", \"!=\", false).list(function(results) {\n                        equals(results.length, 12, \"'!=' filter test\");\n                        callback();\n                      });\n                  });\n              });\n          });\n      });\n  }\n  \n  function dateFilterTests(coll, callback) {\n    var now = new Date();\n  \n    function dateInDays(n) {\n      var newDate = new Date(now.getTime());\n      newDate.setDate(newDate.getDate()+n);\n      return newDate;\n    }\n  \n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, dateAdded: dateInDays(i)});\n      coll.add(t);\n    }\n    coll.list(function(results) {\n        equals(results.length, 24, \"Count items in collection\");\n        coll.filter(\"dateAdded\", \"=\", dateInDays(1)).list(function(results) {\n            equals(results.length, 1, \"= filter test\");\n            coll.filter(\"dateAdded\", \"!=\", dateInDays(1)).list(function(results) {\n                equals(results.length, 23, \"= filter test\");\n                coll.filter(\"dateAdded\", \">\", dateInDays(12)).list(function(results) {\n                    equals(results.length, 11, \"> filter test\");\n                    start();\n                  });\n              })\n          });\n      });\n  }\n  \n   asyncTest(\"Database INT filters\", function() {\n       for(var i = 0; i < 25; i++) {\n         var t = new Task({name: \"Root task \" + i, done: false});\n         t.counter(i);\n         persistence.add(t);\n       }\n  \n       var p = new Project({name: \"My project\"});\n       persistence.add(p);\n       intFilterTests(p.tasks(), start);\n     }); \n  \n  asyncTest(\"Local INT filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      intFilterTests(coll, start);\n    });\n   \n  asyncTest(\"Database TEXT filters\", function() {\n      var alphabet = 'abcdefghijklmnopqrstufwxyz';\n      for(var i = 0; i <= 25; i++) {\n        var t = new Task({name: alphabet[i]});\n        persistence.add(t);\n      }\n  \n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      textFilterTests(p.tasks(), start);\n    });\n  \n  asyncTest(\"Local TEXT filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      textFilterTests(coll, start);\n    });\n  \n  asyncTest(\"Database BOOL filters\", function() {\n      for(var i = 0; i < 25; i++) {\n        var t = new Task({name: \"Root task \" + i, done: false});\n        t.counter(i);\n        persistence.add(t);\n      }\n  \n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      boolFilterTests(p.tasks(), start);\n    });\n  \n  asyncTest(\"Local BOOL filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      boolFilterTests(coll, start);\n    });\n  \n  asyncTest(\"Database DATE filters\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      dateFilterTests(p.tasks(), start);\n    });\n  \n  asyncTest(\"Local DATE filters\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      dateFilterTests(coll, start);\n    });\n   \n  \n   function intOrderTests(coll, callback) {\n     var tasks = [];\n     for(var i = 0; i < 24; i++) {\n       var t = new Task({name: \"Task \" + i, counter: i});\n       tasks.push(t);\n       coll.add(t);\n     }\n     coll.order('counter', true).list(function(results) {\n         for(var i = 0; i < 24; i++) {\n           equals(results[i].id, tasks[i].id, \"order check, ascending\");\n         }\n         tasks.reverse();\n         coll.order('counter', false).list(function(results) {\n             for(var i = 0; i < 24; i++) {\n               equals(results[i].id, tasks[i].id, \"order check, descending\");\n             }\n             callback();\n           });\n       });\n   }\n   \n   function dateOrderTests(coll, callback) {\n     var now = new Date();\n   \n     function dateInDays(n) {\n       var newDate = new Date(now.getTime());\n       newDate.setDate(newDate.getDate()+n);\n       return newDate;\n     }\n   \n     var tasks = [];\n     for(var i = 0; i < 24; i++) {\n       var t = new Task({name: \"Task \" + i, dateAdded: dateInDays(i)});\n       tasks.push(t);\n       coll.add(t);\n     }\n     coll.order('dateAdded', true).list(function(results) {\n         for(var i = 0; i < 24; i++) {\n           equals(results[i].id, tasks[i].id, \"order check, ascending\");\n         }\n         tasks.reverse();\n         coll.order('dateAdded', false).list(function(results) {\n             for(var i = 0; i < 24; i++) {\n               equals(results[i].id, tasks[i].id, \"order check, descending\");\n             }\n             callback();\n           });\n       });\n   }\n   \n   asyncTest(\"Database INT order\", function() {\n       var p = new Project({name: \"My project\"});\n       persistence.add(p);\n       intOrderTests(p.tasks(), start);\n     });\n   \n   asyncTest(\"Local INT order\", function() {\n       var coll = new persistence.LocalQueryCollection();\n       intOrderTests(coll, start);\n     });         \n  \n  asyncTest(\"Database DATE order\", function() {\n      var p = new Project({name: \"My project\"});\n      persistence.add(p);\n      dateOrderTests(p.tasks(), start);\n    });\n  \n  asyncTest(\"Local DATE order\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      dateOrderTests(coll, start);\n    });\n   \n  function collectionLimitTests(coll, callback) {\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, counter: i});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order(\"counter\", true).limit(5).list(function(results) {\n        equals(results.length, 5, \"Result length check\");\n        for(var i = 0; i < 5; i++) {\n          equals(results[i].id, tasks[i].id, \"limit check\");\n        }\n        start();\n      });\n  }\n  \n  function collectionSkipTests(coll, callback) {\n    var tasks = [];\n    for(var i = 0; i < 24; i++) {\n      var t = new Task({name: \"Task \" + i, counter: i});\n      tasks.push(t);\n      coll.add(t);\n    }\n    coll.order(\"counter\", true).skip(5).limit(5).list(function(results) {\n        equals(results.length, 5, \"Result length check\");\n        for(var i = 5; i < 10; i++) {\n          equals(results[i-5].id, tasks[i].id, \"skip check\");\n        }\n        start();\n      });\n  }\n  \n  asyncTest(\"Database limit\", function() {\n      collectionLimitTests(Task.all(), start);\n    });\n  \n  asyncTest(\"Local limit\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      collectionLimitTests(coll, start);\n    });\n  \n  asyncTest(\"Database skip\", function() {\n      collectionSkipTests(Task.all(), start);\n    });\n  \n  asyncTest(\"Local skip\", function() {\n      var coll = new persistence.LocalQueryCollection();\n      collectionSkipTests(coll, start);\n    });   \n});\n"
  },
  {
    "path": "test/browser/uki/uki-persistence.js",
    "content": "// Original file at: http://github.com/rsaccon/uki/tree/master/src/uki-persistence/persistence.js\n\n\n/**\n * persistencejs integration (http://www.persistencejs.org)\n * \n **/ \n\n// Example\n// =======\n// // persistence engine\n// include('path/to/persistence.js');\n// include('path/to/persistence.store.sql.js');\n// include('path/to/persistence.store.websql.js');\n// include('path/to/persistence.store.memory.js');\n// include('path/to/persistence.sync.js'); // optional\n// include('path/to/persistence.search.js'); // optional\n// include('path/to/persistence.migrations.js'); // optional\n// include('path/to/uki-data/uki-persistence.js');\n//                  \n// if (window.openDatabase) {\n//   persistence.store.websql.config(persistence, 'myDbName', 'database', 5 * 1024 * 1024);\n// } else {\n//   persistence.store.memory.config(persistence);\n// }\n//                          \n// var User = uki.persistence.define('User', {\n//   firstname: \"TEXT\",\n//   lastname: \"TEXT\"\n// });\n// \n// var aUser = new User({firstname: \"Joe\", lastname: \"Doo\"});\n// \n// aUser.firstname(\"Mike\") ;\n// \n// console.log(aUser.firstname()); // => Mike \n// \n// persistence.add(aUser);\n// \n// persistence.flush();\n\n\n/**\n * uki implementation for entity-property \n */\npersistence.defineProp = function(scope, field, setterCallback, getterCallback) {\n    scope[field] = function(value) {\n        if (value === undefined) { \n            return getterCallback();\n        } else {\n            setterCallback(value); \n            return scope;\n        }\n    };\n};\n\n/**\n * uki implementation for entity-property setter\n */\npersistence.set = function(scope, fieldName, value) {\n    if (persistence.isImmutable(fieldName)) throw \"immutable field: \"+fieldName;\n    scope[fieldName](value);\n    return scope;\n}; \n\n/**\n * uki implementation for entity-property getter  \n */\npersistence.get = function(arg1, arg2) {\n    var val = (arguments.length == 1) ? arg1 : arg1[arg2];\n    return (typeof val === \"function\") ? val() : val;\n};\n \n/**\n * uki ajax implementation\n */\nif (persistence.sync) {\n    uki.extend(persistence.sync, {\n        getJSON: function(url, callback) { \n            uki.getJSON(url, null, callback); \n        },   \n        postJSON: function(url, data, callback) {\n            uki.ajax({\n                url: url,\n                type: 'POST',\n                data: data, \n                dataType: 'json', \n                success: function(response) {\n                    callback(JSON.parse(response));\n                }\n            });\n        }\n    });\n}"
  },
  {
    "path": "test/browser/util.js",
    "content": "function tableExists(name, callback){\n  var sql = 'select name from sqlite_master where type = \"table\" and name == \"'+name+'\"';\n  persistence.transaction(function(tx){\n    tx.executeSql(sql, null, function(result){\n      ok(result.length == 1, name + ' table exists');\n      if (callback) callback();\n    });\n  });\n}\n\nfunction checkNoTables(callback) {\n  var sql = 'select name from sqlite_master where type = \"table\"';\n  persistence.transaction(function(tx){\n    tx.executeSql(sql, null, function(results){\n        var foundLegitimate = false;\n        results.forEach(function(result) {\n            if(result.name[0] !== '_') {\n              foundLegitimate = true;\n            }\n          });\n      ok(!foundLegitimate, 'all tables are gone');\n      if (callback) callback();\n    });\n  });\n}\n\nfunction tableNotExists(name, callback){\n  var sql = 'select name from sqlite_master where type = \"table\" and name == \"'+name+'\"';\n  persistence.transaction(function(tx){\n    tx.executeSql(sql, null, function(result){\n      ok(result.length == 0, name + ' table not exists');\n      if (callback) callback();\n    });\n  });\n}\n\nfunction columnExists(table, column, type, callback) {\n  var sql = 'select sql from sqlite_master where type = \"table\" and name == \"'+table+'\"';\n  type = type.replace('(', '\\\\(').replace(')', '\\\\)');\n  var regex = \"CREATE TABLE .+`?\" + column + \"`?\\\\s+\" + type + \".+\";\n  persistence.transaction(function(tx){\n    tx.executeSql(sql, null, function(result){\n        console.log('Table def: ------> ', result[0].sql);\n      ok(result[0].sql.match(regex), column + ' colum exists');\n      if (callback) callback();\n    });\n  });\n}\n\nfunction columnNotExists(table, column, type, callback) {\n  var sql = 'select sql from sqlite_master where type = \"table\" and name == \"'+table+'\"';\n  type = type.replace('(', '\\\\(').replace(')', '\\\\)');\n  var regex = \"CREATE TABLE \\\\w+ \\\\((\\\\w|[\\\\(\\\\), ])*\" + column + \" \" + type + \"(\\\\w|[\\\\(\\\\), ])*\\\\)\";\n  persistence.transaction(function(tx){\n    tx.executeSql(sql, null, function(result){\n      ok(!result[0].sql.match(regex), column + ' colum not exists');\n      if (callback) callback();\n    });\n  });\n}\n\nfunction indexExists(table, column, callback) {\n  var sql = 'select sql from sqlite_master where type = \"index\" and name == \"'+table+'_'+column+'\"';\n  persistence.transaction(function(tx){\n    tx.executeSql(sql, null, function(result){\n      ok(result.length == 1, 'index ' + table + '_' + column + ' exists');\n      if (callback) callback();\n    });\n  });\n}\n\nfunction indexNotExists(table, column, callback) {\n  var sql = 'select sql from sqlite_master where type = \"index\" and name == \"'+table+'_'+column+'\"';\n  persistence.transaction(function(tx){\n    tx.executeSql(sql, null, function(result){\n      ok(result.length == 0, 'index ' + table + '_' + column + ' not exists');\n      if (callback) callback();\n    });\n  });\n}\n"
  },
  {
    "path": "test/node/node-blog.js",
    "content": "/**\n * Copyright (c) 2010 Zef Hemel <zef@zef.me>\n * \n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n * \n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n *\n *\n * USAGE:\n * On first run, be sure to initialize the database first: http://localhost:8888/init\n * otherwise the application will hang (because the select query fails). After that,\n * just visit http://localhost:8888/\n */\nvar sys = require('sys');\nvar parseUrl = require('url').parse;\n\nvar persistence = require('persistencejs/persistence').persistence;\nvar persistenceStore = require('persistencejs/persistence.store.mysql');\n\n// Database configuration\npersistenceStore.config(persistence, 'localhost', 3306, 'nodejs_mysql', 'test', 'test');\n\n// Switch off query logging:\n//persistence.db.log = false;\n\nfunction log(o) {\n  sys.print(sys.inspect(o) + \"\\n\");\n}\n\n// Data model\nvar Post = persistence.define('Post', {\n    title: \"TEXT\",\n    text: \"TEXT\",\n    date: \"DATE\"\n});\nvar Comment = persistence.define('Comment', {\n    author: \"TEXT\",\n    text: \"TEXT\",\n    date: \"DATE\"\n});\n\nPost.hasMany('comments', Comment, 'post');\n\n// HTML utilities\n\nfunction htmlHeader(res, title) {\n  res.write(\"<html><head><title>\" + title + \"</title></head><body>\");\n}\nfunction htmlFooter(res) {\n  res.write('<hr/><a href=\"/\">Home</a>');\n  res.write(\"</body></html>\");\n}\n\n// Actions\n\nfunction initDatabase(session, tx, req, res, callback) {\n  htmlHeader(res, \"Initializing database.\");\n  session.schemaSync(tx, function() {\n      res.write(\"Done.\");\n      htmlFooter(res);\n      callback();\n    });\n}\n\nfunction resetDatabase(session, tx, req, res, callback) {\n  htmlHeader(res, \"Dropping all tables\");\n  session.reset(tx, function() {\n      res.write('All tables dropped, <a href=\"/init\">Click here to create fresh ones</a>');\n      htmlFooter(res);\n      callback();\n    });\n}\n\nfunction showItems(session, tx, req, res, callback) {\n  htmlHeader(res, \"Blog\");\n  res.write('<h1>Latest Posts</h1>');\n  Post.all(session).order(\"date\", false).list(tx, function(posts) {\n      for(var i = 0; i < posts.length; i++) {\n        var post = posts[i];\n        res.write('<h2><a href=\"/show?id=' + post.id + '\">' + post.title + '</a></h2>');\n        res.write(post.text);\n        res.write('<hr/>');\n        res.write('Posted ' + post.date);\n      }\n      res.write('<h1>Create new post</h1>');\n      res.write('<form action=\"/post\" method=\"GET\">');\n      res.write('<p>Title: <input name=\"title\"/></p>');\n      res.write('<p><textarea name=\"text\" cols=\"60\" rows=\"8\"></textarea></p>');\n      res.write('<p><input type=\"submit\" value=\"Post\"/></p>');\n      res.write('</form>');\n      htmlFooter(res);\n      callback();\n    });\n}\n\nfunction showItem(session, tx, req, res, callback) {\n  htmlHeader(res, \"Blog\");\n  var query = parseUrl(req.url, true).query;\n  Post.load(session, tx, query.id, function(post) {\n      res.write('<h1>' + post.title + '</h1>');\n      res.write(post.text);\n      res.write('<hr/>');\n      res.write('Posted ' + post.date);\n      res.write('<h1>Comments</h1>');\n      post.comments.order('date', true).list(tx, function(comments) {\n        for(var i = 0; i < comments.length; i++) {\n          var comment = comments[i];\n          res.write('<h2>By ' + comment.author + '</h2>');\n          res.write(comment.text);\n          res.write('<hr/>');\n          res.write('Posted ' + post.date);\n        }\n        res.write('<h1>Add a comment</h1>');\n        res.write('<form action=\"/postComment\" method=\"GET\">');\n        res.write('<input type=\"hidden\" name=\"post\" value=\"' + post.id + '\"/>');\n        res.write('<p>Your name: <input name=\"author\"/></p>');\n        res.write('<p><textarea name=\"text\" cols=\"60\" rows=\"8\"></textarea></p>');\n        res.write('<p><input type=\"submit\" value=\"Post\"/></p>');\n        res.write('</form>');\n        htmlFooter(res);\n        callback();\n      });\n    });\n}\n\nfunction post(session, tx, req, res, callback) {\n  htmlHeader(res, \"Created new post\");\n  var query = parseUrl(req.url, true).query;\n  var post = new Post(session, {title: query.title, text: query.text, date: new Date()});\n  session.add(post);\n  session.flush(tx, function() {\n      res.write('<p>Post added.</p>');\n      res.write('<a href=\"/\">Go back</a>');\n      htmlFooter(res);\n      callback();\n    });\n}\n\nfunction postComment(session, tx, req, res, callback) {\n  htmlHeader(res, \"Created new comment\");\n  var query = parseUrl(req.url, true).query;\n  var comment = new Comment(session, {text: query.text, author: query.author, date: new Date()});\n  Post.load(session, tx, query.post, function(post) {\n      post.comments.add(comment);\n      session.flush(tx, function() {\n          res.write('<p>Comment added.</p>');\n          res.write('<a href=\"/show?id=' + post.id + '\">Go back</a>');\n          htmlFooter(res);\n          callback();\n        });\n    });\n}\n\nvar urlMap = {\n  '/init': initDatabase,\n  '/reset': resetDatabase,\n  '/post': post,\n  '/postComment': postComment,\n  '/show': showItem,\n  '/': showItems\n};\n\nvar http = require('http');\nhttp.createServer(function (req, res) {\n  res.writeHead(200, {'Content-Type': 'text/html'});\n  var parsed = parseUrl(req.url, true);\n  var fn = urlMap[parsed.pathname];\n  if(fn) {\n    var session = persistenceStore.getSession();\n    session.transaction(function(tx) {\n      fn(session, tx, req, res, function() {\n          session.close();\n          res.end();\n        });\n    });\n  } else {\n    res.end(\"Not found: \" + req.url);\n  }\n}).listen(8888, \"127.0.0.1\");\nconsole.log('Server running at http://127.0.0.1:8888/');\n"
  },
  {
    "path": "test/node/partial.sync.schema.sql",
    "content": "--\r\n-- Table structure for table `_syncremovedobject`\r\n--\r\n\r\nDROP TABLE IF EXISTS `_syncremovedobject`;\r\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\r\n/*!40101 SET character_set_client = utf8 */;\r\nCREATE TABLE `_syncremovedobject` (\r\n  `entity` varchar(255) DEFAULT NULL,\r\n  `objectId` varchar(32) DEFAULT NULL,\r\n  `date` bigint(20) DEFAULT NULL,\r\n  `id` varchar(32) NOT NULL,\r\n  PRIMARY KEY (`id`)\r\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\r\n/*!40101 SET character_set_client = @saved_cs_client */;\r\n\r\n--\r\n-- Dumping data for table `_syncremovedobject`\r\n--\r\n\r\nLOCK TABLES `_syncremovedobject` WRITE;\r\n/*!40000 ALTER TABLE `_syncremovedobject` DISABLE KEYS */;\r\n/*!40000 ALTER TABLE `_syncremovedobject` ENABLE KEYS */;\r\nUNLOCK TABLES;\r\n\r\n--\r\n-- Table structure for table `class`\r\n--\r\n\r\nDROP TABLE IF EXISTS `class`;\r\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\r\n/*!40101 SET character_set_client = utf8 */;\r\nCREATE TABLE `class` (\r\n  `class_id` int(11) NOT NULL AUTO_INCREMENT,\r\n  `teacher_id` int(11) NOT NULL,\r\n  `class_name` varchar(120) NOT NULL,\r\n  `id` varchar(32) DEFAULT NULL,\r\n  `_lastChange` bigint(20) DEFAULT NULL,\r\n  PRIMARY KEY (`class_id`),\r\n  KEY `fk_teacher_id` (`teacher_id`),\r\n  CONSTRAINT `fk_teacher_id` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`teacher_id`)\r\n) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;\r\n/*!40101 SET character_set_client = @saved_cs_client */;\r\n\r\n--\r\n-- Dumping data for table `class`\r\n--\r\n\r\nLOCK TABLES `class` WRITE;\r\n/*!40000 ALTER TABLE `class` DISABLE KEYS */;\r\nINSERT INTO `class` VALUES (1,1,'Computer Systems','d2ffcf2a33c911e08f5eb58579ce9ead',1297199638),(2,2,'Linux/Dark Arts','d2ffd2e533c911e08f5eb58579ce9ead',1297199638);\r\n/*!40000 ALTER TABLE `class` ENABLE KEYS */;\r\nUNLOCK TABLES;\r\n\r\n--\r\n-- Table structure for table `student`\r\n--\r\n\r\nDROP TABLE IF EXISTS `student`;\r\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\r\n/*!40101 SET character_set_client = utf8 */;\r\nCREATE TABLE `student` (\r\n  `student_id` int(11) NOT NULL AUTO_INCREMENT,\r\n  `first_name` varchar(60) NOT NULL,\r\n  `last_name` varchar(60) NOT NULL,\r\n  `id` varchar(32) DEFAULT NULL,\r\n  `_lastChange` bigint(20) DEFAULT NULL,\r\n  PRIMARY KEY (`student_id`)\r\n) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1;\r\n/*!40101 SET character_set_client = @saved_cs_client */;\r\n\r\n--\r\n-- Dumping data for table `student`\r\n--\r\n\r\nLOCK TABLES `student` WRITE;\r\n/*!40000 ALTER TABLE `student` DISABLE KEYS */;\r\nINSERT INTO `student` VALUES (1,'John','Doe','cd6068c833c911e08f5eb58579ce9ead',1297199634),(2,'Jane','Doe','cd606df733c911e08f5eb58579ce9ead',1297199634),(3,'Chris','Farmer','cd6070b733c911e08f5eb58579ce9ead',1297199634),(4,'Bob','Jones','cd60738433c911e08f5eb58579ce9ead',1297199634),(5,'Christine','Alexander','cd60761833c911e08f5eb58579ce9ead',1297199634),(6,'Abe','Lincoln','cd60786e33c911e08f5eb58579ce9ead',1297199634),(7,'Adrian','Doty','cd607af033c911e08f5eb58579ce9ead',1297199634),(8,'Eileen','Nyman','cd607dbe33c911e08f5eb58579ce9ead',1297199634),(9,'Amber','Chase','cd60807033c911e08f5eb58579ce9ead',1297199634);\r\n/*!40000 ALTER TABLE `student` ENABLE KEYS */;\r\nUNLOCK TABLES;\r\n\r\n--\r\n-- Table structure for table `student_class`\r\n--\r\n\r\nDROP TABLE IF EXISTS `student_class`;\r\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\r\n/*!40101 SET character_set_client = utf8 */;\r\nCREATE TABLE `student_class` (\r\n  `student_id` int(11) NOT NULL,\r\n  `class_id` int(11) NOT NULL,\r\n  `id` varchar(32) DEFAULT NULL,\r\n  `_lastChange` bigint(20) DEFAULT NULL,\r\n  KEY `fk_student_id` (`student_id`),\r\n  KEY `fk_class_id` (`class_id`),\r\n  CONSTRAINT `fk_class_id` FOREIGN KEY (`class_id`) REFERENCES `class` (`class_id`) ON DELETE CASCADE ON UPDATE CASCADE,\r\n  CONSTRAINT `fk_student_id` FOREIGN KEY (`student_id`) REFERENCES `student` (`student_id`) ON DELETE CASCADE ON UPDATE CASCADE\r\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\r\n/*!40101 SET character_set_client = @saved_cs_client */;\r\n\r\n--\r\n-- Dumping data for table `student_class`\r\n--\r\n\r\nLOCK TABLES `student_class` WRITE;\r\n/*!40000 ALTER TABLE `student_class` DISABLE KEYS */;\r\nINSERT INTO `student_class` VALUES (1,1,'d61678e133c911e08f5eb58579ce9ead',1297199644),(2,1,'d6167d0c33c911e08f5eb58579ce9ead',1297199644),(3,1,'d6167f2833c911e08f5eb58579ce9ead',1297199644),(4,1,'d616812c33c911e08f5eb58579ce9ead',1297199644),(5,1,'d616833c33c911e08f5eb58579ce9ead',1297199644),(6,2,'d616854733c911e08f5eb58579ce9ead',1297199644),(7,2,'d616874e33c911e08f5eb58579ce9ead',1297199644),(8,2,'d616895933c911e08f5eb58579ce9ead',1297199644),(9,2,'d6168b6c33c911e08f5eb58579ce9ead',1297199644);\r\n/*!40000 ALTER TABLE `student_class` ENABLE KEYS */;\r\nUNLOCK TABLES;\r\n\r\n--\r\n-- Table structure for table `teacher`\r\n--\r\n\r\nDROP TABLE IF EXISTS `teacher`;\r\n/*!40101 SET @saved_cs_client     = @@character_set_client */;\r\n/*!40101 SET character_set_client = utf8 */;\r\nCREATE TABLE `teacher` (\r\n  `teacher_id` int(11) NOT NULL AUTO_INCREMENT,\r\n  `first_name` varchar(60) NOT NULL,\r\n  `last_name` varchar(60) NOT NULL,\r\n  `id` varchar(32) DEFAULT NULL,\r\n  `_lastChange` bigint(20) DEFAULT NULL,\r\n  PRIMARY KEY (`teacher_id`)\r\n) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;\r\n/*!40101 SET character_set_client = @saved_cs_client */;\r\n\r\n--\r\n-- Dumping data for table `teacher`\r\n--\r\n\r\nLOCK TABLES `teacher` WRITE;\r\n/*!40000 ALTER TABLE `teacher` DISABLE KEYS */;\r\nINSERT INTO `teacher` VALUES (1,'Mark','Price','d1521f2933c911e08f5eb58579ce9ead',1297199641),(2,'Tony','Basil','d152235d33c911e08f5eb58579ce9ead',1297199641);\r\n/*!40000 ALTER TABLE `teacher` ENABLE KEYS */;\r\nUNLOCK TABLES;\r\n/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\r\n\r\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\r\n/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\r\n/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\r\n/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\r\n/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\r\n/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\r\n/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\r\n"
  },
  {
    "path": "test/node/test.error.handling.js",
    "content": "// $ expresso -s test/test.error.handling.js\n\nvar assert = require('assert');\nvar persistence = require('../lib/persistence').persistence;\nvar persistenceStore = require('../lib/persistence.store.mysql');\n\npersistenceStore.config(persistence, 'localhost', 3306, 'nodejs_mysql', 'test', 'test');\n\nvar InexistentTable = persistence.define('inexistent_table', {\n  name: \"TEXT\"\n});\n\nvar session = persistenceStore.getSession();\n\nvar create = function(data, cb) {\n  var inexistent_table = new InexistentTable(data);\n  session.add(inexistent_table);\n  session.flush(function(result, err) {\n    cb && cb(err, inexistent_table);\n  });\n};\n\nvar remove = function(inexistent_table, cb) {\n  session.remove(inexistent_table);\n  session.flush(function(result, err) {\n    cb && cb(err, result);\n  });\n};\n\nvar temp;\n\nmodule.exports = {\n  'beforeAll': function(done) {\n    session.transaction(function(tx) {\n      tx.executeSql('FLUSH TABLES WITH READ LOCK;', function() {\n        done();\n      });\n    });\n  },\n  'schemaSync fail': function(done) {\n    session.schemaSync(function(tx, err) {\n      assert.isDefined(err);\n      done();\n    });\n  },\n  'create fail': function(done) {\n    create({\n      name: 'test'\n    }, function(err, result) {\n      assert.isDefined(err);\n      temp = result;\n      done();\n    });\n  },\n  'remove fail': function(done) {\n    remove(temp, function(err, result) {\n      assert.isDefined(err);\n      done();\n    });\n  },\n  'destroyAll fail': function(done) {\n    InexistentTable.all(session).destroyAll(function(result, err) {\n      assert.isDefined(err);\n      done();\n    });\n  },\n  'reset fail': function(done) {\n    session.reset(function(result, err) {\n      assert.isDefined(err);\n      done();\n    });\n  },\n  afterAll: function(done) {\n    session.transaction(function(tx) {\n      tx.executeSql('UNLOCK TABLES;', function() {\n        session.close();\n        done();\n      });\n    });\n  }\n};\n"
  },
  {
    "path": "test/node/test.memory.store.js",
    "content": "// $ expresso -s test.memory.store.js\n\nvar assert = require('assert');\nvar persistence = require('../../lib/persistence').persistence;\nvar persistenceStore = require('../../lib/persistence.store.memory');\npersistenceStore.config(persistence);\n\nvar Task = persistence.define('Task', {\n  username: 'TEXT'\n});\n\nvar data = {\n  username: 'test'\n};\n\nvar task, session;\n\nmodule.exports = {\n  init: function(done) {\n    persistence.schemaSync();\n    session = persistenceStore.getSession();\n    done();\n  },\n  add: function(done) {\n    task = new Task(data);\n    session.add(task);\n    session.flush(function(result, err) {\n      assert.ifError(err);\n      done();\n    });\n  },\n  get: function(done) {\n    Task.findBy(session, 'id', task.id, function(task) {\n      assert.equal(task.username, data.username);\n      done();\n    });\n  },\n  update: function(done) {\n    task.username = 'test2';\n    Task.findBy(session, 'id', task.id, function(task) {\n      assert.equal(task.username, 'test2');\n      done();\n    });\n  },\n  remove: function(done) {\n    session.remove(task);\n    session.flush(function(result, err) {\n      assert.ifError(err);\n      Task.findBy(session, 'id', task.id, function(task) {\n        assert.equal(task, null);\n        done();\n      });\n    });\n  },\n  afterAll: function(done) {\n    session.close();\n    done();\n  }\n};\n"
  },
  {
    "path": "test/node/test.sqlite.store.js",
    "content": "// $ expresso -s test.sqlite.store.js\n\nvar assert = require('assert');\nvar persistence = require('../../lib/persistence').persistence;\nvar persistenceStore = require('../../lib/persistence.store.sqlite');\n\nvar dbPath = __dirname + '/test.db';\npersistenceStore.config(persistence, dbPath);\n\nvar Task = persistence.define('Task', {\n  username: 'TEXT'\n});\n\nvar data = {\n  username: 'test'\n};\n\nvar task, session;\n\n// remove test database\nfunction removeDb() {\n  try {\n    require('fs').unlinkSync(dbPath);\n  } catch (err) {\n  }\n}\n\nmodule.exports = {\n init: function(done) {\n    removeDb();\n    session = persistenceStore.getSession(function () {\n      session.schemaSync(done);\n    });\n  },\n  add: function(done) {\n    task = new Task(session, data);\n    session.add(task);\n    session.flush(function(result, err) {\n      assert.ifError(err);\n      done();\n    });\n  },\n  get: function(done) {\n    Task.findBy(session, 'id', task.id, function(task) {\n      assert.equal(task.username, data.username);\n      done();\n    });\n  },\n  update: function(done) {\n    task.username = 'test2';\n    Task.findBy(session, 'id', task.id, function(task) {\n      assert.equal(task.username, 'test2');\n      done();\n    });\n  },\n  remove: function(done) {\n    session.remove(task);\n    session.flush(function(result, err) {\n      assert.ifError(err);\n      Task.findBy(session, 'id', task.id, function(task) {\n        assert.equal(task, null);\n        done();\n      });\n    });\n  },\n  afterAll: function(done) {\n    session.close(function() {\n      removeDb();\n      done();\n    });\n  }\n};\n"
  },
  {
    "path": "test/node/test.sqlite3.store.js",
    "content": "// $ expresso -s test.sqlite3.store.js\n\nvar assert = require('assert');\nvar persistence = require('../../lib/persistence').persistence;\nvar persistenceStore = require('../../lib/persistence.store.sqlite3');\n\nvar dbPath = __dirname + '/test-sqlite3.db';\npersistenceStore.config(persistence, dbPath);\n\nvar Task = persistence.define('Task', {\n  username: 'TEXT'\n});\n\nvar data = {\n  username: 'test'\n};\n\nvar data2 = {\n  username: 'test2'\n};\n\nvar task, task2, session;\n\n// remove test database\nfunction removeDb() {\n  try {\n    require('fs').unlinkSync(dbPath);\n  } catch (err) {\n  }\n}\n\nmodule.exports = {\n  init: function(done) {\n    removeDb();\n    session = persistenceStore.getSession(function () {\n      session.schemaSync(done);\n    });\n  },\n  add: function(done) {\n    task = new Task(session, data);\n    session.add(task);\n    session.flush(function(result, err) {\n      assert.ifError(err);\n      done();\n    });\n  },\n  get: function(done) {\n    Task.findBy(session, 'id', task.id, function(task) {\n      assert.equal(task.username, data.username);\n      done();\n    });\n  },\n  update: function(done) {\n    task.username = 'test2';\n    Task.findBy(session, 'id', task.id, function(task) {\n      assert.equal(task.username, 'test2');\n      done();\n    });\n  },\n  remove: function(done) {\n    session.remove(task);\n    session.flush(function(result, err) {\n      assert.ifError(err);\n      Task.findBy(session, 'id', task.id, function(task) {\n        assert.equal(task, null);\n        done();\n      });\n    });\n  },\n  addMultiple: function(done) {\n    task = new Task(session, data);\n    session.add(task);\n    task2 = new Task(session, data2);\n    session.add(task2);\n    session.flush(function(result, err) {\n      assert.ifError(err);\n      var count = 0;\n      Task.all(session).order('username', true).each(function(row) {\n        count++;\n        if (count == 1) {\n          assert.equal(row.username, data.username);\n        }\n        if (count == 2) {\n          assert.equal(row.username, data2.username);\n          done();\n        }\n      });\n    });\n  },\n  afterAll: function(done) {\n    session.close(function() {\n      removeDb();\n      done();\n    });\n  }\n};\n"
  },
  {
    "path": "test/node/test.store.config.js",
    "content": "// $ expresso test.store.config.js\n\nvar assert = require('assert');\nvar persistence = require('../../lib/persistence').persistence;\n\nvar config = {\n  adaptor: '',\n  database: 'test',\n  host: 'localhost',\n  port: 3306,\n  user: 'root',\n  password: ''\n};\n\nmodule.exports = {\n  memory: function() {\n    config.adaptor = 'memory';\n    var persistenceStore = require('../../lib/persistence.store.config').init(persistence, config);\n    var session = persistenceStore.getSession();\n    session.close();\n  },\n  mysql: function() {\n    config.adaptor = 'mysql';\n    var persistenceStore = require('../../lib/persistence.store.config').init(persistence, config);\n    var session = persistenceStore.getSession();\n    session.close();\n  },\n  default: function() {\n    var persistenceStore = require('../../lib/persistence.store.config').init(persistence, config);\n    var session = persistenceStore.getSession();\n    session.close();\n  }\n};\n"
  },
  {
    "path": "test/node/test.sync.server.js",
    "content": "/**\n * Copyright (c) 2010 Zef Hemel <zef@zef.me>\n * \n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n * \n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n *\n *\n * Requirements:\n * node.js\n * npm install connect\n * npm install express\n */\nvar sys = require('sys');\nvar connect = require('connect');\nvar express = require('express');\n\nvar persistence = require('../../lib/persistence').persistence;\nvar persistenceStore = require('../../lib/persistence.store.mysql');\nvar persistenceSync = require('../../lib/persistence.sync.server');\n\n// Database configuration\npersistenceStore.config(persistence, 'localhost', 3306, 'synctest', 'test', 'test');\n\n// Switch off query logging:\n//persistence.db.log = false;\n\nfunction log(o) {\n  sys.print(sys.inspect(o) + \"\\n\");\n}\n\npersistenceSync.config(persistence);\n\n// Data model\nvar Project = persistence.define('Project', {\n    name: \"TEXT\"\n  });\n\nvar Task = persistence.define('Task', {\n    name: \"TEXT\",\n    done: \"BOOL\"\n  });\n\nvar Tag = persistence.define('Tag', {\n    name: \"TEXT\"\n  });\n\nTask.hasMany('tags', Tag, 'tasks');\nTag.hasMany('tasks', Task, 'tags');\n\nProject.hasMany('tasks', Task, 'project');\n\nProject.enableSync();\nTask.enableSync();\nTag.enableSync();\n\nvar app = express.createServer(\n  //connect.logger(), \n  connect.bodyDecoder(), \n  connect.staticProvider('../browser'),\n  function(req, res, next) {\n    var end = res.end;\n\n    req.conn = persistenceStore.getSession();\n    res.end = function() {\n      req.conn.close();\n      end.apply(res, arguments);\n    };\n    req.conn.transaction(function(tx) {\n        req.tx = tx;\n        next();\n      });\n  }\n);\n\nfunction generateDummyData(session) {\n  var p = new Project(session, {name: \"Main project\"});\n  session.add(p);\n  for(var i = 0; i < 25; i++) {\n    var t = new Task(session, {name: \"Task \" + i, done: false});\n    p.tasks.add(t);\n  }\n}\n\n// Actions\napp.get('/reset', function(req, res) {\n  req.conn.reset(req.tx, function() {\n      req.conn.schemaSync(req.tx, function() {\n          generateDummyData(req.conn);\n          req.conn.flush(req.tx, function() {\n              res.send({status: \"ok\"});\n            });\n        });\n    });\n});\n\napp.get('/projectUpdates',  function(req, res) {\n    persistenceSync.pushUpdates(req.conn, req.tx, Project, req.query.since, function(updates) {\n        res.send(updates);\n      });\n});\n\napp.post('/projectUpdates',  function(req, res) {\n    persistenceSync.receiveUpdates(req.conn, req.tx, Project, req.body, function(result) {\n        res.send(result);\n      });\n  });\n\napp.get('/taskUpdates',  function(req, res) {\n    persistenceSync.pushUpdates(req.conn, req.tx, Task, req.query.since, function(updates) {\n        res.send(updates);\n      });\n});\n\napp.post('/taskUpdates',  function(req, res) {\n    persistenceSync.receiveUpdates(req.conn, req.tx, Task, req.body, function(result) {\n        res.send(result);\n      });\n  });\n\napp.get('/tagUpdates',  function(req, res) {\n    persistenceSync.pushUpdates(req.conn, req.tx, Tag, req.query.since, function(updates) {\n        res.send(updates);\n      });\n});\n\napp.post('/tagUpdates',  function(req, res) {\n    persistenceSync.receiveUpdates(req.conn, req.tx, Tag, req.body, function(result) {\n        res.send(result);\n      });\n  });\n\napp.get('/markAllDone', function(req, res) {\n    Task.all(req.conn).list(req.tx, function(tasks) {\n        tasks.forEach(function(task) {\n            task.done = true;\n          });\n        req.conn.flush(req.tx, function() {\n            res.send({status: 'ok'});\n          });\n      });\n});\n\napp.get('/markAllUndone', function(req, res) {\n    Task.all(req.conn).list(req.tx, function(tasks) {\n        tasks.forEach(function(task) {\n            task.done = false;\n          });\n        req.conn.flush(req.tx, function() {\n            res.send({status: 'ok'});\n          });\n      });\n});\n\napp.listen(8888);\n\nconsole.log('Server running at http://127.0.0.1:8888/');\n"
  },
  {
    "path": "test/titanium/.gitignore",
    "content": "tmp\n"
  },
  {
    "path": "test/titanium/Resources/app.js",
    "content": "var win = Titanium.UI.createWindow({\n  url:'runner.js',\n  title: 'Unit Test'\n});\nwin.open();"
  },
  {
    "path": "test/titanium/Resources/qunit/qunit.js",
    "content": "/*\n * QUnit - A JavaScript Unit Testing Framework\n *\n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2009 John Resig, Jörn Zaefferer\n * Dual licensed under the MIT (MIT-LICENSE.txt)\n * and GPL (GPL-LICENSE.txt) licenses.\n */\n\n(function(window) {\n\nvar QUnit = {\n\n\t// call on start of module test to prepend name to all tests\n\tmodule: function(name, testEnvironment) {\n\t\tconfig.currentModule = name;\n\n\t\tsynchronize(function() {\n\t\t\tif ( config.currentModule ) {\n\t\t\t\tQUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );\n\t\t\t}\n\n\t\t\tconfig.currentModule = name;\n\t\t\tconfig.moduleTestEnvironment = testEnvironment;\n\t\t\tconfig.moduleStats = { all: 0, bad: 0 };\n\n\t\t\tQUnit.moduleStart( name, testEnvironment );\n\t\t});\n\t},\n\n\tasyncTest: function(testName, expected, callback) {\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = 0;\n\t\t}\n\n\t\tQUnit.test(testName, expected, callback, true);\n\t},\n\n\ttest: function(testName, expected, callback, async) {\n\t\tvar name = '<span class=\"test-name\">' + testName + '</span>', testEnvironment, testEnvironmentArg;\n\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = null;\n\t\t}\n\t\t// is 2nd argument a testEnvironment?\n\t\tif ( expected && typeof expected === 'object') {\n\t\t\ttestEnvironmentArg =  expected;\n\t\t\texpected = null;\n\t\t}\n\n\t\tif ( config.currentModule ) {\n\t\t\tname = '<span class=\"module-name\">' + config.currentModule + \"</span>: \" + name;\n\t\t}\n\n\t\tif ( !validTest(config.currentModule + \": \" + testName) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tsynchronize(function() {\n\n\t\t\ttestEnvironment = extend({\n\t\t\t\tsetup: function() {},\n\t\t\t\tteardown: function() {}\n\t\t\t}, config.moduleTestEnvironment);\n\t\t\tif (testEnvironmentArg) {\n\t\t\t\textend(testEnvironment,testEnvironmentArg);\n\t\t\t}\n\n\t\t\tQUnit.testStart( testName, testEnvironment );\n\n\t\t\t// allow utility functions to access the current test environment\n\t\t\tQUnit.current_testEnvironment = testEnvironment;\n\n\t\t\tconfig.assertions = [];\n\t\t\tconfig.expected = expected;\n\n\t\t\tvar tests = id(\"qunit-tests\");\n\t\t\tif (tests) {\n\t\t\t\tvar b = document.createElement(\"strong\");\n\t\t\t\t\tb.innerHTML = \"Running \" + name;\n\t\t\t\tvar li = document.createElement(\"li\");\n\t\t\t\t\tli.appendChild( b );\n\t\t\t\t\tli.id = \"current-test-output\";\n\t\t\t\ttests.appendChild( li )\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif ( !config.pollution ) {\n\t\t\t\t\tsaveGlobal();\n\t\t\t\t}\n\n\t\t\t\ttestEnvironment.setup.call(testEnvironment);\n\t\t\t} catch(e) {\n\t\t\t\tQUnit.ok( false, \"Setup failed on \" + name + \": \" + e.message );\n\t\t\t}\n\t    });\n\n\t    synchronize(function() {\n\t\t\tif ( async ) {\n\t\t\t\tQUnit.stop();\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tcallback.call(testEnvironment);\n\t\t\t} catch(e) {\n\t\t\t\tfail(\"Test \" + name + \" died, exception and test follows\", e, callback);\n\t\t\t\tvar message;\n\t\t\t\tif (e.message) {\n\t\t\t\t\tmessage = e.message;\n\t\t\t\t} else {\n\t\t\t\t\tmessage = JSON.stringify(e);\n\t\t\t\t}\n\t\t\t\tQUnit.ok( false, \"Died on test #\" + (config.assertions.length + 1) + \": \" + message );\n\t\t\t\t// else next test will carry the responsibility\n\t\t\t\tsaveGlobal();\n\n\t\t\t\t// Restart the tests if they're blocking\n\t\t\t\tif ( config.blocking ) {\n\t\t\t\t\tstart();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tsynchronize(function() {\n\t\t\ttry {\n\t\t\t\tcheckPollution();\n\t\t\t\ttestEnvironment.teardown.call(testEnvironment);\n\t\t\t} catch(e) {\n\t\t\t\tQUnit.ok( false, \"Teardown failed on \" + name + \": \" + e.message );\n\t\t\t}\n\t    });\n\n\t    synchronize(function() {\n\t\t\ttry {\n\t\t\t\tQUnit.reset();\n\t\t\t} catch(e) {\n\t\t\t\tfail(\"reset() failed, following Test \" + name + \", exception and reset fn follows\", e, reset);\n\t\t\t}\n\n\t\t\tif ( config.expected && config.expected != config.assertions.length ) {\n\t\t\t\tQUnit.ok( false, \"Expected \" + config.expected + \" assertions, but \" + config.assertions.length + \" were run\" );\n\t\t\t}\n\n\t\t\tvar good = 0, bad = 0,\n\t\t\t\ttests = id(\"qunit-tests\");\n\n\t\t\tconfig.stats.all += config.assertions.length;\n\t\t\tconfig.moduleStats.all += config.assertions.length;\n\n\t\t\tif ( tests ) {\n\t\t\t\tvar ol  = document.createElement(\"ol\");\n\n\t\t\t\tfor ( var i = 0; i < config.assertions.length; i++ ) {\n\t\t\t\t\tvar assertion = config.assertions[i];\n\n\t\t\t\t\tvar li = document.createElement(\"li\");\n\t\t\t\t\tli.className = assertion.result ? \"pass\" : \"fail\";\n\t\t\t\t\tli.innerHTML = assertion.message || \"(no message)\";\n\t\t\t\t\tol.appendChild( li );\n\n\t\t\t\t\tif ( assertion.result ) {\n\t\t\t\t\t\tgood++;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbad++;\n\t\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (bad == 0) {\n\t\t\t\t\tol.style.display = \"none\";\n\t\t\t\t}\n\n\t\t\t\tvar b = document.createElement(\"strong\");\n\t\t\t\tb.innerHTML = name + \" <b style='color:black;'>(<b class='fail'>\" + bad + \"</b>, <b class='pass'>\" + good + \"</b>, \" + config.assertions.length + \")</b>\";\n\n\t\t\t\taddEvent(b, \"click\", function() {\n\t\t\t\t\tvar next = b.nextSibling, display = next.style.display;\n\t\t\t\t\tnext.style.display = display === \"none\" ? \"block\" : \"none\";\n\t\t\t\t});\n\n\t\t\t\taddEvent(b, \"dblclick\", function(e) {\n\t\t\t\t\tvar target = e && e.target ? e.target : window.event.srcElement;\n\t\t\t\t\tif ( target.nodeName.toLowerCase() == \"span\" || target.nodeName.toLowerCase() == \"b\" ) {\n\t\t\t\t\t\ttarget = target.parentNode;\n\t\t\t\t\t}\n\t\t\t\t\tif ( window.location && target.nodeName.toLowerCase() === \"strong\" ) {\n\t\t\t\t\t\twindow.location.search = \"?\" + encodeURIComponent(getText([target]).replace(/\\(.+\\)$/, \"\").replace(/(^\\s*|\\s*$)/g, \"\"));\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tvar li = id(\"current-test-output\");\n\t\t\t\tli.id = \"\";\n\t\t\t\tli.className = bad ? \"fail\" : \"pass\";\n\t\t\t\tli.removeChild( li.firstChild );\n\t\t\t\tli.appendChild( b );\n\t\t\t\tli.appendChild( ol );\n\n\t\t\t\tif ( bad ) {\n\t\t\t\t\tvar toolbar = id(\"qunit-testrunner-toolbar\");\n\t\t\t\t\tif ( toolbar ) {\n\t\t\t\t\t\ttoolbar.style.display = \"block\";\n\t\t\t\t\t\tid(\"qunit-filter-pass\").disabled = null;\n\t\t\t\t\t\tid(\"qunit-filter-missing\").disabled = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tfor ( var i = 0; i < config.assertions.length; i++ ) {\n\t\t\t\t\tif ( !config.assertions[i].result ) {\n\t\t\t\t\t\tbad++;\n\t\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tQUnit.testDone( testName, bad, config.assertions.length );\n\n\t\t\tif ( !window.setTimeout && !config.queue.length ) {\n\t\t\t\tdone();\n\t\t\t}\n\t\t});\n\n\t\tif ( window.setTimeout && !config.doneTimer ) {\n\t\t\tconfig.doneTimer = window.setTimeout(function(){\n\t\t\t\tif ( !config.queue.length ) {\n\t\t\t\t\tdone();\n\t\t\t\t} else {\n\t\t\t\t\tsynchronize( done );\n\t\t\t\t}\n\t\t\t}, 13);\n\t\t}\n\t},\n\n\t/**\n\t * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.\n\t */\n\texpect: function(asserts) {\n\t\tconfig.expected = asserts;\n\t},\n\n\t/**\n\t * Asserts true.\n\t * @example ok( \"asdfasdf\".length > 5, \"There must be at least 5 chars\" );\n\t */\n\tok: function(a, msg) {\n\t\tmsg = escapeHtml(msg);\n\t\tQUnit.log(a, msg);\n\n\t\tconfig.assertions.push({\n\t\t\tresult: !!a,\n\t\t\tmessage: msg\n\t\t});\n\t},\n\n\t/**\n\t * Checks that the first two arguments are equal, with an optional message.\n\t * Prints out both actual and expected values.\n\t *\n\t * Prefered to ok( actual == expected, message )\n\t *\n\t * @example equal( format(\"Received {0} bytes.\", 2), \"Received 2 bytes.\" );\n\t *\n\t * @param Object actual\n\t * @param Object expected\n\t * @param String message (optional)\n\t */\n\tequal: function(actual, expected, message) {\n\t\tpush(expected == actual, actual, expected, message);\n\t},\n\n\tnotEqual: function(actual, expected, message) {\n\t\tpush(expected != actual, actual, expected, message);\n\t},\n\n\tdeepEqual: function(actual, expected, message) {\n\t\tpush(QUnit.equiv(actual, expected), actual, expected, message);\n\t},\n\n\tnotDeepEqual: function(actual, expected, message) {\n\t\tpush(!QUnit.equiv(actual, expected), actual, expected, message);\n\t},\n\n\tstrictEqual: function(actual, expected, message) {\n\t\tpush(expected === actual, actual, expected, message);\n\t},\n\n\tnotStrictEqual: function(actual, expected, message) {\n\t\tpush(expected !== actual, actual, expected, message);\n\t},\n\n\traises: function(fn,  message) {\n\t\ttry {\n\t\t\tfn();\n\t\t\tok( false, message );\n\t\t}\n\t\tcatch (e) {\n\t\t\tok( true, message );\n\t\t}\n\t},\n\n\tstart: function(run_sync) {\n\t\t// A slight delay, to avoid any current callbacks\n\t\tif ( window.setTimeout && !run_sync) {\n\t\t\twindow.setTimeout(function() {\n\t\t\t\tif ( config.timeout ) {\n\t\t\t\t\tclearTimeout(config.timeout);\n\t\t\t\t}\n\n\t\t\t\tconfig.blocking = false;\n\t\t\t\tprocess();\n\t\t\t}, 13);\n\t\t} else {\n\t\t\tconfig.blocking = false;\n\t\t\tprocess();\n\t\t}\n\t},\n\n\tstop: function(timeout) {\n\t\tconfig.blocking = true;\n\n\t\tif ( timeout && window.setTimeout ) {\n\t\t\tconfig.timeout = window.setTimeout(function() {\n\t\t\t\tQUnit.ok( false, \"Test timed out\" );\n\t\t\t\tQUnit.start();\n\t\t\t}, timeout);\n\t\t}\n\t}\n\n};\n\n// Backwards compatibility, deprecated\nQUnit.equals = QUnit.equal;\nQUnit.same = QUnit.deepEqual;\n\n// Maintain internal state\nvar config = {\n\t// The queue of tests to run\n\tqueue: [],\n\n\t// block until document ready\n\tblocking: true\n};\n\n// Load paramaters\n(function() {\n\tvar location = window.location || { search: \"\", protocol: \"file:\" },\n\t\tGETParams = location.search.slice(1).split('&');\n\n\tfor ( var i = 0; i < GETParams.length; i++ ) {\n\t\tGETParams[i] = decodeURIComponent( GETParams[i] );\n\t\tif ( GETParams[i] === \"noglobals\" ) {\n\t\t\tGETParams.splice( i, 1 );\n\t\t\ti--;\n\t\t\tconfig.noglobals = true;\n\t\t} else if ( GETParams[i].search('=') > -1 ) {\n\t\t\tGETParams.splice( i, 1 );\n\t\t\ti--;\n\t\t}\n\t}\n\n\t// restrict modules/tests by get parameters\n\tconfig.filters = GETParams;\n\n\t// Figure out if we're running the tests from a server or not\n\tQUnit.isLocal = !!(location.protocol === 'file:');\n})();\n\n// Expose the API as global variables, unless an 'exports'\n// object exists, in that case we assume we're in CommonJS\nif ( typeof exports === \"undefined\" || typeof require === \"undefined\" ) {\n\textend(window, QUnit);\n\twindow.QUnit = QUnit;\n} else {\n\textend(exports, QUnit);\n\texports.QUnit = QUnit;\n}\n\n// define these after exposing globals to keep them in these QUnit namespace only\nextend(QUnit, {\n\tconfig: config,\n\n\t// Initialize the configuration options\n\tinit: function() {\n\t\textend(config, {\n\t\t\tstats: { all: 0, bad: 0 },\n\t\t\tmoduleStats: { all: 0, bad: 0 },\n\t\t\tstarted: +new Date,\n\t\t\tupdateRate: 1000,\n\t\t\tblocking: false,\n\t\t\tautostart: true,\n\t\t\tautorun: false,\n\t\t\tassertions: [],\n\t\t\tfilters: [],\n\t\t\tqueue: []\n\t\t});\n\n\t\tvar tests = id(\"qunit-tests\"),\n\t\t\tbanner = id(\"qunit-banner\"),\n\t\t\tresult = id(\"qunit-testresult\");\n\n\t\tif ( tests ) {\n\t\t\ttests.innerHTML = \"\";\n\t\t}\n\n\t\tif ( banner ) {\n\t\t\tbanner.className = \"\";\n\t\t}\n\n\t\tif ( result ) {\n\t\t\tresult.parentNode.removeChild( result );\n\t\t}\n\t},\n\n\t/**\n\t * Resets the test setup. Useful for tests that modify the DOM.\n\t */\n\treset: function() {\n\t\tif ( window.jQuery ) {\n\t\t\tjQuery(\"#main, #qunit-fixture\").html( config.fixture );\n\t\t}\n\t},\n\n\t/**\n\t * Trigger an event on an element.\n\t *\n\t * @example triggerEvent( document.body, \"click\" );\n\t *\n\t * @param DOMElement elem\n\t * @param String type\n\t */\n\ttriggerEvent: function( elem, type, event ) {\n\t\tif ( document.createEvent ) {\n\t\t\tevent = document.createEvent(\"MouseEvents\");\n\t\t\tevent.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,\n\t\t\t\t0, 0, 0, 0, 0, false, false, false, false, 0, null);\n\t\t\telem.dispatchEvent( event );\n\n\t\t} else if ( elem.fireEvent ) {\n\t\t\telem.fireEvent(\"on\"+type);\n\t\t}\n\t},\n\n\t// Safe object type checking\n\tis: function( type, obj ) {\n\t\treturn QUnit.objectType( obj ) == type;\n\t},\n\n\tobjectType: function( obj ) {\n\t\tif (typeof obj === \"undefined\") {\n\t\t\t\treturn \"undefined\";\n\n\t\t// consider: typeof null === object\n\t\t}\n\t\tif (obj === null) {\n\t\t\t\treturn \"null\";\n\t\t}\n\n\t\tvar type = Object.prototype.toString.call( obj )\n\t\t\t.match(/^\\[object\\s(.*)\\]$/)[1] || '';\n\n\t\tswitch (type) {\n\t\t\t\tcase 'Number':\n\t\t\t\t\t\tif (isNaN(obj)) {\n\t\t\t\t\t\t\t\treturn \"nan\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn \"number\";\n\t\t\t\t\t\t}\n\t\t\t\tcase 'String':\n\t\t\t\tcase 'Boolean':\n\t\t\t\tcase 'Array':\n\t\t\t\tcase 'Date':\n\t\t\t\tcase 'RegExp':\n\t\t\t\tcase 'Function':\n\t\t\t\t\t\treturn type.toLowerCase();\n\t\t}\n\t\tif (typeof obj === \"object\") {\n\t\t\t\treturn \"object\";\n\t\t}\n\t\treturn undefined;\n\t},\n\n\t// Logging callbacks\n\tbegin: function() {},\n\tdone: function(failures, total) {},\n\tlog: function(result, message) {},\n\ttestStart: function(name, testEnvironment) {},\n\ttestDone: function(name, failures, total) {},\n\tmoduleStart: function(name, testEnvironment) {},\n\tmoduleDone: function(name, failures, total) {}\n});\n\nif ( typeof document === \"undefined\" || document.readyState === \"complete\" ) {\n\tconfig.autorun = true;\n}\n\naddEvent(window, \"load\", function() {\n\tQUnit.begin();\n\n\t// Initialize the config, saving the execution queue\n\tvar oldconfig = extend({}, config);\n\tQUnit.init();\n\textend(config, oldconfig);\n\n\tconfig.blocking = false;\n\n\tvar userAgent = id(\"qunit-userAgent\");\n\tif ( userAgent ) {\n\t\tuserAgent.innerHTML = navigator.userAgent;\n\t}\n\n\tvar toolbar = id(\"qunit-testrunner-toolbar\");\n\tif ( toolbar ) {\n\t\ttoolbar.style.display = \"none\";\n\n\t\tvar filter = document.createElement(\"input\");\n\t\tfilter.type = \"checkbox\";\n\t\tfilter.id = \"qunit-filter-pass\";\n\t\tfilter.disabled = true;\n\t\taddEvent( filter, \"click\", function() {\n\t\t\tvar li = document.getElementsByTagName(\"li\");\n\t\t\tfor ( var i = 0; i < li.length; i++ ) {\n\t\t\t\tif ( li[i].className.indexOf(\"pass\") > -1 ) {\n\t\t\t\t\tli[i].style.display = filter.checked ? \"none\" : \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttoolbar.appendChild( filter );\n\n\t\tvar label = document.createElement(\"label\");\n\t\tlabel.setAttribute(\"for\", \"qunit-filter-pass\");\n\t\tlabel.innerHTML = \"Hide passed tests\";\n\t\ttoolbar.appendChild( label );\n\n\t\tvar missing = document.createElement(\"input\");\n\t\tmissing.type = \"checkbox\";\n\t\tmissing.id = \"qunit-filter-missing\";\n\t\tmissing.disabled = true;\n\t\taddEvent( missing, \"click\", function() {\n\t\t\tvar li = document.getElementsByTagName(\"li\");\n\t\t\tfor ( var i = 0; i < li.length; i++ ) {\n\t\t\t\tif ( li[i].className.indexOf(\"fail\") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {\n\t\t\t\t\tli[i].parentNode.parentNode.style.display = missing.checked ? \"none\" : \"block\";\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\ttoolbar.appendChild( missing );\n\n\t\tlabel = document.createElement(\"label\");\n\t\tlabel.setAttribute(\"for\", \"qunit-filter-missing\");\n\t\tlabel.innerHTML = \"Hide missing tests (untested code is broken code)\";\n\t\ttoolbar.appendChild( label );\n\t}\n\n\tvar main = id('main') || id('qunit-fixture');\n\tif ( main ) {\n\t\tconfig.fixture = main.innerHTML;\n\t}\n\n\tif (config.autostart) {\n\t\tQUnit.start(true);\n\t}\n});\n\nfunction done() {\n\tif ( config.doneTimer && window.clearTimeout ) {\n\t\twindow.clearTimeout( config.doneTimer );\n\t\tconfig.doneTimer = null;\n\t}\n\n\tif ( config.queue.length ) {\n\t\tconfig.doneTimer = window.setTimeout(function(){\n\t\t\tif ( !config.queue.length ) {\n\t\t\t\tdone();\n\t\t\t} else {\n\t\t\t\tsynchronize( done );\n\t\t\t}\n\t\t}, 13);\n\n\t\treturn;\n\t}\n\n\tconfig.autorun = true;\n\n\t// Log the last module results\n\tif ( config.currentModule ) {\n\t\tQUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );\n\t}\n\n\tvar banner = id(\"qunit-banner\"),\n\t\ttests = id(\"qunit-tests\"),\n\t\thtml = ['Tests completed in ',\n\t\t+new Date - config.started, ' milliseconds.<br/>',\n\t\t'<span class=\"passed\">', config.stats.all - config.stats.bad, '</span> tests of <span class=\"total\">', config.stats.all, '</span> passed, <span class=\"failed\">', config.stats.bad,'</span> failed.'].join('');\n\n\tif ( banner ) {\n\t\tbanner.className = (config.stats.bad ? \"qunit-fail\" : \"qunit-pass\");\n\t}\n\n\tif ( tests ) {\n\t\tvar result = id(\"qunit-testresult\");\n\n\t\tif ( !result ) {\n\t\t\tresult = document.createElement(\"p\");\n\t\t\tresult.id = \"qunit-testresult\";\n\t\t\tresult.className = \"result\";\n\t\t\ttests.parentNode.insertBefore( result, tests.nextSibling );\n\t\t}\n\n\t\tresult.innerHTML = html;\n\t}\n\n\tQUnit.done( config.stats.bad, config.stats.all );\n}\n\nfunction validTest( name ) {\n\tvar i = config.filters.length,\n\t\trun = false;\n\n\tif ( !i ) {\n\t\treturn true;\n\t}\n\n\twhile ( i-- ) {\n\t\tvar filter = config.filters[i],\n\t\t\tnot = filter.charAt(0) == '!';\n\n\t\tif ( not ) {\n\t\t\tfilter = filter.slice(1);\n\t\t}\n\n\t\tif ( name.indexOf(filter) !== -1 ) {\n\t\t\treturn !not;\n\t\t}\n\n\t\tif ( not ) {\n\t\t\trun = true;\n\t\t}\n\t}\n\n\treturn run;\n}\n\nfunction escapeHtml(s) {\n\ts = s === null ? \"\" : s + \"\";\n\treturn s.replace(/[\\&\"<>\\\\]/g, function(s) {\n\t\tswitch(s) {\n\t\t\tcase \"&\": return \"&amp;\";\n\t\t\tcase \"\\\\\": return \"\\\\\\\\\";\n\t\t\tcase '\"': return '\\\"';\n\t\t\tcase \"<\": return \"&lt;\";\n\t\t\tcase \">\": return \"&gt;\";\n\t\t\tdefault: return s;\n\t\t}\n\t});\n}\n\nfunction push(result, actual, expected, message) {\n\tmessage = escapeHtml(message) || (result ? \"okay\" : \"failed\");\n\tmessage = '<span class=\"test-message\">' + message + \"</span>\";\n\texpected = escapeHtml(QUnit.jsDump.parse(expected));\n\tactual = escapeHtml(QUnit.jsDump.parse(actual));\n\tvar output = message + ', expected: <span class=\"test-expected\">' + expected + '</span>';\n\tif (actual != expected) {\n\t\toutput += ' result: <span class=\"test-actual\">' + actual + '</span>, diff: ' + QUnit.diff(expected, actual);\n\t}\n\n\t// can't use ok, as that would double-escape messages\n\tQUnit.log(result, output);\n\tconfig.assertions.push({\n\t\tresult: !!result,\n\t\tmessage: output\n\t});\n}\n\nfunction synchronize( callback ) {\n\tconfig.queue.push( callback );\n\n\tif ( config.autorun && !config.blocking ) {\n\t\tprocess();\n\t}\n}\n\nfunction process() {\n\tvar start = (new Date()).getTime();\n\n\twhile ( config.queue.length && !config.blocking ) {\n\t\tif ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {\n\t\t\tconfig.queue.shift()();\n\n\t\t} else {\n\t\t\tsetTimeout( process, 13 );\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction saveGlobal() {\n\tconfig.pollution = [];\n\n\tif ( config.noglobals ) {\n\t\tfor ( var key in window ) {\n\t\t\tconfig.pollution.push( key );\n\t\t}\n\t}\n}\n\nfunction checkPollution( name ) {\n\tvar old = config.pollution;\n\tsaveGlobal();\n\n\tvar newGlobals = diff( old, config.pollution );\n\tif ( newGlobals.length > 0 ) {\n\t\tok( false, \"Introduced global variable(s): \" + newGlobals.join(\", \") );\n\t\tconfig.expected++;\n\t}\n\n\tvar deletedGlobals = diff( config.pollution, old );\n\tif ( deletedGlobals.length > 0 ) {\n\t\tok( false, \"Deleted global variable(s): \" + deletedGlobals.join(\", \") );\n\t\tconfig.expected++;\n\t}\n}\n\n// returns a new Array with the elements that are in a but not in b\nfunction diff( a, b ) {\n\tvar result = a.slice();\n\tfor ( var i = 0; i < result.length; i++ ) {\n\t\tfor ( var j = 0; j < b.length; j++ ) {\n\t\t\tif ( result[i] === b[j] ) {\n\t\t\t\tresult.splice(i, 1);\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction fail(message, exception, callback) {\n\tif ( typeof console !== \"undefined\" && console.error && console.warn ) {\n\t\tconsole.error(message);\n\t\tconsole.error(exception);\n\t\tconsole.warn(callback.toString());\n\n\t} else if ( window.opera && opera.postError ) {\n\t\topera.postError(message, exception, callback.toString);\n\t}\n}\n\nfunction extend(a, b) {\n\tfor ( var prop in b ) {\n\t\ta[prop] = b[prop];\n\t}\n\n\treturn a;\n}\n\nfunction addEvent(elem, type, fn) {\n\tif ( elem.addEventListener ) {\n\t\telem.addEventListener( type, fn, false );\n\t} else if ( elem.attachEvent ) {\n\t\telem.attachEvent( \"on\" + type, fn );\n\t} else {\n\t\tfn();\n\t}\n}\n\nfunction id(name) {\n\treturn !!(typeof document !== \"undefined\" && document && document.getElementById) &&\n\t\tdocument.getElementById( name );\n}\n\n// Test for equality any JavaScript type.\n// Discussions and reference: http://philrathe.com/articles/equiv\n// Test suites: http://philrathe.com/tests/equiv\n// Author: Philippe Rathé <prathe@gmail.com>\nQUnit.equiv = function () {\n\n    var innerEquiv; // the real equiv function\n    var callers = []; // stack to decide between skip/abort functions\n    var parents = []; // stack to avoiding loops from circular referencing\n\n    // Call the o related callback with the given arguments.\n    function bindCallbacks(o, callbacks, args) {\n        var prop = QUnit.objectType(o);\n        if (prop) {\n            if (QUnit.objectType(callbacks[prop]) === \"function\") {\n                return callbacks[prop].apply(callbacks, args);\n            } else {\n                return callbacks[prop]; // or undefined\n            }\n        }\n    }\n\n    var callbacks = function () {\n\n        // for string, boolean, number and null\n        function useStrictEquality(b, a) {\n            if (b instanceof a.constructor || a instanceof b.constructor) {\n                // to catch short annotaion VS 'new' annotation of a declaration\n                // e.g. var i = 1;\n                //      var j = new Number(1);\n                return a == b;\n            } else {\n                return a === b;\n            }\n        }\n\n        return {\n            \"string\": useStrictEquality,\n            \"boolean\": useStrictEquality,\n            \"number\": useStrictEquality,\n            \"null\": useStrictEquality,\n            \"undefined\": useStrictEquality,\n\n            \"nan\": function (b) {\n                return isNaN(b);\n            },\n\n            \"date\": function (b, a) {\n                return QUnit.objectType(b) === \"date\" && a.valueOf() === b.valueOf();\n            },\n\n            \"regexp\": function (b, a) {\n                return QUnit.objectType(b) === \"regexp\" &&\n                    a.source === b.source && // the regex itself\n                    a.global === b.global && // and its modifers (gmi) ...\n                    a.ignoreCase === b.ignoreCase &&\n                    a.multiline === b.multiline;\n            },\n\n            // - skip when the property is a method of an instance (OOP)\n            // - abort otherwise,\n            //   initial === would have catch identical references anyway\n            \"function\": function () {\n                var caller = callers[callers.length - 1];\n                return caller !== Object &&\n                        typeof caller !== \"undefined\";\n            },\n\n            \"array\": function (b, a) {\n                var i, j, loop;\n                var len;\n\n                // b could be an object literal here\n                if ( ! (QUnit.objectType(b) === \"array\")) {\n                    return false;\n                }\n\n                len = a.length;\n                if (len !== b.length) { // safe and faster\n                    return false;\n                }\n\n                //track reference to avoid circular references\n                parents.push(a);\n                for (i = 0; i < len; i++) {\n                    loop = false;\n                    for(j=0;j<parents.length;j++){\n                        if(parents[j] === a[i]){\n                            loop = true;//dont rewalk array\n                        }\n                    }\n                    if (!loop && ! innerEquiv(a[i], b[i])) {\n                        parents.pop();\n                        return false;\n                    }\n                }\n                parents.pop();\n                return true;\n            },\n\n            \"object\": function (b, a) {\n                var i, j, loop;\n                var eq = true; // unless we can proove it\n                var aProperties = [], bProperties = []; // collection of strings\n\n                // comparing constructors is more strict than using instanceof\n                if ( a.constructor !== b.constructor) {\n                    return false;\n                }\n\n                // stack constructor before traversing properties\n                callers.push(a.constructor);\n                //track reference to avoid circular references\n                parents.push(a);\n\n                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep\n                    loop = false;\n                    for(j=0;j<parents.length;j++){\n                        if(parents[j] === a[i])\n                            loop = true; //don't go down the same path twice\n                    }\n                    aProperties.push(i); // collect a's properties\n\n                    if (!loop && ! innerEquiv(a[i], b[i])) {\n                        eq = false;\n                        break;\n                    }\n                }\n\n                callers.pop(); // unstack, we are done\n                parents.pop();\n\n                for (i in b) {\n                    bProperties.push(i); // collect b's properties\n                }\n\n                // Ensures identical properties name\n                return eq && innerEquiv(aProperties.sort(), bProperties.sort());\n            }\n        };\n    }();\n\n    innerEquiv = function () { // can take multiple arguments\n        var args = Array.prototype.slice.apply(arguments);\n        if (args.length < 2) {\n            return true; // end transition\n        }\n\n        return (function (a, b) {\n            if (a === b) {\n                return true; // catch the most you can\n            } else if (a === null || b === null || typeof a === \"undefined\" || typeof b === \"undefined\" || QUnit.objectType(a) !== QUnit.objectType(b)) {\n                return false; // don't lose time with error prone cases\n            } else {\n                return bindCallbacks(a, callbacks, [b, a]);\n            }\n\n        // apply transition with (1..n) arguments\n        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));\n    };\n\n    return innerEquiv;\n\n}();\n\n/**\n * jsDump\n * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com\n * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)\n * Date: 5/15/2008\n * @projectDescription Advanced and extensible data dumping for Javascript.\n * @version 1.0.0\n * @author Ariel Flesler\n * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}\n */\nQUnit.jsDump = (function() {\n\tfunction quote( str ) {\n\t\treturn '\"' + str.toString().replace(/\"/g, '\\\\\"') + '\"';\n\t};\n\tfunction literal( o ) {\n\t\treturn o + '';\n\t};\n\tfunction join( pre, arr, post ) {\n\t\tvar s = jsDump.separator(),\n\t\t\tbase = jsDump.indent(),\n\t\t\tinner = jsDump.indent(1);\n\t\tif ( arr.join )\n\t\t\tarr = arr.join( ',' + s + inner );\n\t\tif ( !arr )\n\t\t\treturn pre + post;\n\t\treturn [ pre, inner + arr, base + post ].join(s);\n\t};\n\tfunction array( arr ) {\n\t\tvar i = arr.length,\tret = Array(i);\n\t\tthis.up();\n\t\twhile ( i-- )\n\t\t\tret[i] = this.parse( arr[i] );\n\t\tthis.down();\n\t\treturn join( '[', ret, ']' );\n\t};\n\n\tvar reName = /^function (\\w+)/;\n\n\tvar jsDump = {\n\t\tparse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance\n\t\t\tvar\tparser = this.parsers[ type || this.typeOf(obj) ];\n\t\t\ttype = typeof parser;\n\n\t\t\treturn type == 'function' ? parser.call( this, obj ) :\n\t\t\t\t   type == 'string' ? parser :\n\t\t\t\t   this.parsers.error;\n\t\t},\n\t\ttypeOf:function( obj ) {\n\t\t\tvar type;\n\t\t\tif ( obj === null ) {\n\t\t\t\ttype = \"null\";\n\t\t\t} else if (typeof obj === \"undefined\") {\n\t\t\t\ttype = \"undefined\";\n\t\t\t} else if (QUnit.is(\"RegExp\", obj)) {\n\t\t\t\ttype = \"regexp\";\n\t\t\t} else if (QUnit.is(\"Date\", obj)) {\n\t\t\t\ttype = \"date\";\n\t\t\t} else if (QUnit.is(\"Function\", obj)) {\n\t\t\t\ttype = \"function\";\n\t\t\t} else if (obj.setInterval && obj.document && !obj.nodeType) {\n\t\t\t\ttype = \"window\";\n\t\t\t} else if (obj.nodeType === 9) {\n\t\t\t\ttype = \"document\";\n\t\t\t} else if (obj.nodeType) {\n\t\t\t\ttype = \"node\";\n\t\t\t} else if (typeof obj === \"object\" && typeof obj.length === \"number\" && obj.length >= 0) {\n\t\t\t\ttype = \"array\";\n\t\t\t} else {\n\t\t\t\ttype = typeof obj;\n\t\t\t}\n\t\t\treturn type;\n\t\t},\n\t\tseparator:function() {\n\t\t\treturn this.multiline ?\tthis.HTML ? '<br />' : '\\n' : this.HTML ? '&nbsp;' : ' ';\n\t\t},\n\t\tindent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing\n\t\t\tif ( !this.multiline )\n\t\t\t\treturn '';\n\t\t\tvar chr = this.indentChar;\n\t\t\tif ( this.HTML )\n\t\t\t\tchr = chr.replace(/\\t/g,'   ').replace(/ /g,'&nbsp;');\n\t\t\treturn Array( this._depth_ + (extra||0) ).join(chr);\n\t\t},\n\t\tup:function( a ) {\n\t\t\tthis._depth_ += a || 1;\n\t\t},\n\t\tdown:function( a ) {\n\t\t\tthis._depth_ -= a || 1;\n\t\t},\n\t\tsetParser:function( name, parser ) {\n\t\t\tthis.parsers[name] = parser;\n\t\t},\n\t\t// The next 3 are exposed so you can use them\n\t\tquote:quote,\n\t\tliteral:literal,\n\t\tjoin:join,\n\t\t//\n\t\t_depth_: 1,\n\t\t// This is the list of parsers, to modify them, use jsDump.setParser\n\t\tparsers:{\n\t\t\twindow: '[Window]',\n\t\t\tdocument: '[Document]',\n\t\t\terror:'[ERROR]', //when no parser is found, shouldn't happen\n\t\t\tunknown: '[Unknown]',\n\t\t\t'null':'null',\n\t\t\tundefined:'undefined',\n\t\t\t'function':function( fn ) {\n\t\t\t\tvar ret = 'function',\n\t\t\t\t\tname = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE\n\t\t\t\tif ( name )\n\t\t\t\t\tret += ' ' + name;\n\t\t\t\tret += '(';\n\n\t\t\t\tret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');\n\t\t\t\treturn join( ret, this.parse(fn,'functionCode'), '}' );\n\t\t\t},\n\t\t\tarray: array,\n\t\t\tnodelist: array,\n\t\t\targuments: array,\n\t\t\tobject:function( map ) {\n\t\t\t\tvar ret = [ ];\n\t\t\t\tthis.up();\n\t\t\t\tfor ( var key in map )\n\t\t\t\t\tret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );\n\t\t\t\tthis.down();\n\t\t\t\treturn join( '{', ret, '}' );\n\t\t\t},\n\t\t\tnode:function( node ) {\n\t\t\t\tvar open = this.HTML ? '&lt;' : '<',\n\t\t\t\t\tclose = this.HTML ? '&gt;' : '>';\n\n\t\t\t\tvar tag = node.nodeName.toLowerCase(),\n\t\t\t\t\tret = open + tag;\n\n\t\t\t\tfor ( var a in this.DOMAttrs ) {\n\t\t\t\t\tvar val = node[this.DOMAttrs[a]];\n\t\t\t\t\tif ( val )\n\t\t\t\t\t\tret += ' ' + a + '=' + this.parse( val, 'attribute' );\n\t\t\t\t}\n\t\t\t\treturn ret + close + open + '/' + tag + close;\n\t\t\t},\n\t\t\tfunctionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function\n\t\t\t\tvar l = fn.length;\n\t\t\t\tif ( !l ) return '';\n\n\t\t\t\tvar args = Array(l);\n\t\t\t\twhile ( l-- )\n\t\t\t\t\targs[l] = String.fromCharCode(97+l);//97 is 'a'\n\t\t\t\treturn ' ' + args.join(', ') + ' ';\n\t\t\t},\n\t\t\tkey:quote, //object calls it internally, the key part of an item in a map\n\t\t\tfunctionCode:'[code]', //function calls it internally, it's the content of the function\n\t\t\tattribute:quote, //node calls it internally, it's an html attribute value\n\t\t\tstring:quote,\n\t\t\tdate:quote,\n\t\t\tregexp:literal, //regex\n\t\t\tnumber:literal,\n\t\t\t'boolean':literal\n\t\t},\n\t\tDOMAttrs:{//attributes to dump from nodes, name=>realName\n\t\t\tid:'id',\n\t\t\tname:'name',\n\t\t\t'class':'className'\n\t\t},\n\t\tHTML:false,//if true, entities are escaped ( <, >, \\t, space and \\n )\n\t\tindentChar:'   ',//indentation unit\n\t\tmultiline:false //if true, items in a collection, are separated by a \\n, else just a space.\n\t};\n\n\treturn jsDump;\n})();\n\n// from Sizzle.js\nfunction getText( elems ) {\n\tvar ret = \"\", elem;\n\n\tfor ( var i = 0; elems[i]; i++ ) {\n\t\telem = elems[i];\n\n\t\t// Get the text from text nodes and CDATA nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 4 ) {\n\t\t\tret += elem.nodeValue;\n\n\t\t// Traverse everything else, except comment nodes\n\t\t} else if ( elem.nodeType !== 8 ) {\n\t\t\tret += getText( elem.childNodes );\n\t\t}\n\t}\n\n\treturn ret;\n};\n\n/*\n * Javascript Diff Algorithm\n *  By John Resig (http://ejohn.org/)\n *  Modified by Chu Alan \"sprite\"\n *\n * Released under the MIT license.\n *\n * More Info:\n *  http://ejohn.org/projects/javascript-diff-algorithm/\n *\n * Usage: QUnit.diff(expected, actual)\n *\n * QUnit.diff(\"the quick brown fox jumped over\", \"the quick fox jumps over\") == \"the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over\"\n */\nQUnit.diff = (function() {\n\tfunction diff(o, n){\n\t\tvar ns = new Object();\n\t\tvar os = new Object();\n\n\t\tfor (var i = 0; i < n.length; i++) {\n\t\t\tif (ns[n[i]] == null)\n\t\t\t\tns[n[i]] = {\n\t\t\t\t\trows: new Array(),\n\t\t\t\t\to: null\n\t\t\t\t};\n\t\t\tns[n[i]].rows.push(i);\n\t\t}\n\n\t\tfor (var i = 0; i < o.length; i++) {\n\t\t\tif (os[o[i]] == null)\n\t\t\t\tos[o[i]] = {\n\t\t\t\t\trows: new Array(),\n\t\t\t\t\tn: null\n\t\t\t\t};\n\t\t\tos[o[i]].rows.push(i);\n\t\t}\n\n\t\tfor (var i in ns) {\n\t\t\tif (ns[i].rows.length == 1 && typeof(os[i]) != \"undefined\" && os[i].rows.length == 1) {\n\t\t\t\tn[ns[i].rows[0]] = {\n\t\t\t\t\ttext: n[ns[i].rows[0]],\n\t\t\t\t\trow: os[i].rows[0]\n\t\t\t\t};\n\t\t\t\to[os[i].rows[0]] = {\n\t\t\t\t\ttext: o[os[i].rows[0]],\n\t\t\t\t\trow: ns[i].rows[0]\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0; i < n.length - 1; i++) {\n\t\t\tif (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&\n\t\t\tn[i + 1] == o[n[i].row + 1]) {\n\t\t\t\tn[i + 1] = {\n\t\t\t\t\ttext: n[i + 1],\n\t\t\t\t\trow: n[i].row + 1\n\t\t\t\t};\n\t\t\t\to[n[i].row + 1] = {\n\t\t\t\t\ttext: o[n[i].row + 1],\n\t\t\t\t\trow: i + 1\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = n.length - 1; i > 0; i--) {\n\t\t\tif (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&\n\t\t\tn[i - 1] == o[n[i].row - 1]) {\n\t\t\t\tn[i - 1] = {\n\t\t\t\t\ttext: n[i - 1],\n\t\t\t\t\trow: n[i].row - 1\n\t\t\t\t};\n\t\t\t\to[n[i].row - 1] = {\n\t\t\t\t\ttext: o[n[i].row - 1],\n\t\t\t\t\trow: i - 1\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\to: o,\n\t\t\tn: n\n\t\t};\n\t}\n\n\treturn function(o, n){\n\t\to = o.replace(/\\s+$/, '');\n\t\tn = n.replace(/\\s+$/, '');\n\t\tvar out = diff(o == \"\" ? [] : o.split(/\\s+/), n == \"\" ? [] : n.split(/\\s+/));\n\n\t\tvar str = \"\";\n\n\t\tvar oSpace = o.match(/\\s+/g);\n\t\tif (oSpace == null) {\n\t\t\toSpace = [\" \"];\n\t\t}\n\t\telse {\n\t\t\toSpace.push(\" \");\n\t\t}\n\t\tvar nSpace = n.match(/\\s+/g);\n\t\tif (nSpace == null) {\n\t\t\tnSpace = [\" \"];\n\t\t}\n\t\telse {\n\t\t\tnSpace.push(\" \");\n\t\t}\n\n\t\tif (out.n.length == 0) {\n\t\t\tfor (var i = 0; i < out.o.length; i++) {\n\t\t\t\tstr += '<del>' + out.o[i] + oSpace[i] + \"</del>\";\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (out.n[0].text == null) {\n\t\t\t\tfor (n = 0; n < out.o.length && out.o[n].text == null; n++) {\n\t\t\t\t\tstr += '<del>' + out.o[n] + oSpace[n] + \"</del>\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (var i = 0; i < out.n.length; i++) {\n\t\t\t\tif (out.n[i].text == null) {\n\t\t\t\t\tstr += '<ins>' + out.n[i] + nSpace[i] + \"</ins>\";\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tvar pre = \"\";\n\n\t\t\t\t\tfor (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {\n\t\t\t\t\t\tpre += '<del>' + out.o[n] + oSpace[n] + \"</del>\";\n\t\t\t\t\t}\n\t\t\t\t\tstr += \" \" + out.n[i].text + nSpace[i] + pre;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn str;\n\t}\n})();\n\n})(this);\n"
  },
  {
    "path": "test/titanium/Resources/qunit/titanium_adaptor.js",
    "content": "Titanium.include('qunit/qunit.js');\n\n// =============================================================================\n// Uncomment the following lines in order to get jsMockito support for mocking\n// (after following jsMockito install instructions)\n// =============================================================================\n\n// Titanium.include('qunit/jshamcrest.js');\n// Titanium.include('qunit/jsmockito-1.0.2.js');\n// JsHamcrest.Integration.QUnit();\n// JsMockito.Integration.importTo(this);\n\nvar logger = function(failures, message) {\n\tif (failures) {\n\t\tTitanium.API.error(message);\n\t} else {\n\t\tTitanium.API.info(message);\n\t}\n};\n// QUnit.testStart(name) is called whenever a new test batch of assertions starts running. name is the string name of the test batch.\nQUnit.testStart = function(name) {\n\tlogger(false, '>> >> >>TEST START: '+name);\n};\n// QUnit.testDone(name, failures, total) is called whenever a batch of assertions finishes running. name is the string name of the test batch. failures is the number of test failures that occurred. total is the total number of test assertions that occurred.\nQUnit.testDone = function(name, failures, total) {\n\tlogger(failures, '<< << <<TEST DONE : '+name+' FAILURES: '+failures+' out of TOTAL: '+total);\n};\n// QUnit.moduleStart(name) is called whenever a new module of tests starts running. name is the string name of the module.\nQUnit.moduleStart = function(name) {\n\tlogger(false, '>> >>MODULE START: '+name);\n};\n// QUnit.moduleDone(name, failures, total) is called whenever a module finishes running. name is the string name of the module. failures is the number of module failures that occurred. total is the total number of module assertions that occurred.\nQUnit.moduleDone = function(name, failures, total) {\n\tlogger(failures, '<< <<MODULE DONE : '+name+' FAILURES: '+failures+' out of TOTAL: '+total);\n};\n// QUnit.begin() is called once before running any tests. It should have been called QUnit.start, but thats already in use elsewhere and can't be changed.\nQUnit.begin = function() {\n\tlogger(false, 'BEGIN');\n};\n// QUnit.done(failures, total) is called whenever all the tests have finished running. failures is the number of failures that occurred. total is the total number of assertions that occurred.\nQUnit.done = function(failures, total) {\n\tlogger(failures, 'DONE : FAILURES: '+failures+' out of TOTAL: '+total);\n};\n\n// QUnit.log(result, message) is called whenever an assertion is completed. result is a boolean (true for passing, false for failing) and message is a string description provided by the assertion.\n\nQUnit.log = function(result, message) {\n\tif (!result) {\n\t    logger(true, message);\n\t} else {\n\t    Titanium.API.info(message);\n\t}\n};\n\n// Tests to run\nTitanium.include('test/tests_to_run.js');"
  },
  {
    "path": "test/titanium/Resources/runner.js",
    "content": "// This file needs to sit in the Resources directory so that when\n// it is used as a URL to a window, the include structure doesn't change.\nTitanium.include('qunit/titanium_adaptor.js');"
  },
  {
    "path": "test/titanium/Resources/test/tests_to_run.js",
    "content": "//setting up stuff so that the environment kind of looks like a browser\nvar window = {};\nvar console = {\n  log: function() {\n    Titanium.API.debug(arguments[0]);\n  }\n};\nvar document = null;\n$ = function(document) {\n  return {ready: function(f) {f();}};\n};\n\n//requiring persistencejs\nTitanium.include('lib/persistence.js',\n                 'lib/persistence.store.sql.js',\n                 'lib/persistence.store.titanium.js');\nvar persistence = window.persistence;\n//allows us to run unmodified browser tests in titanium\npersistence.store.websql = persistence.store.titanium;\n\n\n// Tests to run\nTitanium.include('test/browser/test.persistence.js');\n//Titanium.include('test/browser/util.js');\n//Titanium.include('test/browser/test.migrations.js');\n"
  },
  {
    "path": "test/titanium/manifest",
    "content": "#appname: titanium\n#publisher: staugaard\n#url: https://github.com/zefhemel/persistencejs\n#image: appicon.png\n#appid: persistencejs.titanium.test\n#desc: undefined\n#type: mobile\n#guid: add78b91-427a-456d-9d6f-e21a272adf95\n"
  },
  {
    "path": "test/titanium/tiapp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ti:app xmlns:ti=\"http://ti.appcelerator.org\">\n<id>persistencejs.titanium.test</id>\n<name>titanium</name>\n<version>1.0</version>\n<publisher>staugaard</publisher>\n<url>https://github.com/zefhemel/persistencejs</url>\n<description>No description provided</description>\n<copyright>2011 by staugaard</copyright>\n<icon>default_app_logo.png</icon>\n    <persistent-wifi>false</persistent-wifi>\n    <prerendered-icon>false</prerendered-icon>\n    <statusbar-style>default</statusbar-style>\n    <statusbar-hidden>false</statusbar-hidden>\n    <fullscreen>false</fullscreen>\n    <navbar-hidden>false</navbar-hidden>\n    <analytics>true</analytics>\n<guid>add78b91-427a-456d-9d6f-e21a272adf95</guid>\n\t<iphone>\n\t\t<orientations device=\"iphone\">\n\t\t\t<orientation>Ti.UI.PORTRAIT</orientation>\n\t\t</orientations>\n\t\t<orientations device=\"ipad\">\n\t\t\t<orientation>Ti.UI.PORTRAIT</orientation>\n\t\t\t<orientation>Ti.UI.UPSIDE_PORTRAIT</orientation>\n\t\t\t<orientation>Ti.UI.LANDSCAPE_LEFT</orientation>\n\t\t\t<orientation>Ti.UI.LANDSCAPE_RIGHT</orientation>\n\t\t</orientations>\n\t</iphone>\n\t<android xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t</android>\n\t<modules>\n\t</modules>\n</ti:app>\n"
  }
]