123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- 'use strict';
- /* !
- * Chai - pathval utility
- * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com>
- * @see https://github.com/logicalparadox/filtr
- * MIT Licensed
- */
- /**
- * ### .hasProperty(object, name)
- *
- * This allows checking whether an object has own
- * or inherited from prototype chain named property.
- *
- * Basically does the same thing as the `in`
- * operator but works properly with null/undefined values
- * and other primitives.
- *
- * var obj = {
- * arr: ['a', 'b', 'c']
- * , str: 'Hello'
- * }
- *
- * The following would be the results.
- *
- * hasProperty(obj, 'str'); // true
- * hasProperty(obj, 'constructor'); // true
- * hasProperty(obj, 'bar'); // false
- *
- * hasProperty(obj.str, 'length'); // true
- * hasProperty(obj.str, 1); // true
- * hasProperty(obj.str, 5); // false
- *
- * hasProperty(obj.arr, 'length'); // true
- * hasProperty(obj.arr, 2); // true
- * hasProperty(obj.arr, 3); // false
- *
- * @param {Object} object
- * @param {String|Symbol} name
- * @returns {Boolean} whether it exists
- * @namespace Utils
- * @name hasProperty
- * @api public
- */
- function hasProperty(obj, name) {
- if (typeof obj === 'undefined' || obj === null) {
- return false;
- }
- // The `in` operator does not work with primitives.
- return name in Object(obj);
- }
- /* !
- * ## parsePath(path)
- *
- * Helper function used to parse string object
- * paths. Use in conjunction with `internalGetPathValue`.
- *
- * var parsed = parsePath('myobject.property.subprop');
- *
- * ### Paths:
- *
- * * Can be infinitely deep and nested.
- * * Arrays are also valid using the formal `myobject.document[3].property`.
- * * Literal dots and brackets (not delimiter) must be backslash-escaped.
- *
- * @param {String} path
- * @returns {Object} parsed
- * @api private
- */
- function parsePath(path) {
- var str = path.replace(/([^\\])\[/g, '$1.[');
- var parts = str.match(/(\\\.|[^.]+?)+/g);
- return parts.map(function mapMatches(value) {
- if (
- value === 'constructor' ||
- value === '__proto__' ||
- value === 'prototype'
- ) {
- return {};
- }
- var regexp = /^\[(\d+)\]$/;
- var mArr = regexp.exec(value);
- var parsed = null;
- if (mArr) {
- parsed = { i: parseFloat(mArr[1]) };
- } else {
- parsed = { p: value.replace(/\\([.[\]])/g, '$1') };
- }
- return parsed;
- });
- }
- /* !
- * ## internalGetPathValue(obj, parsed[, pathDepth])
- *
- * Helper companion function for `.parsePath` that returns
- * the value located at the parsed address.
- *
- * var value = getPathValue(obj, parsed);
- *
- * @param {Object} object to search against
- * @param {Object} parsed definition from `parsePath`.
- * @param {Number} depth (nesting level) of the property we want to retrieve
- * @returns {Object|Undefined} value
- * @api private
- */
- function internalGetPathValue(obj, parsed, pathDepth) {
- var temporaryValue = obj;
- var res = null;
- pathDepth = typeof pathDepth === 'undefined' ? parsed.length : pathDepth;
- for (var i = 0; i < pathDepth; i++) {
- var part = parsed[i];
- if (temporaryValue) {
- if (typeof part.p === 'undefined') {
- temporaryValue = temporaryValue[part.i];
- } else {
- temporaryValue = temporaryValue[part.p];
- }
- if (i === pathDepth - 1) {
- res = temporaryValue;
- }
- }
- }
- return res;
- }
- /* !
- * ## internalSetPathValue(obj, value, parsed)
- *
- * Companion function for `parsePath` that sets
- * the value located at a parsed address.
- *
- * internalSetPathValue(obj, 'value', parsed);
- *
- * @param {Object} object to search and define on
- * @param {*} value to use upon set
- * @param {Object} parsed definition from `parsePath`
- * @api private
- */
- function internalSetPathValue(obj, val, parsed) {
- var tempObj = obj;
- var pathDepth = parsed.length;
- var part = null;
- // Here we iterate through every part of the path
- for (var i = 0; i < pathDepth; i++) {
- var propName = null;
- var propVal = null;
- part = parsed[i];
- // If it's the last part of the path, we set the 'propName' value with the property name
- if (i === pathDepth - 1) {
- propName = typeof part.p === 'undefined' ? part.i : part.p;
- // Now we set the property with the name held by 'propName' on object with the desired val
- tempObj[propName] = val;
- } else if (typeof part.p !== 'undefined' && tempObj[part.p]) {
- tempObj = tempObj[part.p];
- } else if (typeof part.i !== 'undefined' && tempObj[part.i]) {
- tempObj = tempObj[part.i];
- } else {
- // If the obj doesn't have the property we create one with that name to define it
- var next = parsed[i + 1];
- // Here we set the name of the property which will be defined
- propName = typeof part.p === 'undefined' ? part.i : part.p;
- // Here we decide if this property will be an array or a new object
- propVal = typeof next.p === 'undefined' ? [] : {};
- tempObj[propName] = propVal;
- tempObj = tempObj[propName];
- }
- }
- }
- /**
- * ### .getPathInfo(object, path)
- *
- * This allows the retrieval of property info in an
- * object given a string path.
- *
- * The path info consists of an object with the
- * following properties:
- *
- * * parent - The parent object of the property referenced by `path`
- * * name - The name of the final property, a number if it was an array indexer
- * * value - The value of the property, if it exists, otherwise `undefined`
- * * exists - Whether the property exists or not
- *
- * @param {Object} object
- * @param {String} path
- * @returns {Object} info
- * @namespace Utils
- * @name getPathInfo
- * @api public
- */
- function getPathInfo(obj, path) {
- var parsed = parsePath(path);
- var last = parsed[parsed.length - 1];
- var info = {
- parent:
- parsed.length > 1 ?
- internalGetPathValue(obj, parsed, parsed.length - 1) :
- obj,
- name: last.p || last.i,
- value: internalGetPathValue(obj, parsed),
- };
- info.exists = hasProperty(info.parent, info.name);
- return info;
- }
- /**
- * ### .getPathValue(object, path)
- *
- * This allows the retrieval of values in an
- * object given a string path.
- *
- * var obj = {
- * prop1: {
- * arr: ['a', 'b', 'c']
- * , str: 'Hello'
- * }
- * , prop2: {
- * arr: [ { nested: 'Universe' } ]
- * , str: 'Hello again!'
- * }
- * }
- *
- * The following would be the results.
- *
- * getPathValue(obj, 'prop1.str'); // Hello
- * getPathValue(obj, 'prop1.att[2]'); // b
- * getPathValue(obj, 'prop2.arr[0].nested'); // Universe
- *
- * @param {Object} object
- * @param {String} path
- * @returns {Object} value or `undefined`
- * @namespace Utils
- * @name getPathValue
- * @api public
- */
- function getPathValue(obj, path) {
- var info = getPathInfo(obj, path);
- return info.value;
- }
- /**
- * ### .setPathValue(object, path, value)
- *
- * Define the value in an object at a given string path.
- *
- * ```js
- * var obj = {
- * prop1: {
- * arr: ['a', 'b', 'c']
- * , str: 'Hello'
- * }
- * , prop2: {
- * arr: [ { nested: 'Universe' } ]
- * , str: 'Hello again!'
- * }
- * };
- * ```
- *
- * The following would be acceptable.
- *
- * ```js
- * var properties = require('tea-properties');
- * properties.set(obj, 'prop1.str', 'Hello Universe!');
- * properties.set(obj, 'prop1.arr[2]', 'B');
- * properties.set(obj, 'prop2.arr[0].nested.value', { hello: 'universe' });
- * ```
- *
- * @param {Object} object
- * @param {String} path
- * @param {Mixed} value
- * @api private
- */
- function setPathValue(obj, path, val) {
- var parsed = parsePath(path);
- internalSetPathValue(obj, val, parsed);
- return obj;
- }
- module.exports = {
- hasProperty: hasProperty,
- getPathInfo: getPathInfo,
- getPathValue: getPathValue,
- setPathValue: setPathValue,
- };
|