UNPKG

forerunnerdb

Version:

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

1,167 lines (1,021 loc) 33.3 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: Persist.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: Persist.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; // Import external names locally var Shared = require('./Shared'), async = require('async'), localforage = require('localforage'), FdbCompress = require('./PersistCompress'),// jshint ignore:line FdbCrypto = require('./PersistCrypto'),// jshint ignore:line Db, Collection, CollectionDrop, CollectionGroup, CollectionInit, DbInit, DbDrop, Persist, Overload;//, //DataVersion = '2.0'; /** * The persistent storage class handles loading and saving data to browser * storage. * @class * @constructor */ Persist = function () { this.init.apply(this, arguments); }; /** * The local forage library. */ Persist.prototype.localforage = localforage; /** * The init method that can be overridden or extended. * @param {Db} db The ForerunnerDB database instance. */ Persist.prototype.init = function (db) { var self = this; this._encodeSteps = [ function () { return self._encode.apply(self, arguments); } ]; this._decodeSteps = [ function () { return self._decode.apply(self, arguments); } ]; // Check environment if (db.isClient()) { this.mode('localforage'); localforage.config({ name: String(db.core().name()), storeName: 'FDB' }); } }; Shared.addModule('Persist', Persist); Shared.mixin(Persist.prototype, 'Mixin.ChainReactor'); Shared.mixin(Persist.prototype, 'Mixin.Common'); Db = Shared.modules.Db; Collection = require('./Collection'); CollectionDrop = Collection.prototype.drop; CollectionGroup = require('./CollectionGroup'); CollectionInit = Collection.prototype.init; DbInit = Db.prototype.init; DbDrop = Db.prototype.drop; Overload = Shared.overload; /** * Gets / sets the auto flag which determines if the persistence module * will automatically load data for collections the first time they are * accessed and save data whenever it changes. This is disabled by * default. * @param {Boolean} val Set to true to enable, false to disable * @returns {*} */ Shared.synthesize(Persist.prototype, 'auto', function (val) { var self = this; if (val !== undefined) { if (val) { // Hook db events this._db.on('create', function () { self._autoLoad.apply(self, arguments); }); this._db.on('change', function () { self._autoSave.apply(self, arguments); }); if (this._db.debug()) { console.log(this._db.logIdentifier() + ' Automatic load/save enabled'); } } else { // Un-hook db events this._db.off('create', this._autoLoad); this._db.off('change', this._autoSave); if (this._db.debug()) { console.log(this._db.logIdentifier() + ' Automatic load/save disabled'); } } } return this.$super.call(this, val); }); Persist.prototype._autoLoad = function (obj, objType, name) { var self = this; if (typeof obj.load === 'function') { if (self._db.debug()) { console.log(self._db.logIdentifier() + ' Auto-loading data for ' + objType + ':', name); } obj.load(function (err, data) { if (err &amp;&amp; self._db.debug()) { console.log(self._db.logIdentifier() + ' Automatic load failed:', err); } self.emit('load', err, data); }); } else { if (self._db.debug()) { console.log(self._db.logIdentifier() + ' Auto-load for ' + objType + ':', name, 'no load method, skipping'); } } }; Persist.prototype._autoSave = function (obj, objType, name) { var self = this; if (typeof obj.save === 'function') { if (self._db.debug()) { console.log(self._db.logIdentifier() + ' Auto-saving data for ' + objType + ':', name); } obj.save(function (err, data) { if (err &amp;&amp; self._db.debug()) { console.log(self._db.logIdentifier() + ' Automatic save failed:', err); } self.emit('save', err, data); }); } }; /** * Gets / sets the persistent storage mode (the library used * to persist data to the browser - defaults to localForage). * @param {String} type The library to use for storage. Defaults * to localForage. * @returns {*} */ Persist.prototype.mode = function (type) { if (type !== undefined) { this._mode = type; return this; } return this._mode; }; /** * Sets - if Persist is load in "localforage" mode - a custom driver which applies to the rules * given by localforage, given here: https://localforage.github.io/localForage/#driver-api-definedriver * @param driver A local forage driver definition * @param callback A possible callback, which is executed after the customdriver was initialized */ Persist.prototype.customdriver = function(driver, callback) { if (this.mode() !== 'localforage') { throw 'No interface ready to set custom driver!'; } var driverName = driver._driver; // copying the drivername to avoid possible variable modification errors localforage.defineDriver(driver).then(function() { return localforage.setDriver(driverName); }) .then(function() { if (callback) { callback(null); } }) .catch(function(err) { callback(err); }); }; /** * Gets / sets the driver used when persisting data. * @param {String} val Specify the driver type (LOCALSTORAGE, * WEBSQL or INDEXEDDB) * @returns {*} */ Persist.prototype.driver = function (val) { if (val !== undefined) { switch (val.toUpperCase()) { case 'LOCALSTORAGE': localforage.setDriver(localforage.LOCALSTORAGE); break; case 'WEBSQL': localforage.setDriver(localforage.WEBSQL); break; case 'INDEXEDDB': localforage.setDriver(localforage.INDEXEDDB); break; default: throw('ForerunnerDB.Persist: The persistence driver you have specified is not found. Please use either IndexedDB, WebSQL or LocalStorage!'); } return this; } return localforage.driver(); }; /** * Starts a decode waterfall process. * @param {*} val The data to be decoded. * @param {Function} finished The callback to pass final data to. */ Persist.prototype.decode = function (val, finished) { async.waterfall([function (callback) { if (callback) { callback(false, val, {}); } }].concat(this._decodeSteps), finished); }; /** * Starts an encode waterfall process. * @param {*} val The data to be encoded. * @param {Function} finished The callback to pass final data to. */ Persist.prototype.encode = function (val, finished) { async.waterfall([function (callback) { if (callback) { callback(false, val, {}); } }].concat(this._encodeSteps), finished); }; Shared.synthesize(Persist.prototype, 'encodeSteps'); Shared.synthesize(Persist.prototype, 'decodeSteps'); Persist.prototype.addStep = new Overload({ /** * Adds an encode/decode step to the persistent storage system so * that you can add custom functionality. * @name addStep * @method Persist.addStep * @param {Function} obj The object to encode / decode. */ 'object': function (obj) { return this.$main.call(this, function objEncode () { obj.encode.apply(obj, arguments); }, function objDecode () { obj.decode.apply(obj, arguments); }, 0); }, /** * Adds an encode/decode step to the persistent storage system so * that you can add custom functionality. * @name addStep * @method Persist.addStep * @param {Function} encode The encode method called with the data from the * previous encode step. When your method is complete it MUST call the * callback method. If you provide anything other than false to the err * parameter the encoder will fail and throw an error. * @param {Function} decode The decode method called with the data from the * previous decode step. When your method is complete it MUST call the * callback method. If you provide anything other than false to the err * parameter the decoder will fail and throw an error. */ 'function, function': function (encode, decode) { return this.$main.call(this, encode, decode, 0); }, /** * Adds an encode/decode step to the persistent storage system so * that you can add custom functionality. * @name addStep * @method Persist.addStep * @param {Function} encode The encode method called with the data from the * previous encode step. When your method is complete it MUST call the * callback method. If you provide anything other than false to the err * parameter the encoder will fail and throw an error. * @param {Function} decode The decode method called with the data from the * previous decode step. When your method is complete it MUST call the * callback method. If you provide anything other than false to the err * parameter the decoder will fail and throw an error. * @param {Number=} index Optional index to add the encoder step to. This * allows you to place a step before or after other existing steps. If not * provided your step is placed last in the list of steps. For instance if * you are providing an encryption step it makes sense to place this last * since all previous steps will then have their data encrypted by your * final step. */ 'function, function, number': function (encode, decode, index) { return this.$main.call(this, encode, decode, index); }, $main: function (encode, decode, index) { if (index === 0 || index === undefined) { this._encodeSteps.push(encode); this._decodeSteps.unshift(decode); } else { // Place encoder step at index then work out correct // index to place decoder step this._encodeSteps.splice(index, 0, encode); this._decodeSteps.splice(this._decodeSteps.length - index, 0, decode); } } }); Persist.prototype.unwrap = function (dataStr) { var parts = dataStr.split('::fdb::'), data; switch (parts[0]) { case 'json': data = this.jParse(parts[1]); break; case 'raw': data = parts[1]; break; default: break; } }; /** * Takes encoded data and decodes it for use as JS native objects and arrays. * @param {String} val The currently encoded string data. * @param {Object} meta Meta data object that can be used to pass back useful * supplementary data. * @param {Function} finished The callback method to call when decoding is * completed. * @private */ Persist.prototype._decode = function (val, meta, finished) { var parts, data; if (val) { parts = val.split('::fdb::'); switch (parts[0]) { case 'json': data = this.jParse(parts[1]); break; case 'raw': data = parts[1]; break; default: break; } if (data) { meta.foundData = true; meta.rowCount = data.length || 0; } else { meta.foundData = false; meta.rowCount = 0; } if (finished) { finished(false, data, meta); } } else { meta.foundData = false; meta.rowCount = 0; if (finished) { finished(false, val, meta); } } }; /** * Takes native JS data and encodes it for for storage as a string. * @param {Object} val The current un-encoded data. * @param {Object} meta Meta data object that can be used to pass back useful * supplementary data. * @param {Function} finished The callback method to call when encoding is * completed. * @private */ Persist.prototype._encode = function (val, meta, finished) { var data = val; if (typeof val === 'object') { val = 'json::fdb::' + this.jStringify(val); } else { val = 'raw::fdb::' + val; } if (data) { meta.foundData = true; meta.rowCount = data.length || 0; } else { meta.foundData = false; meta.rowCount = 0; } if (finished) { finished(false, val, meta); } }; /** * Encodes passed data and then stores it in the browser's persistent * storage layer. * @param {String} key The key to store the data under in the persistent * storage. * @param {Object} data The data to store under the key. * @param {Function=} callback The method to call when the save process * has completed. */ Persist.prototype.save = function (key, data, callback) { switch (this.mode()) { case 'localforage': this.encode(data, function (err, data, tableStats) { if (err) { return callback(err); } localforage.setItem(key, data, function (err) { if (callback) { if (err) { callback(err); return; } callback(false, data, tableStats); } }); }); break; default: if (callback) { callback('No data handler.'); } break; } }; /** * Loads and decodes data from the passed key. * @param {String} key The key to retrieve data from in the persistent * storage. * @param {Function=} callback The method to call when the load process * has completed. */ Persist.prototype.load = function (key, callback) { var self = this; switch (this.mode()) { case 'localforage': localforage.getItem(key, function (err, val) { if (err) { if (callback) { callback(err); } return; } self.decode(val, callback); }); break; default: if (callback) { callback('No data handler or unrecognised data type.'); } break; } }; /** * Deletes data in persistent storage stored under the passed key. * @param {String} key The key to drop data for in the storage. * @param {Function=} callback The method to call when the data is dropped. */ Persist.prototype.drop = function (key, callback) { switch (this.mode()) { case 'localforage': localforage.removeItem(key, function (err) { if (callback) { callback(err); } }); break; default: if (callback) { callback('No data handler or unrecognised data type.'); } break; } }; /** * Determines the byte size of a String, also taking special chars into account. * Inspired by http://codereview.stackexchange.com/questions/37512/count-byte-length-of-string * @param {String} string String to get bytes size of * @return {Number} Size in bytes of the input string * @private */ Persist.prototype._calcSize = function (string) { var iCount = 0, stringPrep = String(string || ""), stringLength = stringPrep.length, iPartCount, i; for (i = 0; i &lt; stringLength; i++) { iPartCount = encodeURI(stringPrep[i]).split("%").length; iCount += iPartCount === 1 ? 1 : iPartCount - 1; } return iCount; }; /** * Argument to the persistedSize callback function * @typedef {Object} persistedSizeCallbackArg * @property {number} total The total size of either the collection or the DB in byte * @property {Array[]} collections per Array item: collection name and size as an Array tupel: collectionName, size */ /** * Callback for persisted size functions * * @callback persistedSizeCallback * @param {persistedSizeCallbackArg} */ /** * determine byte size of a persisted object, * either entire DB or specific collection * @param {string|object} target either a string (collection name) or an object (DB) * @param {persistedSizeCallback} callback method to call when size determination is done - mandatory for obtaining a result! */ Persist.prototype.persistedSize = function (target, callback) { var self = this, resultMap; // mapping: { // total: $totalsize // collections: [ // [collection, bytesize] // .... // ] // } resultMap = { total: 0, collections: [] }; /** * named helper function to map-reduce the total byte size of a persisted object * and to log each collections individual size * @param {String} value * @param {String} key * @private */ function _mapSizeCb (value, key) { // cont only if collection contains content if (value !== null) { // Both key and value of the persisted storage obj count toward total size var atomicTotal = self._calcSize(value) + self._calcSize(key); // Make an entry: collection (key) is $total bytes resultMap.collections.push([key, atomicTotal]); // Count towards total size only if value != null resultMap.total += atomicTotal; } } switch (this.mode()) { case 'localforage': // In general: don't do any decoding (enc, compression), // b/c only the "raw" size of the storage object is of interest switch ((typeof target).toLowerCase()) { case 'string': // Collection name was provided // Utilize localforages built-in Promise functionality (includes // shim for IE) get collection + collection meta data for size // determination localforage.getItem(target).then(function (value) { _mapSizeCb(value, target); }).then(function () { return localforage.getItem(target + "-metaData"); }).then(function (value) { _mapSizeCb(value, target + "-metaData"); }).then(function () { // done - report back if (callback) { callback(null, resultMap); } }).catch(function (err) { console.error(JSON.stringify(err)); if (callback) { callback(err); } }); break; case 'object': // DB object was provided // determine size of DB // by iterating over all key/value pairs // filtering for this DB // we want to measure all persisted collections of a DB // independent of whether they're attached to the DB // at runtime via db.collection(name) localforage.iterate(function (value, key) { if ((key.lastIndexOf(target.name(), 0) === 0) &amp;&amp; value !== null) { _mapSizeCb(value, key); } }).then(function () { // done - report back callback(null, resultMap); }).catch(function (err) { console.error(JSON.stringify(err)); if (callback) { callback(err); } }); break; default: if (callback) { callback("Couldn't determine target for size calculation - must be either a collection name (string) or a db reference (object)"); } } break; default: if (callback) { callback('No data handler or unrecognised data type.'); } break; } }; // Extend the Collection prototype with persist methods Collection.prototype.drop = new Overload({ /** * Drop collection and persistent storage. * @name drop * @method Collection.drop */ '': function () { if (!this.isDropped()) { this.drop(true); } }, /** * Drop collection and persistent storage with callback. * @name drop * @method Collection.drop * @param {Function} callback Callback method. */ 'function': function (callback) { if (!this.isDropped()) { this.drop(true, callback); } }, /** * Drop collection and optionally drop persistent storage. * @name drop * @method Collection.drop * @param {Boolean} removePersistent True to drop persistent storage, false to keep it. */ 'boolean': function (removePersistent) { if (!this.isDropped()) { // Remove persistent storage if (removePersistent) { if (this._name) { if (this._db) { // Drop the collection data from storage this._db.persist.drop(this._db._name + '-' + this._name); this._db.persist.drop(this._db._name + '-' + this._name + '-metaData'); } } else { throw('ForerunnerDB.Persist: Cannot drop a collection\'s persistent storage when no name assigned to collection!'); } } // Call the original method CollectionDrop.call(this); } }, /** * Drop collections and optionally drop persistent storage with callback. * @name drop * @method Collection.drop * @param {Boolean} removePersistent True to drop persistent storage, false to keep it. * @param {Function} callback Callback method. */ 'boolean, function': function (removePersistent, callback) { var self = this; if (!this.isDropped()) { // Remove persistent storage if (removePersistent) { if (this._name) { if (this._db) { // Drop the collection data from storage this._db.persist.drop(this._db._name + '-' + this._name, function () { self._db.persist.drop(self._db._name + '-' + self._name + '-metaData', callback); }); return CollectionDrop.call(this); } else { if (callback) { callback('Cannot drop a collection\'s persistent storage when the collection is not attached to a database!'); } } } else { if (callback) { callback('Cannot drop a collection\'s persistent storage when no name assigned to collection!'); } } } else { // Call the original method return CollectionDrop.call(this, callback); } } } }); /** * Saves an entire collection's data to persistent storage. * @param {Function=} callback The method to call when the save function * has completed. */ Collection.prototype.save = function (callback) { var self = this, processSave; if (self._name) { if (self._db) { processSave = function () { // Save the collection data self._db.persist.save(self._db._name + '-' + self._name, self._data, function (err, tableData, tableStats) { if (err) { if (callback) { callback(err); } return; } self._db.persist.save(self._db._name + '-' + self._name + '-metaData', self.metaData(), function (err, metaData, metaStats) { self.deferEmit('save', tableStats, metaStats, { tableData: tableData, metaData: metaData, tableDataName: self._db._name + '-' + self._name, metaDataName: self._db._name + '-' + self._name + '-metaData' }); if (callback) { // Defer till the next VM tick to give the VM some breathing room // around the persistent data getting into storage. setTimeout(function () { callback(err, tableStats, metaStats, { tableData: tableData, metaData: metaData, tableDataName: self._db._name + '-' + self._name, metaDataName: self._db._name + '-' + self._name + '-metaData' }); }, 1); } }); }); }; // Check for processing queues if (self.isProcessingQueue()) { // Hook queue complete to process save self.on('queuesComplete', function () { processSave(); }); } else { // Process save immediately processSave(); } } else { if (callback) { callback('Cannot save a collection that is not attached to a database!'); } } } else { if (callback) { callback('Cannot save a collection with no assigned name!'); } } }; /** * Determines the byte size of a persisted collection * @param {persistedSizeCallback} callback The method to call when the size check is complete */ Collection.prototype.persistedSize = function (callback) { var self = this; if (self._name) { if (self._db) { self._db.persist.persistedSize(self._db._name + '-' + self._name, callback); } else { if (callback) { callback('Cannot determine persisted size of a collection that is not attached to a database!'); } } } else { if (callback) { callback('Cannot determine persisted size of a collection with no assigned name!'); } } }; /** * Loads an entire collection's data from persistent storage. * @param {Function=} callback The method to call when the load function * has completed. */ Collection.prototype.load = function (callback) { var self = this; if (self._name) { if (self._db) { // Load the collection data self._db.persist.load(self._db._name + '-' + self._name, function (err, data, tableStats) { if (!err) { // Remove all previous data self.remove({}, function () { // Now insert the new data data = data || []; self.insert(data, function () { // Now load the collection's metadata self._db.persist.load(self._db._name + '-' + self._name + '-metaData', function (err, data, metaStats) { if (!err) { if (data) { self.metaData(data); } } self.deferEmit('load', tableStats, metaStats); if (callback) { callback(err, tableStats, metaStats); } }); }); //self.setData(data); }); } else { self.deferEmit('load', tableStats); if (callback) { callback(err); } } }); } else { if (callback) { callback('Cannot load a collection that is not attached to a database!'); } } } else { if (callback) { callback('Cannot load a collection with no assigned name!'); } } }; /** * Gets the data that represents this collection for easy storage using * a third-party method / plugin instead of using the standard persistent * storage system. * @param {Function} callback The method to call with the data response. */ Collection.prototype.saveCustom = function (callback) { var self = this, myData = {}, processSave; if (self._name) { if (self._db) { processSave = function () { self.encode(self._data, function (err, data, tableStats) { if (!err) { myData.data = { name: self._db._name + '-' + self._name, store: data, tableStats: tableStats }; self.encode(self._data, function (err, data, tableStats) { if (!err) { myData.metaData = { name: self._db._name + '-' + self._name + '-metaData', store: data, tableStats: tableStats }; callback(false, myData); } else { callback(err); } }); } else { callback(err); } }); }; // Check for processing queues if (self.isProcessingQueue()) { // Hook queue complete to process save self.on('queuesComplete', function () { processSave(); }); } else { // Process save immediately processSave(); } } else { if (callback) { callback('Cannot save a collection that is not attached to a database!'); } } } else { if (callback) { callback('Cannot save a collection with no assigned name!'); } } }; /** * Loads custom data loaded by a third-party plugin into the collection. * @param {Object} myData Data object previously saved by using saveCustom() * @param {Function} callback A callback method to receive notification when * data has loaded. */ Collection.prototype.loadCustom = function (myData, callback) { var self = this; if (self._name) { if (self._db) { if (myData.data &amp;&amp; myData.data.store) { if (myData.metaData &amp;&amp; myData.metaData.store) { self.decode(myData.data.store, function (err, data, tableStats) { if (!err) { if (data) { // Remove all previous data self.remove({}); self.insert(data); self.decode(myData.metaData.store, function (err, data, metaStats) { if (!err) { if (data) { self.metaData(data); if (callback) { callback(err, tableStats, metaStats); } } } else { callback(err); } }); } } else { callback(err); } }); } else { callback('No "metaData" key found in passed object!'); } } else { callback('No "data" key found in passed object!'); } } else { if (callback) { callback('Cannot load a collection that is not attached to a database!'); } } } else { if (callback) { callback('Cannot load a collection with no assigned name!'); } } }; // Override the DB init to instantiate the plugin Db.prototype.init = function () { DbInit.apply(this, arguments); this.persist = new Persist(this); }; Db.prototype.load = new Overload({ /** * Loads an entire database's data from persistent storage. * @name load * @method Db.load * @param {Function=} callback The method to call when the load function * has completed. */ 'function': function (callback) { this.$main.call(this, undefined, callback); }, /** * Loads an entire database's data from persistent storage. * @name load * @method Db.load * @param {Object} myData Custom data to load into the collection. * @param {Function} callback The method to call when the load function * has completed. */ 'object, function': function (myData, callback) { this.$main.call(this, myData, callback); }, '$main': function (myData, callback) { // Loop the collections in the database var self = this, obj, keys, keyCount, loadCallback, index; obj = this._collection; keys = Object.keys(obj); keyCount = keys.length; if (keyCount &lt;= 0) { return callback(false); } loadCallback = function (err) { if (!err) { keyCount--; if (keyCount &lt;= 0) { self.deferEmit('load'); if (callback) { callback(false); } } } else { if (callback) { callback(err); } } }; for (index in obj) { if (obj.hasOwnProperty(index)) { // Call the collection load method if (!myData) { obj[index].load(loadCallback); } else { obj[index].loadCustom(myData, loadCallback); } } } } }); Db.prototype.save = new Overload({ /** * Saves an entire database's data to persistent storage. * @name save * @method Db.save * @param {Function=} callback The method to call when the save function * has completed. */ 'function': function (callback) { this.$main.call(this, {}, callback); }, /** * Saves an entire database's data to persistent storage. * @name save * @method Db.save * @param {Object} options The options object. * @param {Function} callback The method to call when the save function * has completed. */ 'object, function': function (options, callback) { this.$main.call(this, options, callback); }, '$main': function (options, callback) { // Loop the collections in the database var self = this, obj, keys, keyCount, saveCallback, index; obj = this._collection; keys = Object.keys(obj); keyCount = keys.length; if (keyCount &lt;= 0) { return callback(false); } saveCallback = function (err) { if (!err) { keyCount--; if (keyCount &lt;= 0) { self.deferEmit('save'); if (callback) { callback(false); } } } else { if (callback) { callback(err); } } }; for (index in obj) { if (obj.hasOwnProperty(index)) { // Call the collection save method if (!options.custom) { obj[index].save(saveCallback); } else { obj[index].saveCustom(saveCallback); } } } } }); /** * Determines the byte size of a persisted DB * @param {persistedSizeCallback} callback The method to call when the size check is complete */ Db.prototype.persistedSize = function(callback) { this.persist.persistedSize(this, callback); }; Shared.finishModule('Persist'); module.exports = Persist;</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="Collection.html">Collection</a></li><li><a href="CollectionGroup.html">CollectionGroup</a></li><li><a href="Condition.html">Condition</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="Index2d.html">Index2d</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="MyModule.html">MyModule</a></li><li><a href="NodeApiClient.html">NodeApiClient</a></li><li><a href="NodeApiServer.html">NodeApiServer</a></li><li><a href="NodeRAS.html">NodeRAS</a></li><li><a href="Odm.html">Odm</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="Overview.html">Overview</a></li><li><a href="Overview_init.html">init</a></li><li><a href="Path.html">Path</a></li><li><a href="Persist.html">Persist</a></li><li><a href="Procedure.html">Procedure</a></li><li><a href="ReactorIO.html">ReactorIO</a></li><li><a href="Section.html">Section</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="Common.html">Common</a></li><li><a href="Constants.html">Constants</a></li><li><a href="Events.html">Events</a></li><li><a href="Matching.html">Matching</a></li><li><a href="Shared.html">Shared</a></li><li><a href="Sorting.html">Sorting</a></li><li><a href="Tags.html">Tags</a></li><li><a href="Triggers.html">Triggers</a></li><li><a href="Updating.html">Updating</a></li></ul><h3><a href="global.html">Global</a></h3> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Thu Mar 01 2018 11:34:22 GMT+0000 (GMT) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>