UNPKG

forerunnerdb

Version:

A NoSQL document store database for browsers and Node.js.

1,176 lines (982 loc) 32.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: View.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: View.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; // Import external names locally var Shared, Db, Collection, CollectionGroup, CollectionInit, DbInit, ReactorIO, ActiveBucket; Shared = require('./Shared'); /** * Creates a new view instance. * @param {String} name The name of the view. * @param {Object=} query The view's query. * @param {Object=} options An options object. * @constructor */ var View = function (name, query, options) { this.init.apply(this, arguments); }; View.prototype.init = function (name, query, options) { var self = this; this._name = name; this._listeners = {}; this._querySettings = {}; this._debug = {}; this.query(query, false); this.queryOptions(options, false); this._collectionDroppedWrap = function () { self._collectionDropped.apply(self, arguments); }; this._privateData = new Collection(this.name() + '_internalPrivate'); }; Shared.addModule('View', View); Shared.mixin(View.prototype, 'Mixin.Common'); Shared.mixin(View.prototype, 'Mixin.ChainReactor'); Shared.mixin(View.prototype, 'Mixin.Constants'); Shared.mixin(View.prototype, 'Mixin.Triggers'); Collection = require('./Collection'); CollectionGroup = require('./CollectionGroup'); ActiveBucket = require('./ActiveBucket'); ReactorIO = require('./ReactorIO'); CollectionInit = Collection.prototype.init; Db = Shared.modules.Db; DbInit = Db.prototype.init; /** * Gets / sets the current state. * @param {String=} val The name of the state to set. * @returns {*} */ Shared.synthesize(View.prototype, 'state'); /** * Gets / sets the current name. * @param {String=} val The new name to set. * @returns {*} */ Shared.synthesize(View.prototype, 'name'); /** * Gets / sets the current cursor. * @param {String=} val The new cursor to set. * @returns {*} */ Shared.synthesize(View.prototype, 'cursor', function (val) { if (val === undefined) { return this._cursor || {}; } this.$super.apply(this, arguments); }); /** * Executes an insert against the view's underlying data-source. * @see Collection::insert() */ View.prototype.insert = function () { this._from.insert.apply(this._from, arguments); }; /** * Executes an update against the view's underlying data-source. * @see Collection::update() */ View.prototype.update = function () { this._from.update.apply(this._from, arguments); }; /** * Executes an updateById against the view's underlying data-source. * @see Collection::updateById() */ View.prototype.updateById = function () { this._from.updateById.apply(this._from, arguments); }; /** * Executes a remove against the view's underlying data-source. * @see Collection::remove() */ View.prototype.remove = function () { this._from.remove.apply(this._from, arguments); }; /** * Queries the view data. * @see Collection::find() * @returns {Array} The result of the find query. */ View.prototype.find = function (query, options) { return this.publicData().find(query, options); }; /** * Queries the view data by specific id. * @see Collection::findById() * @returns {Array} The result of the find query. */ View.prototype.findById = function (id, options) { return this.publicData().findById(id, options); }; /** * Gets the module's internal data collection. * @returns {Collection} */ View.prototype.data = function () { return this._privateData; }; /** * Sets the source from which the view will assemble its data. * @param {Collection|View} source The source to use to assemble view data. * @returns {*} If no argument is passed, returns the current value of from, * otherwise returns itself for chaining. */ View.prototype.from = function (source) { var self = this; if (source !== undefined) { // Check if we have an existing from if (this._from) { // Remove the listener to the drop event this._from.off('drop', this._collectionDroppedWrap); delete this._from; } if (typeof(source) === 'string') { source = this._db.collection(source); } if (source.className === 'View') { // The source is a view so IO to the internal data collection // instead of the view proper source = source.privateData(); if (this.debug()) { console.log(this.logIdentifier() + ' Using internal private data "' + source.instanceIdentifier() + '" for IO graph linking'); } } this._from = source; this._from.on('drop', this._collectionDroppedWrap); // Create a new reactor IO graph node that intercepts chain packets from the // view's "from" source and determines how they should be interpreted by // this view. If the view does not have a query then this reactor IO will // simply pass along the chain packet without modifying it. this._io = new ReactorIO(source, this, function (chainPacket) { var data, diff, query, filteredData, doSend, pk, i; // Check that the state of the "self" object is not dropped if (self &amp;&amp; !self.isDropped()) { // Check if we have a constraining query if (self._querySettings.query) { if (chainPacket.type === 'insert') { data = chainPacket.data; // Check if the data matches our query if (data instanceof Array) { filteredData = []; for (i = 0; i &lt; data.length; i++) { if (self._privateData._match(data[i], self._querySettings.query, self._querySettings.options, 'and', {})) { filteredData.push(data[i]); doSend = true; } } } else { if (self._privateData._match(data, self._querySettings.query, self._querySettings.options, 'and', {})) { filteredData = data; doSend = true; } } if (doSend) { this.chainSend('insert', filteredData); } return true; } if (chainPacket.type === 'update') { // Do a DB diff between this view's data and the underlying collection it reads from // to see if something has changed diff = self._privateData.diff(self._from.subset(self._querySettings.query, self._querySettings.options)); if (diff.insert.length || diff.remove.length) { // Now send out new chain packets for each operation if (diff.insert.length) { this.chainSend('insert', diff.insert); } if (diff.update.length) { pk = self._privateData.primaryKey(); for (i = 0; i &lt; diff.update.length; i++) { query = {}; query[pk] = diff.update[i][pk]; this.chainSend('update', { query: query, update: diff.update[i] }); } } if (diff.remove.length) { pk = self._privateData.primaryKey(); var $or = [], removeQuery = { query: { $or: $or } }; for (i = 0; i &lt; diff.remove.length; i++) { $or.push({_id: diff.remove[i][pk]}); } this.chainSend('remove', removeQuery); } // Return true to stop further propagation of the chain packet return true; } else { // Returning false informs the chain reactor to continue propagation // of the chain packet down the graph tree return false; } } } } // Returning false informs the chain reactor to continue propagation // of the chain packet down the graph tree return false; }); var collData = source.find(this._querySettings.query, this._querySettings.options); this._transformPrimaryKey(source.primaryKey()); this._transformSetData(collData); this._privateData.primaryKey(source.primaryKey()); this._privateData.setData(collData); if (this._querySettings.options &amp;&amp; this._querySettings.options.$orderBy) { this.rebuildActiveBucket(this._querySettings.options.$orderBy); } else { this.rebuildActiveBucket(); } return this; } return this._from; }; /** * Handles when an underlying collection the view is using as a data * source is dropped. * @param {Collection} collection The collection that has been dropped. * @private */ View.prototype._collectionDropped = function (collection) { if (collection) { // Collection was dropped, remove from view delete this._from; } }; /** * Creates an index on the view. * @see Collection::ensureIndex() * @returns {*} */ View.prototype.ensureIndex = function () { return this._privateData.ensureIndex.apply(this._privateData, arguments); }; /** * The chain reaction handler method for the view. * @param {Object} chainPacket The chain reaction packet to handle. * @private */ View.prototype._chainHandler = function (chainPacket) { var //self = this, arr, count, index, insertIndex, //tempData, //dataIsArray, updates, //finalUpdates, primaryKey, tQuery, item, currentIndex, i; if (this.debug()) { console.log(this.logIdentifier() + ' Received chain reactor data'); } switch (chainPacket.type) { case 'setData': if (this.debug()) { console.log(this.logIdentifier() + ' Setting data in underlying (internal) view collection "' + this._privateData.name() + '"'); } // Get the new data from our underlying data source sorted as we want var collData = this._from.find(this._querySettings.query, this._querySettings.options); // Modify transform data this._transformSetData(collData); this._privateData.setData(collData); break; case 'insert': if (this.debug()) { console.log(this.logIdentifier() + ' Inserting some data into underlying (internal) view collection "' + this._privateData.name() + '"'); } // Decouple the data to ensure we are working with our own copy chainPacket.data = this.decouple(chainPacket.data); // Make sure we are working with an array if (!(chainPacket.data instanceof Array)) { chainPacket.data = [chainPacket.data]; } if (this._querySettings.options &amp;&amp; this._querySettings.options.$orderBy) { // Loop the insert data and find each item's index arr = chainPacket.data; count = arr.length; for (index = 0; index &lt; count; index++) { insertIndex = this._activeBucket.insert(arr[index]); // Modify transform data this._transformInsert(chainPacket.data, insertIndex); this._privateData._insertHandle(chainPacket.data, insertIndex); } } else { // Set the insert index to the passed index, or if none, the end of the view data array insertIndex = this._privateData._data.length; // Modify transform data this._transformInsert(chainPacket.data, insertIndex); this._privateData._insertHandle(chainPacket.data, insertIndex); } break; case 'update': if (this.debug()) { console.log(this.logIdentifier() + ' Updating some data in underlying (internal) view collection "' + this._privateData.name() + '"'); } primaryKey = this._privateData.primaryKey(); // Do the update updates = this._privateData.update( chainPacket.data.query, chainPacket.data.update, chainPacket.data.options ); if (this._querySettings.options &amp;&amp; this._querySettings.options.$orderBy) { // TODO: This would be a good place to improve performance by somehow // TODO: inspecting the change that occurred when update was performed // TODO: above and determining if it affected the order clause keys // TODO: and if not, skipping the active bucket updates here // Loop the updated items and work out their new sort locations count = updates.length; for (index = 0; index &lt; count; index++) { item = updates[index]; // Remove the item from the active bucket (via it's id) this._activeBucket.remove(item); // Get the current location of the item currentIndex = this._privateData._data.indexOf(item); // Add the item back in to the active bucket insertIndex = this._activeBucket.insert(item); if (currentIndex !== insertIndex) { // Move the updated item to the new index this._privateData._updateSpliceMove(this._privateData._data, currentIndex, insertIndex); } } } if (this._transformEnabled &amp;&amp; this._transformIn) { primaryKey = this._publicData.primaryKey(); for (i = 0; i &lt; updates.length; i++) { tQuery = {}; item = updates[i]; tQuery[primaryKey] = item[primaryKey]; this._transformUpdate(tQuery, item); } } break; case 'remove': if (this.debug()) { console.log(this.logIdentifier() + ' Removing some data from underlying (internal) view collection "' + this._privateData.name() + '"'); } // Modify transform data this._transformRemove(chainPacket.data.query, chainPacket.options); this._privateData.remove(chainPacket.data.query, chainPacket.options); break; default: break; } }; /** * Listens for an event. * @see Mixin.Events::on() */ View.prototype.on = function () { return this._privateData.on.apply(this._privateData, arguments); }; /** * Cancels an event listener. * @see Mixin.Events::off() */ View.prototype.off = function () { return this._privateData.off.apply(this._privateData, arguments); }; /** * Emits an event. * @see Mixin.Events::emit() */ View.prototype.emit = function () { return this._privateData.emit.apply(this._privateData, arguments); }; /** * Find the distinct values for a specified field across a single collection and * returns the results in an array. * @param {String} key The field path to return distinct values for e.g. "person.name". * @param {Object=} query The query to use to filter the documents used to return values from. * @param {Object=} options The query options to use when running the query. * @returns {Array} */ View.prototype.distinct = function (key, query, options) { return this._privateData.distinct.apply(this._privateData, arguments); }; /** * Gets the primary key for this view from the assigned collection. * @see Collection::primaryKey() * @returns {String} */ View.prototype.primaryKey = function () { return this._privateData.primaryKey(); }; /** * Drops a view and all it's stored data from the database. * @returns {boolean} True on success, false on failure. */ View.prototype.drop = function () { if (!this.isDropped()) { if (this._from) { this._from.off('drop', this._collectionDroppedWrap); this._from._removeView(this); } if (this.debug() || (this._db &amp;&amp; this._db.debug())) { console.log(this.logIdentifier() + ' Dropping'); } this._state = 'dropped'; // Clear io and chains if (this._io) { this._io.drop(); } // Drop the view's internal collection if (this._privateData) { this._privateData.drop(); } if (this._publicData &amp;&amp; this._publicData !== this._privateData) { this._publicData.drop(); } if (this._db &amp;&amp; this._name) { delete this._db._view[this._name]; } this.emit('drop', this); delete this._chain; delete this._from; delete this._privateData; delete this._io; delete this._listeners; delete this._querySettings; delete this._db; return true; } else { return true; } return false; }; /** * Gets / sets the db instance this class instance belongs to. * @param {Db=} db The db instance. * @memberof View * @returns {*} */ Shared.synthesize(View.prototype, 'db', function (db) { if (db) { this.privateData().db(db); this.publicData().db(db); // Apply the same debug settings this.debug(db.debug()); this.privateData().debug(db.debug()); this.publicData().debug(db.debug()); } return this.$super.apply(this, arguments); }); /** * Gets / sets the query object and query options that the view uses * to build it's data set. This call modifies both the query and * query options at the same time. * @param {Object=} query The query to set. * @param {Boolean=} options The query options object. * @param {Boolean=} refresh Whether to refresh the view data after * this operation. Defaults to true. * @returns {*} */ View.prototype.queryData = function (query, options, refresh) { if (query !== undefined) { this._querySettings.query = query; } if (options !== undefined) { this._querySettings.options = options; } if (query !== undefined || options !== undefined) { if (refresh === undefined || refresh === true) { this.refresh(); } return this; } return this._querySettings; }; /** * Add data to the existing query. * @param {Object} obj The data whose keys will be added to the existing * query object. * @param {Boolean} overwrite Whether or not to overwrite data that already * exists in the query object. Defaults to true. * @param {Boolean=} refresh Whether or not to refresh the view data set * once the operation is complete. Defaults to true. */ View.prototype.queryAdd = function (obj, overwrite, refresh) { this._querySettings.query = this._querySettings.query || {}; var query = this._querySettings.query, i; if (obj !== undefined) { // Loop object properties and add to existing query for (i in obj) { if (obj.hasOwnProperty(i)) { if (query[i] === undefined || (query[i] !== undefined &amp;&amp; overwrite !== false)) { query[i] = obj[i]; } } } } if (refresh === undefined || refresh === true) { this.refresh(); } }; /** * Remove data from the existing query. * @param {Object} obj The data whose keys will be removed from the existing * query object. * @param {Boolean=} refresh Whether or not to refresh the view data set * once the operation is complete. Defaults to true. */ View.prototype.queryRemove = function (obj, refresh) { var query = this._querySettings.query, i; if (query) { if (obj !== undefined) { // Loop object properties and add to existing query for (i in obj) { if (obj.hasOwnProperty(i)) { delete query[i]; } } } if (refresh === undefined || refresh === true) { this.refresh(); } } }; /** * Gets / sets the query being used to generate the view data. It * does not change or modify the view's query options. * @param {Object=} query The query to set. * @param {Boolean=} refresh Whether to refresh the view data after * this operation. Defaults to true. * @returns {*} */ View.prototype.query = function (query, refresh) { if (query !== undefined) { this._querySettings.query = query; if (refresh === undefined || refresh === true) { this.refresh(); } return this; } return this._querySettings.query; }; /** * Gets / sets the orderBy clause in the query options for the view. * @param {Object=} val The order object. * @returns {*} */ View.prototype.orderBy = function (val) { if (val !== undefined) { var queryOptions = this.queryOptions() || {}; queryOptions.$orderBy = val; this.queryOptions(queryOptions); return this; } return (this.queryOptions() || {}).$orderBy; }; /** * Gets / sets the page clause in the query options for the view. * @param {Number=} val The page number to change to (zero index). * @returns {*} */ View.prototype.page = function (val) { if (val !== undefined) { var queryOptions = this.queryOptions() || {}; // Only execute a query options update if page has changed if (val !== queryOptions.$page) { queryOptions.$page = val; this.queryOptions(queryOptions); } return this; } return (this.queryOptions() || {}).$page; }; /** * Jump to the first page in the data set. * @returns {*} */ View.prototype.pageFirst = function () { return this.page(0); }; /** * Jump to the last page in the data set. * @returns {*} */ View.prototype.pageLast = function () { var pages = this.cursor().pages, lastPage = pages !== undefined ? pages : 0; return this.page(lastPage - 1); }; /** * Move forward or backwards in the data set pages by passing a positive * or negative integer of the number of pages to move. * @param {Number} val The number of pages to move. * @returns {*} */ View.prototype.pageScan = function (val) { if (val !== undefined) { var pages = this.cursor().pages, queryOptions = this.queryOptions() || {}, currentPage = queryOptions.$page !== undefined ? queryOptions.$page : 0; currentPage += val; if (currentPage &lt; 0) { currentPage = 0; } if (currentPage >= pages) { currentPage = pages - 1; } return this.page(currentPage); } }; /** * Gets / sets the query options used when applying sorting etc to the * view data set. * @param {Object=} options An options object. * @param {Boolean=} refresh Whether to refresh the view data after * this operation. Defaults to true. * @returns {*} */ View.prototype.queryOptions = function (options, refresh) { if (options !== undefined) { this._querySettings.options = options; if (options.$decouple === undefined) { options.$decouple = true; } if (refresh === undefined || refresh === true) { this.refresh(); } else { this.rebuildActiveBucket(options.$orderBy); } return this; } return this._querySettings.options; }; View.prototype.rebuildActiveBucket = function (orderBy) { if (orderBy) { var arr = this._privateData._data, arrCount = arr.length; // Build a new active bucket this._activeBucket = new ActiveBucket(orderBy); this._activeBucket.primaryKey(this._privateData.primaryKey()); // Loop the current view data and add each item for (var i = 0; i &lt; arrCount; i++) { this._activeBucket.insert(arr[i]); } } else { // Remove any existing active bucket delete this._activeBucket; } }; /** * Refreshes the view data such as ordering etc. */ View.prototype.refresh = function () { if (this._from) { var pubData = this.publicData(), refreshResults; // Re-grab all the data for the view from the collection this._privateData.remove(); pubData.remove(); refreshResults = this._from.find(this._querySettings.query, this._querySettings.options); this.cursor(refreshResults.$cursor); this._privateData.insert(refreshResults); this._privateData._data.$cursor = refreshResults.$cursor; pubData._data.$cursor = refreshResults.$cursor; /*if (pubData._linked) { // Update data and observers //var transformedData = this._privateData.find(); // TODO: Shouldn't this data get passed into a transformIn first? // TODO: This breaks linking because its passing decoupled data and overwriting non-decoupled data // TODO: Is this even required anymore? After commenting it all seems to work // TODO: Might be worth setting up a test to check transforms and linking then remove this if working? //jQuery.observable(pubData._data).refresh(transformedData); }*/ } if (this._querySettings.options &amp;&amp; this._querySettings.options.$orderBy) { this.rebuildActiveBucket(this._querySettings.options.$orderBy); } else { this.rebuildActiveBucket(); } return this; }; /** * Returns the number of documents currently in the view. * @returns {Number} */ View.prototype.count = function () { if (this.publicData()) { return this.publicData().count.apply(this.publicData(), arguments); } return 0; }; // Call underlying View.prototype.subset = function () { return this.publicData().subset.apply(this._privateData, arguments); }; /** * Takes the passed data and uses it to set transform methods and globally * enable or disable the transform system for the view. * @param {Object} obj The new transform system settings "enabled", "dataIn" and "dataOut": * { * "enabled": true, * "dataIn": function (data) { return data; }, * "dataOut": function (data) { return data; } * } * @returns {*} */ View.prototype.transform = function (obj) { if (obj !== undefined) { if (typeof obj === "object") { if (obj.enabled !== undefined) { this._transformEnabled = obj.enabled; } if (obj.dataIn !== undefined) { this._transformIn = obj.dataIn; } if (obj.dataOut !== undefined) { this._transformOut = obj.dataOut; } } else { this._transformEnabled = obj !== false; } // Update the transformed data object this._transformPrimaryKey(this.privateData().primaryKey()); this._transformSetData(this.privateData().find()); return this; } return { enabled: this._transformEnabled, dataIn: this._transformIn, dataOut: this._transformOut }; }; /** * Executes a method against each document that matches query and returns an * array of documents that may have been modified by the method. * @param {Object} query The query object. * @param {Function} func The method that each document is passed to. If this method * returns false for a particular document it is excluded from the results. * @param {Object=} options Optional options object. * @returns {Array} */ View.prototype.filter = function (query, func, options) { return (this.publicData()).filter(query, func, options); }; /** * Returns the non-transformed data the view holds as a collection * reference. * @return {Collection} The non-transformed collection reference. */ View.prototype.privateData = function () { return this._privateData; }; /** * Returns a data object representing the public data this view * contains. This can change depending on if transforms are being * applied to the view or not. * * If no transforms are applied then the public data will be the * same as the private data the view holds. If transforms are * applied then the public data will contain the transformed version * of the private data. * * The public data collection is also used by data binding to only * changes to the publicData will show in a data-bound element. */ View.prototype.publicData = function () { if (this._transformEnabled) { return this._publicData; } else { return this._privateData; } }; /** * Updates the public data object to match data from the private data object * by running private data through the dataIn method provided in * the transform() call. * @private */ View.prototype._transformSetData = function (data) { if (this._transformEnabled) { // Clear existing data this._publicData = new Collection('__FDB__view_publicData_' + this._name); this._publicData.db(this._privateData._db); this._publicData.transform({ enabled: true, dataIn: this._transformIn, dataOut: this._transformOut }); this._publicData.setData(data); } }; View.prototype._transformInsert = function (data, index) { if (this._transformEnabled &amp;&amp; this._publicData) { this._publicData.insert(data, index); } }; View.prototype._transformUpdate = function (query, update, options) { if (this._transformEnabled &amp;&amp; this._publicData) { this._publicData.update(query, update, options); } }; View.prototype._transformRemove = function (query, options) { if (this._transformEnabled &amp;&amp; this._publicData) { this._publicData.remove(query, options); } }; View.prototype._transformPrimaryKey = function (key) { if (this._transformEnabled &amp;&amp; this._publicData) { this._publicData.primaryKey(key); } }; // Extend collection with view init Collection.prototype.init = function () { this._view = []; CollectionInit.apply(this, arguments); }; /** * Creates a view and assigns the collection as its data source. * @param {String} name The name of the new view. * @param {Object} query The query to apply to the new view. * @param {Object} options The options object to apply to the view. * @returns {*} */ Collection.prototype.view = function (name, query, options) { if (this._db &amp;&amp; this._db._view ) { if (!this._db._view[name]) { var view = new View(name, query, options) .db(this._db) .from(this); this._view = this._view || []; this._view.push(view); return view; } else { throw(this.logIdentifier() + ' Cannot create a view using this collection because a view with this name already exists: ' + name); } } }; /** * Adds a view to the internal view lookup. * @param {View} view The view to add. * @returns {Collection} * @private */ Collection.prototype._addView = CollectionGroup.prototype._addView = function (view) { if (view !== undefined) { this._view.push(view); } return this; }; /** * Removes a view from the internal view lookup. * @param {View} view The view to remove. * @returns {Collection} * @private */ Collection.prototype._removeView = CollectionGroup.prototype._removeView = function (view) { if (view !== undefined) { var index = this._view.indexOf(view); if (index > -1) { this._view.splice(index, 1); } } return this; }; // Extend DB with views init Db.prototype.init = function () { this._view = {}; DbInit.apply(this, arguments); }; /** * Gets a view by it's name. * @param {String} viewName The name of the view to retrieve. * @returns {*} */ Db.prototype.view = function (viewName) { // Handle being passed an instance if (viewName instanceof View) { return viewName; } if (!this._view[viewName]) { if (this.debug() || (this._db &amp;&amp; this._db.debug())) { console.log(this.logIdentifier() + ' Creating view ' + viewName); } } this._view[viewName] = this._view[viewName] || new View(viewName).db(this); return this._view[viewName]; }; /** * Determine if a view with the passed name already exists. * @param {String} viewName The name of the view to check for. * @returns {boolean} */ Db.prototype.viewExists = function (viewName) { return Boolean(this._view[viewName]); }; /** * Returns an array of views the DB currently has. * @returns {Array} An array of objects containing details of each view * the database is currently managing. */ Db.prototype.views = function () { var arr = [], view, i; for (i in this._view) { if (this._view.hasOwnProperty(i)) { view = this._view[i]; arr.push({ name: i, count: view.count(), linked: view.isLinked !== undefined ? view.isLinked() : false }); } } return arr; }; Shared.finishModule('View'); module.exports = View;</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ActiveBucket.html">ActiveBucket</a></li><li><a href="Angular.html">Angular</a></li><li><a href="AutoBind.html">AutoBind</a></li><li><a href="BinaryTree.html">BinaryTree</a></li><li><a href="Collection.html">Collection</a></li><li><a href="CollectionGroup.html">CollectionGroup</a></li><li><a href="Core.html">Core</a></li><li><a href="Db.html">Db</a></li><li><a href="Document.html">Document</a></li><li><a href="Grid.html">Grid</a></li><li><a href="Highchart.html">Highchart</a></li><li><a href="IndexBinaryTree.html">IndexBinaryTree</a></li><li><a href="IndexHashMap.html">IndexHashMap</a></li><li><a href="Infinilist.html">Infinilist</a></li><li><a href="KeyValueStore.html">KeyValueStore</a></li><li><a href="Metrics.html">Metrics</a></li><li><a href="OldView.html">OldView</a></li><li><a href="Operation.html">Operation</a></li><li><a href="Overload.html">Overload</a></li><li><a href="Path.html">Path</a></li><li><a href="Persist.html">Persist</a></li><li><a href="ReactorIO.html">ReactorIO</a></li><li><a href="Serialiser.html">Serialiser</a></li><li><a href="Shared.overload.html">overload</a></li><li><a href="View.html">View</a></li></ul><h3>Mixins</h3><ul><li><a href="ChainReactor.html">ChainReactor</a></li><li><a href="crcTable.html">crcTable</a></li><li><a href="Shared.html">Shared</a></li></ul><h3>Global</h3><ul><li><a href="global.html#%2522boolean,function%2522">"boolean, function"</a></li><li><a href="global.html#%2522string,*,function%2522">"string, *, function"</a></li><li><a href="global.html#%2522string,function%2522">"string, function"</a></li><li><a href="global.html#boolean">boolean</a></li><li><a href="global.html#function">function</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.3</a> on Thu Nov 19 2015 13:31:32 GMT+0000 (GMT) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>