UNPKG

forerunnerdb

Version:

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

753 lines (627 loc) 20.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: OldView.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: OldView.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; // Import external names locally var Shared, Db, CollectionGroup, Collection, CollectionInit, CollectionGroupInit, DbInit; Shared = require('./Shared'); /** * The view constructor. * @param viewName * @constructor */ var OldView = function (viewName) { this.init.apply(this, arguments); }; OldView.prototype.init = function (viewName) { var self = this; this._name = viewName; this._listeners = {}; this._query = { query: {}, options: {} }; // Register listeners for the CRUD events this._onFromSetData = function () { self._onSetData.apply(self, arguments); }; this._onFromInsert = function () { self._onInsert.apply(self, arguments); }; this._onFromUpdate = function () { self._onUpdate.apply(self, arguments); }; this._onFromRemove = function () { self._onRemove.apply(self, arguments); }; this._onFromChange = function () { if (self.debug()) { console.log('ForerunnerDB.OldView: Received change'); } self._onChange.apply(self, arguments); }; }; Shared.addModule('OldView', OldView); CollectionGroup = require('./CollectionGroup'); Collection = require('./Collection'); CollectionInit = Collection.prototype.init; CollectionGroupInit = CollectionGroup.prototype.init; Db = Shared.modules.Db; DbInit = Db.prototype.init; Shared.mixin(OldView.prototype, 'Mixin.Events'); /** * Drops a view and all it's stored data from the database. * @returns {boolean} True on success, false on failure. */ OldView.prototype.drop = function () { if ((this._db || this._from) &amp;&amp; this._name) { if (this.debug()) { console.log('ForerunnerDB.OldView: Dropping view ' + this._name); } this._state = 'dropped'; this.emit('drop', this); if (this._db &amp;&amp; this._db._oldViews) { delete this._db._oldViews[this._name]; } if (this._from &amp;&amp; this._from._oldViews) { delete this._from._oldViews[this._name]; } return true; } return false; }; OldView.prototype.debug = function () { // TODO: Make this function work return false; }; /** * Gets / sets the DB the view is bound against. Automatically set * when the db.oldView(viewName) method is called. * @param db * @returns {*} */ OldView.prototype.db = function (db) { if (db !== undefined) { this._db = db; return this; } return this._db; }; /** * Gets / sets the collection that the view derives it's data from. * @param {*} collection A collection instance or the name of a collection * to use as the data set to derive view data from. * @returns {*} */ OldView.prototype.from = function (collection) { if (collection !== undefined) { // Check if this is a collection name or a collection instance if (typeof(collection) === 'string') { if (this._db.collectionExists(collection)) { collection = this._db.collection(collection); } else { throw('ForerunnerDB.OldView "' + this.name() + '": Invalid collection in view.from() call.'); } } // Check if the existing from matches the passed one if (this._from !== collection) { // Check if we already have a collection assigned if (this._from) { // Remove ourselves from the collection view lookup this.removeFrom(); } this.addFrom(collection); } return this; } return this._from; }; OldView.prototype.addFrom = function (collection) { //var self = this; this._from = collection; if (this._from) { this._from.on('setData', this._onFromSetData); //this._from.on('insert', this._onFromInsert); //this._from.on('update', this._onFromUpdate); //this._from.on('remove', this._onFromRemove); this._from.on('change', this._onFromChange); // Add this view to the collection's view lookup this._from._addOldView(this); this._primaryKey = this._from._primaryKey; this.refresh(); return this; } else { throw('ForerunnerDB.OldView "' + this.name() + '": Cannot determine collection type in view.from()'); } }; OldView.prototype.removeFrom = function () { // Unsubscribe from events on this "from" this._from.off('setData', this._onFromSetData); //this._from.off('insert', this._onFromInsert); //this._from.off('update', this._onFromUpdate); //this._from.off('remove', this._onFromRemove); this._from.off('change', this._onFromChange); this._from._removeOldView(this); }; /** * Gets the primary key for this view from the assigned collection. * @returns {String} */ OldView.prototype.primaryKey = function () { if (this._from) { return this._from.primaryKey(); } return undefined; }; /** * Gets / sets the query that the view uses to build it's data set. * @param {Object=} query * @param {Boolean=} options An options object. * @param {Boolean=} refresh Whether to refresh the view data after * this operation. Defaults to true. * @returns {*} */ OldView.prototype.queryData = function (query, options, refresh) { if (query !== undefined) { this._query.query = query; } if (options !== undefined) { this._query.options = options; } if (query !== undefined || options !== undefined) { if (refresh === undefined || refresh === true) { this.refresh(); } return this; } return this._query; }; /** * 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. */ OldView.prototype.queryAdd = function (obj, overwrite, refresh) { var query = this._query.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)) { 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. */ OldView.prototype.queryRemove = function (obj, refresh) { var query = this._query.query, i; 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. * @param {Object=} query The query to set. * @param {Boolean=} refresh Whether to refresh the view data after * this operation. Defaults to true. * @returns {*} */ OldView.prototype.query = function (query, refresh) { if (query !== undefined) { this._query.query = query; if (refresh === undefined || refresh === true) { this.refresh(); } return this; } return this._query.query; }; /** * 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 {*} */ OldView.prototype.queryOptions = function (options, refresh) { if (options !== undefined) { this._query.options = options; if (refresh === undefined || refresh === true) { this.refresh(); } return this; } return this._query.options; }; /** * Refreshes the view data and diffs between previous and new data to * determine if any events need to be triggered or DOM binds updated. */ OldView.prototype.refresh = function (force) { if (this._from) { // Take a copy of the data before updating it, we will use this to // "diff" between the old and new data and handle DOM bind updates var oldData = this._data, oldDataArr, oldDataItem, newData, newDataArr, query, primaryKey, dataItem, inserted = [], updated = [], removed = [], operated = false, i; if (this.debug()) { console.log('ForerunnerDB.OldView: Refreshing view ' + this._name); console.log('ForerunnerDB.OldView: Existing data: ' + (typeof(this._data) !== "undefined")); if (typeof(this._data) !== "undefined") { console.log('ForerunnerDB.OldView: Current data rows: ' + this._data.find().length); } //console.log(OldView.prototype.refresh.caller); } // Query the collection and update the data if (this._query) { if (this.debug()) { console.log('ForerunnerDB.OldView: View has query and options, getting subset...'); } // Run query against collection //console.log('refresh with query and options', this._query.options); this._data = this._from.subset(this._query.query, this._query.options); //console.log(this._data); } else { // No query, return whole collection if (this._query.options) { if (this.debug()) { console.log('ForerunnerDB.OldView: View has options, getting subset...'); } this._data = this._from.subset({}, this._query.options); } else { if (this.debug()) { console.log('ForerunnerDB.OldView: View has no query or options, getting subset...'); } this._data = this._from.subset({}); } } // Check if there was old data if (!force &amp;&amp; oldData) { if (this.debug()) { console.log('ForerunnerDB.OldView: Refresh not forced, old data detected...'); } // Now determine the difference newData = this._data; if (oldData.subsetOf() === newData.subsetOf()) { if (this.debug()) { console.log('ForerunnerDB.OldView: Old and new data are from same collection...'); } newDataArr = newData.find(); oldDataArr = oldData.find(); primaryKey = newData._primaryKey; // The old data and new data were derived from the same parent collection // so scan the data to determine changes for (i = 0; i &lt; newDataArr.length; i++) { dataItem = newDataArr[i]; query = {}; query[primaryKey] = dataItem[primaryKey]; // Check if this item exists in the old data oldDataItem = oldData.find(query)[0]; if (!oldDataItem) { // New item detected inserted.push(dataItem); } else { // Check if an update has occurred if (JSON.stringify(oldDataItem) !== JSON.stringify(dataItem)) { // Updated / already included item detected updated.push(dataItem); } } } // Now loop the old data and check if any records were removed for (i = 0; i &lt; oldDataArr.length; i++) { dataItem = oldDataArr[i]; query = {}; query[primaryKey] = dataItem[primaryKey]; // Check if this item exists in the old data if (!newData.find(query)[0]) { // Removed item detected removed.push(dataItem); } } if (this.debug()) { console.log('ForerunnerDB.OldView: Removed ' + removed.length + ' rows'); console.log('ForerunnerDB.OldView: Inserted ' + inserted.length + ' rows'); console.log('ForerunnerDB.OldView: Updated ' + updated.length + ' rows'); } // Now we have a diff of the two data sets, we need to get the DOM updated if (inserted.length) { this._onInsert(inserted, []); operated = true; } if (updated.length) { this._onUpdate(updated, []); operated = true; } if (removed.length) { this._onRemove(removed, []); operated = true; } } else { // The previous data and the new data are derived from different collections // and can therefore not be compared, all data is therefore effectively "new" // so first perform a remove of all existing data then do an insert on all new data if (this.debug()) { console.log('ForerunnerDB.OldView: Old and new data are from different collections...'); } removed = oldData.find(); if (removed.length) { this._onRemove(removed); operated = true; } inserted = newData.find(); if (inserted.length) { this._onInsert(inserted); operated = true; } } } else { // Force an update as if the view never got created by padding all elements // to the insert if (this.debug()) { console.log('ForerunnerDB.OldView: Forcing data update', newDataArr); } this._data = this._from.subset(this._query.query, this._query.options); newDataArr = this._data.find(); if (this.debug()) { console.log('ForerunnerDB.OldView: Emitting change event with data', newDataArr); } this._onInsert(newDataArr, []); } if (this.debug()) { console.log('ForerunnerDB.OldView: Emitting change'); } this.emit('change'); } return this; }; /** * Returns the number of documents currently in the view. * @returns {Number} */ OldView.prototype.count = function () { return this._data &amp;&amp; this._data._data ? this._data._data.length : 0; }; /** * Queries the view data. See Collection.find() for more information. * @returns {*} */ OldView.prototype.find = function () { if (this._data) { if (this.debug()) { console.log('ForerunnerDB.OldView: Finding data in view collection...', this._data); } return this._data.find.apply(this._data, arguments); } else { return []; } }; /** * Inserts into view data via the view collection. See Collection.insert() for more information. * @returns {*} */ OldView.prototype.insert = function () { if (this._from) { // Pass the args through to the from object return this._from.insert.apply(this._from, arguments); } else { return []; } }; /** * Updates into view data via the view collection. See Collection.update() for more information. * @returns {*} */ OldView.prototype.update = function () { if (this._from) { // Pass the args through to the from object return this._from.update.apply(this._from, arguments); } else { return []; } }; /** * Removed from view data via the view collection. See Collection.remove() for more information. * @returns {*} */ OldView.prototype.remove = function () { if (this._from) { // Pass the args through to the from object return this._from.remove.apply(this._from, arguments); } else { return []; } }; OldView.prototype._onSetData = function (newDataArr, oldDataArr) { this.emit('remove', oldDataArr, []); this.emit('insert', newDataArr, []); //this.refresh(); }; OldView.prototype._onInsert = function (successArr, failArr) { this.emit('insert', successArr, failArr); //this.refresh(); }; OldView.prototype._onUpdate = function (successArr, failArr) { this.emit('update', successArr, failArr); //this.refresh(); }; OldView.prototype._onRemove = function (successArr, failArr) { this.emit('remove', successArr, failArr); //this.refresh(); }; OldView.prototype._onChange = function () { if (this.debug()) { console.log('ForerunnerDB.OldView: Refreshing data'); } this.refresh(); }; // Extend collection with view init Collection.prototype.init = function () { this._oldViews = []; CollectionInit.apply(this, arguments); }; /** * Adds a view to the internal view lookup. * @param {View} view The view to add. * @returns {Collection} * @private */ Collection.prototype._addOldView = function (view) { if (view !== undefined) { this._oldViews[view._name] = view; } return this; }; /** * Removes a view from the internal view lookup. * @param {View} view The view to remove. * @returns {Collection} * @private */ Collection.prototype._removeOldView = function (view) { if (view !== undefined) { delete this._oldViews[view._name]; } return this; }; // Extend collection with view init CollectionGroup.prototype.init = function () { this._oldViews = []; CollectionGroupInit.apply(this, arguments); }; /** * Adds a view to the internal view lookup. * @param {View} view The view to add. * @returns {Collection} * @private */ CollectionGroup.prototype._addOldView = function (view) { if (view !== undefined) { this._oldViews[view._name] = view; } return this; }; /** * Removes a view from the internal view lookup. * @param {View} view The view to remove. * @returns {Collection} * @private */ CollectionGroup.prototype._removeOldView = function (view) { if (view !== undefined) { delete this._oldViews[view._name]; } return this; }; // Extend DB with views init Db.prototype.init = function () { this._oldViews = {}; DbInit.apply(this, arguments); }; /** * Gets a view by it's name. * @param {String} viewName The name of the view to retrieve. * @returns {*} */ Db.prototype.oldView = function (viewName) { if (!this._oldViews[viewName]) { if (this.debug()) { console.log('ForerunnerDB.OldView: Creating view ' + viewName); } } this._oldViews[viewName] = this._oldViews[viewName] || new OldView(viewName).db(this); return this._oldViews[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.oldViewExists = function (viewName) { return Boolean(this._oldViews[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.oldViews = function () { var arr = [], i; for (i in this._oldViews) { if (this._oldViews.hasOwnProperty(i)) { arr.push({ name: i, count: this._oldViews[i].count() }); } } return arr; }; Shared.finishModule('OldView'); module.exports = OldView;</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>