UNPKG

forerunnerdb

Version:

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

587 lines (485 loc) 15.9 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: Path.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: Path.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; var Shared = require('./Shared'); /** * Path object used to resolve object paths and retrieve data from * objects by using paths. * @param {String=} path The path to assign. * @constructor */ var Path = function (path) { this.init.apply(this, arguments); }; Path.prototype.init = function (path) { if (path) { this.path(path); } }; Shared.addModule('Path', Path); Shared.mixin(Path.prototype, 'Mixin.Common'); Shared.mixin(Path.prototype, 'Mixin.ChainReactor'); /** * Gets / sets the given path for the Path instance. * @param {String=} path The path to assign. */ Path.prototype.path = function (path) { if (path !== undefined) { this._path = this.clean(path); this._pathParts = this._path.split('.'); return this; } return this._path; }; /** * Tests if the passed object has the paths that are specified and that * a value exists in those paths. * @param {Object} testKeys The object describing the paths to test for. * @param {Object} testObj The object to test paths against. * @returns {Boolean} True if the object paths exist. */ Path.prototype.hasObjectPaths = function (testKeys, testObj) { var result = true, i; for (i in testKeys) { if (testKeys.hasOwnProperty(i)) { if (testObj[i] === undefined) { return false; } if (typeof testKeys[i] === 'object') { // Recurse object result = this.hasObjectPaths(testKeys[i], testObj[i]); // Should we exit early? if (!result) { return false; } } } } return result; }; /** * Counts the total number of key endpoints in the passed object. * @param {Object} testObj The object to count key endpoints for. * @returns {Number} The number of endpoints. */ Path.prototype.countKeys = function (testObj) { var totalKeys = 0, i; for (i in testObj) { if (testObj.hasOwnProperty(i)) { if (testObj[i] !== undefined) { if (typeof testObj[i] !== 'object') { totalKeys++; } else { totalKeys += this.countKeys(testObj[i]); } } } } return totalKeys; }; /** * Tests if the passed object has the paths that are specified and that * a value exists in those paths and if so returns the number matched. * @param {Object} testKeys The object describing the paths to test for. * @param {Object} testObj The object to test paths against. * @returns {Object} Stats on the matched keys */ Path.prototype.countObjectPaths = function (testKeys, testObj) { var matchData, matchedKeys = {}, matchedKeyCount = 0, totalKeyCount = 0, i; for (i in testObj) { if (testObj.hasOwnProperty(i)) { if (typeof testObj[i] === 'object') { // The test / query object key is an object, recurse matchData = this.countObjectPaths(testKeys[i], testObj[i]); matchedKeys[i] = matchData.matchedKeys; totalKeyCount += matchData.totalKeyCount; matchedKeyCount += matchData.matchedKeyCount; } else { // The test / query object has a property that is not an object so add it as a key totalKeyCount++; // Check if the test keys also have this key and it is also not an object if (testKeys &amp;&amp; testKeys[i] &amp;&amp; typeof testKeys[i] !== 'object') { matchedKeys[i] = true; matchedKeyCount++; } else { matchedKeys[i] = false; } } } } return { matchedKeys: matchedKeys, matchedKeyCount: matchedKeyCount, totalKeyCount: totalKeyCount }; }; /** * Takes a non-recursive object and converts the object hierarchy into * a path string. * @param {Object} obj The object to parse. * @param {Boolean=} withValue If true will include a 'value' key in the returned * object that represents the value the object path points to. * @returns {Object} */ Path.prototype.parse = function (obj, withValue) { var paths = [], path = '', resultData, i, k; for (i in obj) { if (obj.hasOwnProperty(i)) { // Set the path to the key path = i; if (typeof(obj[i]) === 'object') { if (withValue) { resultData = this.parse(obj[i], withValue); for (k = 0; k &lt; resultData.length; k++) { paths.push({ path: path + '.' + resultData[k].path, value: resultData[k].value }); } } else { resultData = this.parse(obj[i]); for (k = 0; k &lt; resultData.length; k++) { paths.push({ path: path + '.' + resultData[k].path }); } } } else { if (withValue) { paths.push({ path: path, value: obj[i] }); } else { paths.push({ path: path }); } } } } return paths; }; /** * Takes a non-recursive object and converts the object hierarchy into * an array of path strings that allow you to target all possible paths * in an object. * * The options object accepts an "ignore" field with a regular expression * as the value. If any key matches the expression it is not included in * the results. * * The options object accepts a boolean "verbose" field. If set to true * the results will include all paths leading up to endpoints as well as * they endpoints themselves. * * @returns {Array} */ Path.prototype.parseArr = function (obj, options) { options = options || {}; return this._parseArr(obj, '', [], options); }; Path.prototype._parseArr = function (obj, path, paths, options) { var i, newPath = ''; path = path || ''; paths = paths || []; for (i in obj) { if (obj.hasOwnProperty(i)) { if (!options.ignore || (options.ignore &amp;&amp; !options.ignore.test(i))) { if (path) { newPath = path + '.' + i; } else { newPath = i; } if (typeof(obj[i]) === 'object') { if (options.verbose) { paths.push(newPath); } this._parseArr(obj[i], newPath, paths, options); } else { paths.push(newPath); } } } } return paths; }; /** * Sets a value on an object for the specified path. * @param {Object} obj The object to update. * @param {String} path The path to update. * @param {*} val The value to set the object path to. * @returns {*} */ Path.prototype.set = function (obj, path, val) { if (obj !== undefined &amp;&amp; path !== undefined) { var pathParts, part; path = this.clean(path); pathParts = path.split('.'); part = pathParts.shift(); if (pathParts.length) { // Generate the path part in the object if it does not already exist obj[part] = obj[part] || {}; // Recurse this.set(obj[part], pathParts.join('.'), val); } else { // Set the value obj[part] = val; } } return obj; }; /** * Retrieves all the values inside an object based on the passed * path string. Will automatically traverse any arrays it encounters * and assumes array indexes are not part of the specifed path. * @param {Object|Array} obj An object or array of objects to * scan paths for. * @param {String} path The path string delimited by a period. * @return {Array} An array of values found at the end of each path * termination. */ Path.prototype.aggregate = function (obj, path) { var pathParts, part, values = [], i; // First, check if the object we are given // is an array. If so, loop it and work on // the objects inside if (obj instanceof Array) { // Loop array and get path data from each sub object for (i = 0; i &lt; obj.length; i++) { values = values.concat(this.aggregate(obj[i], path)); } return values; } if (path.indexOf('.') === -1) { // No further parts to navigate // Return an array so the value can be concatenated on return via array.concat() return [obj[path]]; } pathParts = path.split('.'); // Grab the next part of our path part = pathParts.shift(); values = values.concat(this.aggregate(obj[part], pathParts.join('.'))); return values; }; /** * Gets a single value from the passed object and given path. * @param {Object} obj The object to inspect. * @param {String} path The path to retrieve data from. * @returns {*} */ Path.prototype.get = function (obj, path) { return this.value(obj, path)[0]; }; /** * Gets the value(s) that the object contains for the currently assigned path string. * @param {Object} obj The object to evaluate the path against. * @param {String=} path A path to use instead of the existing one passed in path(). * @param {Object=} options An optional options object. * @returns {Array} An array of values for the given path. */ Path.prototype.value = function (obj, path, options) { var pathParts, arr, arrCount, objPart, objPartParent, valuesArr, returnArr, i, k; // Detect early exit if (path &amp;&amp; path.indexOf('.') === -1) { if (options &amp;&amp; options.skipArrCheck) { return [obj[path]]; } if (!(obj instanceof Array)) { return [obj[path]]; } } if (obj !== undefined &amp;&amp; typeof obj === 'object') { if (!options || options &amp;&amp; !options.skipArrCheck) { // Check if we were passed an array of objects and if so, // iterate over the array and return the value from each // array item if (obj instanceof Array) { returnArr = []; for (i = 0; i &lt; obj.length; i++) { returnArr.push(this.get(obj[i], path)); } return returnArr; } } valuesArr = []; if (path !== undefined) { path = this.clean(path); pathParts = path.split('.'); } arr = pathParts || this._pathParts; arrCount = arr.length; objPart = obj; for (i = 0; i &lt; arrCount; i++) { objPart = objPart[arr[i]]; if (objPartParent instanceof Array) { // Search inside the array for the next key for (k = 0; k &lt; objPartParent.length; k++) { valuesArr = valuesArr.concat(this.value(objPartParent, k + '.' + arr[i], {skipArrCheck: true})); } return valuesArr; } else { if (!objPart || typeof(objPart) !== 'object') { break; } } objPartParent = objPart; } return [objPart]; } else { return []; } }; /** * Push a value to an array on an object for the specified path. * @param {Object} obj The object to update. * @param {String} path The path to the array to push to. * @param {*} val The value to push to the array at the object path. * @returns {*} */ Path.prototype.push = function (obj, path, val) { if (obj !== undefined &amp;&amp; path !== undefined) { var pathParts, part; path = this.clean(path); pathParts = path.split('.'); part = pathParts.shift(); if (pathParts.length) { // Generate the path part in the object if it does not already exist obj[part] = obj[part] || {}; // Recurse this.set(obj[part], pathParts.join('.'), val); } else { // Set the value obj[part] = obj[part] || []; if (obj[part] instanceof Array) { obj[part].push(val); } else { throw('ForerunnerDB.Path: Cannot push to a path whose endpoint is not an array!'); } } } return obj; }; /** * Gets the value(s) that the object contains for the currently assigned path string * with their associated keys. * @param {Object} obj The object to evaluate the path against. * @param {String=} path A path to use instead of the existing one passed in path(). * @returns {Array} An array of values for the given path with the associated key. */ Path.prototype.keyValue = function (obj, path) { var pathParts, arr, arrCount, objPart, objPartParent, objPartHash, i; if (path !== undefined) { path = this.clean(path); pathParts = path.split('.'); } arr = pathParts || this._pathParts; arrCount = arr.length; objPart = obj; for (i = 0; i &lt; arrCount; i++) { objPart = objPart[arr[i]]; if (!objPart || typeof(objPart) !== 'object') { objPartHash = arr[i] + ':' + objPart; break; } objPartParent = objPart; } return objPartHash; }; /** * Sets a value on an object for the specified path. * @param {Object} obj The object to update. * @param {String} path The path to update. * @param {*} val The value to set the object path to. * @returns {*} */ Path.prototype.set = function (obj, path, val) { if (obj !== undefined &amp;&amp; path !== undefined) { var pathParts, part; path = this.clean(path); pathParts = path.split('.'); part = pathParts.shift(); if (pathParts.length) { // Generate the path part in the object if it does not already exist obj[part] = obj[part] || {}; // Recurse this.set(obj[part], pathParts.join('.'), val); } else { // Set the value obj[part] = val; } } return obj; }; /** * Removes leading period (.) from string and returns it. * @param {String} str The string to clean. * @returns {*} */ Path.prototype.clean = function (str) { if (str.substr(0, 1) === '.') { str = str.substr(1, str.length -1); } return str; }; Shared.finishModule('Path'); module.exports = Path;</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>