forerunnerdb
Version:
A NoSQL document store database for browsers and Node.js.
587 lines (485 loc) • 15.9 kB
HTML
<!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 && testKeys[i] && 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 < resultData.length; k++) {
paths.push({
path: path + '.' + resultData[k].path,
value: resultData[k].value
});
}
} else {
resultData = this.parse(obj[i]);
for (k = 0; k < 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 && !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 && 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 < 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 && path.indexOf('.') === -1) {
if (options && options.skipArrCheck) {
return [obj[path]];
}
if (!(obj instanceof Array)) {
return [obj[path]];
}
}
if (obj !== undefined && typeof obj === 'object') {
if (!options || options && !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 < 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 < arrCount; i++) {
objPart = objPart[arr[i]];
if (objPartParent instanceof Array) {
// Search inside the array for the next key
for (k = 0; k < 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 && 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 < 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 && 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>