--- /dev/null
+/**\r
+ * Lodash mixins for (deep) object accessing / manipulation.\r
+ * @author Mark Lagendijk <mark@lagendijk.info>\r
+ * @license MIT\r
+ */\r
+(function(root, factory){\r
+ if(typeof define === 'function' && define.amd){\r
+ // AMD. Register as an anonymous module.\r
+ define(['lodash'], factory);\r
+ }\r
+ else if(typeof exports === 'object'){\r
+ // Node. Does not work with strict CommonJS, but\r
+ // only CommonJS-like environments that support module.exports,\r
+ // like Node.\r
+ module.exports = factory(require('lodash').runInContext());\r
+ }\r
+ else{\r
+ // Browser globals (root is window)\r
+ root._.mixin(factory(root._));\r
+ }\r
+}(this, function(_, undefined){\r
+ 'use strict';\r
+\r
+ var mixins = /** @lends _ */ {\r
+ /**\r
+ * Executes a deep check for the existence of a property in an object tree.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {boolean}\r
+ */\r
+ deepIn: function(collection, propertyPath){\r
+ var properties = getProperties(propertyPath);\r
+ for(var i = 0; i < properties.length; i++){\r
+ var property = properties[i];\r
+ if(_.has(collection, property) ||\r
+ _.isObject(collection) && property in collection){\r
+ collection = collection[property];\r
+ }\r
+ else{\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ },\r
+ /**\r
+ * Executes a deep check for the existence of a own property in an object tree.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {boolean}\r
+ */\r
+ deepHas: function(collection, propertyPath){\r
+ var properties = getProperties(propertyPath);\r
+ for(var i = 0; i < properties.length; i++){\r
+ var property = properties[i];\r
+ if(_.has(collection, property)){\r
+ collection = collection[property];\r
+ }\r
+ else{\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ },\r
+ /**\r
+ * Retrieves the own value of a property in an object tree.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {*} - The value, or undefined if it doesn't exists.\r
+ */\r
+ deepOwn: function(collection, propertyPath){\r
+ var properties = getProperties(propertyPath);\r
+ if(_.deepHas(collection, properties)){\r
+ return _.reduce(properties, function(object, property){\r
+ return object[property];\r
+ }, collection);\r
+ }\r
+ },\r
+ /**\r
+ * Retrieves the value of a property in an object tree.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {*} - The value, or undefined if it doesn't exists.\r
+ */\r
+ deepGet: function(collection, propertyPath){\r
+ var properties = getProperties(propertyPath);\r
+ if(_.deepIn(collection, properties)){\r
+ return _.reduce(properties, function(object, property){\r
+ return object[property];\r
+ }, collection);\r
+ }\r
+ },\r
+ /**\r
+ * Sets a value of a property in an object tree. Any missing objects/arrays will be created.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @param {*} value - The value to set.\r
+ * @returns {Object} The object.\r
+ */\r
+ deepSet: function(collection, propertyPath, value){\r
+ var properties = getProperties(propertyPath);\r
+ var currentObject = collection;\r
+ _.forEach(properties, function(property, index){\r
+ if(index + 1 === properties.length){\r
+ currentObject[property] = value;\r
+ }\r
+ else if(!_.isObject(currentObject[property])){\r
+ currentObject[property] = isArrayKey(properties[index + 1]) ? [] : {};\r
+ }\r
+ currentObject = currentObject[property];\r
+ });\r
+\r
+ return collection;\r
+ },\r
+ /**\r
+ * Checks if the value at the propertyPath resolves to undefined, and sets it to defaultValue if this is the\r
+ * case.\r
+ * @param {Object|Array} collection - The collection of object trees.\r
+ * @param {string|Array} propertyPath - The propertyPath of the function.\r
+ * @param {*} defaultValue - The default value.\r
+ * @returns {*} Either the existing, unchanged value, or the new (default) value.\r
+ */\r
+ deepDefault: function(collection, propertyPath, defaultValue){\r
+ var value = _.deepGet(collection, propertyPath);\r
+ if(_.isUndefined(value)){\r
+ _.deepSet(collection, propertyPath, defaultValue);\r
+ return defaultValue;\r
+ }\r
+ else{\r
+ return value;\r
+ }\r
+ },\r
+ /**\r
+ * Calls a function located at the specified property path, if it exists.\r
+ * @param {Object|Array} collection - The collection of object trees.\r
+ * @param {string|Array} propertyPath - The propertyPath of the function.\r
+ * @param {Object} [thisArg] - The 'this' argument the function should be executed with.\r
+ * @param {...*} [arg] - One of the arguments the function should be executed with. Can occur 0..n times.\r
+ * @returns {*} The result of executing the function, or undefined if it doesn't exist.\r
+ */\r
+ deepCall: function(collection, propertyPath, thisArg, arg){\r
+ var args = Array.prototype.slice.call(arguments, 3);\r
+ return _.deepApply(collection, propertyPath, thisArg, args);\r
+ },\r
+ /**\r
+ * Applies a function located at the specified property path, if it exists.\r
+ * @param {Object|Array} collection - The collection of object trees.\r
+ * @param {string|Array} propertyPath - The propertyPath of the function.\r
+ * @param {Object} [thisArg] - The 'this' argument the function should be executed with.\r
+ * @param {Array} [args] - An array of the arguments the function should be executed with.\r
+ * @returns {*} The result of executing the function, or undefined if it doesn't exist.\r
+ */\r
+ deepApply: function(collection, propertyPath, thisArg, args){\r
+ var func = _.deepGet(collection, propertyPath);\r
+ if(_.isFunction(func)){\r
+ return func.apply(thisArg, args);\r
+ }\r
+ },\r
+ /**\r
+ * Escapes a property name for usage in a string based property path.\r
+ * @param {string} propertyName - The name / key of the property.\r
+ * @returns {string}\r
+ */\r
+ deepEscapePropertyName: function(propertyName){\r
+ return propertyName\r
+ .replace(/\\/g, '\\\\')\r
+ .replace(/(\.|\[|\])/g, '\\$1');\r
+ },\r
+ /**\r
+ * Maps all values in an object tree and returns a new object with the same structure as the original.\r
+ * @param {Object} object - The object to map.\r
+ * @param {Function} callback - The function to be called per iteration on any non-object value in the tree.\r
+ * Callback is invoked with 2 arguments: (value, propertyPath)\r
+ * propertyPath is the path of the current property, in array format.\r
+ * @returns {Object}\r
+ */\r
+ deepMapValues: function(object, callback, propertyPath){\r
+ var properties = getProperties(propertyPath);\r
+ if(_.isArray(object)){\r
+ return _.map(object, deepMapValuesIteratee);\r
+ }\r
+ else if(_.isObject(object) && !_.isDate(object) && !_.isRegExp(object)){\r
+ return _.extend({}, object, _.mapValues(object, deepMapValuesIteratee));\r
+ }\r
+ else{\r
+ return callback(object, properties);\r
+ }\r
+\r
+ function deepMapValuesIteratee(value, key){\r
+ return _.deepMapValues(value, callback, _.flatten([properties, key]));\r
+ }\r
+ },\r
+ /**\r
+ * @function\r
+ * Executes a deep pluck on an collection of object trees.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepPluck: createDeepPluckStyleCallback(_.map),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#findIndex _.findIndex} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {int}\r
+ */\r
+ deepFindIndex: createDeepPluckStyleCallback(_.findIndex),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#findLastIndex _.findLastIndex} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {int}\r
+ */\r
+ deepFindLastIndex: createDeepPluckStyleCallback(_.findLastIndex),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#first _.first} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {*}\r
+ */\r
+ deepFirst: createDeepPluckStyleCallback(_.first),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#flatten _.flatten} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepFlatten: createDeepPluckStyleCallback(_.flatten),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#initial _.initial} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepInitial: createDeepPluckStyleCallback(_.initial),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#last _.last} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {*}\r
+ */\r
+ deepLast: createDeepPluckStyleCallback(_.last),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#lastIndexOf _.lastIndexOf} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {int}\r
+ */\r
+ deepLastIndexOf: createDeepPluckStyleCallback(_.lastIndexOf),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#remove _.remove} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepRemove: createDeepPluckStyleCallback(_.remove),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#rest _.rest} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepRest: createDeepPluckStyleCallback(_.rest),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#sortedIndex _.sortedIndex} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {int}\r
+ */\r
+ deepSortedIndex: createDeepPluckStyleCallback(_.sortedIndex),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#uniq _.uniq} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepUniq: createDeepPluckStyleCallback(_.uniq),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#countBy _.countBy} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Object}\r
+ */\r
+ deepCountBy: createDeepPluckStyleCallback(_.countBy),\r
+ /**\r
+ * @function\r
+ * Executes{@link https://lodash.com/docs#every _.every} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {boolean}\r
+ */\r
+ deepEvery: createDeepPluckStyleCallback(_.every),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#filter _.filter} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepFilter: createDeepPluckStyleCallback(_.filter),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#find _.find} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {*}\r
+ */\r
+ deepFind: createDeepPluckStyleCallback(_.find),\r
+ /**\r
+ * @function\r
+ * Executes{@link https://lodash.com/docs#groupBy _.groupBy} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Object}\r
+ */\r
+ deepGroupBy: createDeepPluckStyleCallback(_.groupBy),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#indexBy _.indexBy} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Object}\r
+ */\r
+ deepIndexBy: createDeepPluckStyleCallback(_.indexBy),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#max _.max} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {*}\r
+ */\r
+ deepMax: createDeepPluckStyleCallback(_.max),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#min _.min} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {*}\r
+ */\r
+ deepMin: createDeepPluckStyleCallback(_.min),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#reject _.reject} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepReject: createDeepPluckStyleCallback(_.reject),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#some _.some} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {boolean}\r
+ */\r
+ deepSome: createDeepPluckStyleCallback(_.some),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#sortBy _.sortBy} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {Array}\r
+ */\r
+ deepSortBy: createDeepPluckStyleCallback(_.sortBy),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#findKey _.findKey} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {string|undefined}\r
+ */\r
+ deepFindKey: createDeepPluckStyleCallback(_.findKey),\r
+ /**\r
+ * @function\r
+ * Executes {@link https://lodash.com/docs#findLastKey _.findLastKey} with a "_.deepPluck" style callback.\r
+ * @param {Object|Array} collection - The root object/array of the tree.\r
+ * @param {string|Array} propertyPath - The propertyPath.\r
+ * @returns {string|undefined}\r
+ */\r
+ deepFindLastKey: createDeepPluckStyleCallback(_.findLastKey)\r
+ };\r
+\r
+ // Support pre 1.2.0 function names\r
+ mixins.deepSetValue = mixins.deepSet;\r
+ mixins.deepGetValue = mixins.deepGet;\r
+ mixins.deepGetOwnValue = mixins.deepOwn;\r
+\r
+ _.mixin(mixins);\r
+\r
+ /**\r
+ * Returns the property path as array.\r
+ * @param {string|Array} propertyPath\r
+ * @returns {Array}\r
+ */\r
+ function getProperties(propertyPath){\r
+ if(_.isArray(propertyPath)){\r
+ return propertyPath;\r
+ }\r
+\r
+ if(!_.isString(propertyPath)){\r
+ return [];\r
+ }\r
+\r
+ return parseStringPropertyPath(propertyPath);\r
+ }\r
+\r
+ /**\r
+ * Parses a string based propertyPath\r
+ * @param {string} propertyPath\r
+ * @returns {Array}\r
+ */\r
+ function parseStringPropertyPath(propertyPath){\r
+ var character = '';\r
+ var parsedPropertyPath = [];\r
+ var parsedPropertyPathPart = '';\r
+ var escapeNextCharacter = false;\r
+ var isSpecialCharacter = false;\r
+ var insideBrackets = false;\r
+\r
+ // Walk through the path and find backslashes that escape periods or other backslashes, and split on unescaped\r
+ // periods and brackets.\r
+ for(var i = 0; i < propertyPath.length; i++){\r
+ character = propertyPath[i];\r
+ isSpecialCharacter = (character === '\\' || character === '[' || character === ']' || character === '.');\r
+\r
+ if(isSpecialCharacter && !escapeNextCharacter){\r
+ if(insideBrackets && character !== ']'){\r
+ throw new SyntaxError('unexpected "' + character + '" within brackets at character ' + i + ' in property path ' + propertyPath);\r
+ }\r
+\r
+ switch(character){\r
+ case '\\':\r
+ escapeNextCharacter = true;\r
+ break;\r
+ case ']':\r
+ insideBrackets = false;\r
+ break;\r
+ case '[':\r
+ insideBrackets = true;\r
+ /* falls through */\r
+ case '.':\r
+ parsedPropertyPath.push(parsedPropertyPathPart);\r
+ parsedPropertyPathPart = '';\r
+ break;\r
+ }\r
+ }\r
+ else{\r
+ parsedPropertyPathPart += character;\r
+ escapeNextCharacter = false;\r
+ }\r
+ }\r
+\r
+ if(parsedPropertyPath[0] === ''){\r
+ //allow '[0]', or '.0'\r
+ parsedPropertyPath.splice(0, 1);\r
+ }\r
+\r
+ // capture the final part\r
+ parsedPropertyPath.push(parsedPropertyPathPart);\r
+ return parsedPropertyPath;\r
+ }\r
+\r
+ /**\r
+ * Creates a function which executes the originalFunction with a "_.deepPluck" style callback.\r
+ * @param {Function} originalFunction - The orignal (Lodash) function.\r
+ * @returns {Function}\r
+ */\r
+ function createDeepPluckStyleCallback(originalFunction){\r
+ return function(collection, propertyPath){\r
+ return originalFunction(collection, function(item){\r
+ return _.deepGet(item, propertyPath);\r
+ });\r
+ };\r
+ }\r
+\r
+ /**\r
+ * Checks whether key is a valid array key\r
+ * @param key\r
+ * @returns {boolean}\r
+ */\r
+ function isArrayKey(key){\r
+ var array = [];\r
+ array[key] = null;\r
+ return array.length > 0;\r
+ }\r
+\r
+ return mixins;\r
+}));\r