UNPKG

generator-yomonger

Version:

A yeoman generator for MODX Manager custom themes.

1,539 lines (1,403 loc) 136 kB
/*! * Lo-Dash v0.7.0 <http://lodash.com> * Copyright 2012 John-David Dalton <http://allyoucanleet.com/> * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. * <http://documentcloud.github.com/underscore> * Available under MIT license <http://lodash.com/license> */ ;(function(window, undefined) { 'use strict'; /** Detect free variable `exports` */ var freeExports = typeof exports == 'object' && exports && (typeof global == 'object' && global && global == global.global && (window = global), exports); /** Native prototype shortcuts */ var ArrayProto = Array.prototype, BoolProto = Boolean.prototype, ObjectProto = Object.prototype, NumberProto = Number.prototype, StringProto = String.prototype; /** Used to generate unique IDs */ var idCounter = 0; /** Used by `cachedContains` as the default size when optimizations are enabled for large arrays */ var largeArraySize = 30; /** Used to restore the original `_` reference in `noConflict` */ var oldDash = window._; /** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */ var reComplexDelimiter = /[-?+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; /** Used to match HTML entities */ var reEscapedHtml = /&(?:amp|lt|gt|quot|#x27);/g; /** Used to match empty string literals in compiled template source */ var reEmptyStringLeading = /\b__p \+= '';/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; /** Used to match regexp flags from their coerced string values */ var reFlags = /\w*$/; /** Used to insert the data object variable into compiled template source */ var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; /** Used to detect if a method is native */ var reNative = RegExp('^' + (ObjectProto.valueOf + '') .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') .replace(/valueOf|for [^\]]+/g, '.+?') + '$' ); /** Used to ensure capturing order and avoid matches for undefined delimiters */ var reNoMatch = /($^)/; /** Used to match HTML characters */ var reUnescapedHtml = /[&<>"']/g; /** Used to match unescaped characters in compiled string literals */ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; /** Used to fix the JScript [[DontEnum]] bug */ var shadowed = [ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf' ]; /** Used to make template sourceURLs easier to identify */ var templateCounter = 0; /** Native method shortcuts */ var concat = ArrayProto.concat, hasOwnProperty = ObjectProto.hasOwnProperty, push = ArrayProto.push, propertyIsEnumerable = ObjectProto.propertyIsEnumerable, slice = ArrayProto.slice, toString = ObjectProto.toString; /* Native method shortcuts for methods with the same name as other `lodash` methods */ var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, nativeFloor = Math.floor, nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, nativeIsFinite = window.isFinite, nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, nativeMax = Math.max, nativeMin = Math.min, nativeRandom = Math.random; /** `Object#toString` result shortcuts */ var argsClass = '[object Arguments]', arrayClass = '[object Array]', boolClass = '[object Boolean]', dateClass = '[object Date]', funcClass = '[object Function]', numberClass = '[object Number]', objectClass = '[object Object]', regexpClass = '[object RegExp]', stringClass = '[object String]'; /** Timer shortcuts */ var clearTimeout = window.clearTimeout, setTimeout = window.setTimeout; /** * Detect the JScript [[DontEnum]] bug: * * In IE < 9 an objects own properties, shadowing non-enumerable ones, are * made non-enumerable as well. */ var hasDontEnumBug; /** * Detect if `Array#shift` and `Array#splice` augment array-like objects * incorrectly: * * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` * and `splice()` functions that fail to remove the last element, `value[0]`, * of array-like objects even though the `length` property is set to `0`. * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. */ var hasObjectSpliceBug; /** Detect if own properties are iterated after inherited properties (IE < 9) */ var iteratesOwnLast; /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */ var noArgsEnum = true; (function() { var object = { '0': 1, 'length': 1 }, props = []; function ctor() { this.x = 1; } ctor.prototype = { 'valueOf': 1, 'y': 1 }; for (var prop in new ctor) { props.push(prop); } for (prop in arguments) { noArgsEnum = !prop; } hasDontEnumBug = (props + '').length < 4; iteratesOwnLast = props[0] != 'x'; hasObjectSpliceBug = (props.splice.call(object, 0, 1), object[0]); }(1)); /** Detect if an `arguments` object's [[Class]] is unresolvable (Firefox < 4, IE < 9) */ var noArgsClass = !isArguments(arguments); /** Detect if `Array#slice` cannot be used to convert strings to arrays (Opera < 10.52) */ var noArraySliceOnStrings = slice.call('x')[0] != 'x'; /** * Detect lack of support for accessing string characters by index: * * IE < 8 can't access characters by index and IE 8 can only access * characters by index on string literals. */ var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; /** * Detect if a node's [[Class]] is unresolvable (IE < 9) * and that the JS engine won't error when attempting to coerce an object to * a string without a `toString` property value of `typeof` "function". */ try { var noNodeClass = ({ 'toString': 0 } + '', toString.call(window.document || 0) == objectClass); } catch(e) { } /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); /* Detect if strict mode, "use strict", is inferred to be fast (V8) */ var isStrictFast = !isBindFast; /** * Detect if sourceURL syntax is usable without erroring: * * The JS engine in Adobe products, like InDesign, will throw a syntax error * when it encounters a single line comment beginning with the `@` symbol. * * The JS engine in Narwhal will generate the function `function anonymous(){//}` * and throw a syntax error. * * Avoid comments beginning `@` symbols in IE because they are part of its * non-standard conditional compilation support. * http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx */ try { var useSourceURL = (Function('//@')(), !window.attachEvent); } catch(e) { } /** Used to identify object classifications that are array-like */ var arrayLikeClasses = {}; arrayLikeClasses[boolClass] = arrayLikeClasses[dateClass] = arrayLikeClasses[funcClass] = arrayLikeClasses[numberClass] = arrayLikeClasses[objectClass] = arrayLikeClasses[regexpClass] = false; arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true; /** Used to identify object classifications that `_.clone` supports */ var cloneableClasses = {}; cloneableClasses[argsClass] = cloneableClasses[funcClass] = false; cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] = cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; /** * Used to convert characters to HTML entities: * * Though the `>` character is escaped for symmetry, characters like `>` and `/` * don't require escaping in HTML and have no special meaning unless they're part * of a tag or an unquoted attribute value. * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact") */ var htmlEscapes = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#x27;' }; /** Used to convert HTML entities to characters */ var htmlUnescapes = { '&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"', '&#x27;': "'" }; /** Used to determine if values are of the language type Object */ var objectTypes = { 'boolean': false, 'function': true, 'object': true, 'number': false, 'string': false, 'undefined': false, 'unknown': true }; /** Used to escape characters for inclusion in compiled string literals */ var stringEscapes = { '\\': '\\', "'": "'", '\n': 'n', '\r': 'r', '\t': 't', '\u2028': 'u2028', '\u2029': 'u2029' }; /*--------------------------------------------------------------------------*/ /** * The `lodash` function. * * @name _ * @constructor * @param {Mixed} value The value to wrap in a `LoDash` instance. * @returns {Object} Returns a `LoDash` instance. */ function lodash(value) { // allow invoking `lodash` without the `new` operator return new LoDash(value); } /** * Creates a `LoDash` instance that wraps a value to allow chaining. * * @private * @constructor * @param {Mixed} value The value to wrap. */ function LoDash(value) { // exit early if already wrapped if (value && value.__wrapped__) { return value; } this.__wrapped__ = value; } /** * By default, the template delimiters used by Lo-Dash are similar to those in * embedded Ruby (ERB). Change the following template settings to use alternative * delimiters. * * @static * @memberOf _ * @type Object */ lodash.templateSettings = { /** * Used to detect `data` property values to be HTML-escaped. * * @static * @memberOf _.templateSettings * @type RegExp */ 'escape': /<%-([\s\S]+?)%>/g, /** * Used to detect code to be evaluated. * * @static * @memberOf _.templateSettings * @type RegExp */ 'evaluate': /<%([\s\S]+?)%>/g, /** * Used to detect `data` property values to inject. * * @static * @memberOf _.templateSettings * @type RegExp */ 'interpolate': /<%=([\s\S]+?)%>/g, /** * Used to reference the data object in the template text. * * @static * @memberOf _.templateSettings * @type String */ 'variable': '' }; /*--------------------------------------------------------------------------*/ /** * The template used to create iterator functions. * * @private * @param {Obect} data The data object used to populate the text. * @returns {String} Returns the interpolated text. */ var iteratorTemplate = template( // conditional strict mode '<% if (useStrict) { %>\'use strict\';\n<% } %>' + // the `iteratee` may be reassigned by the `top` snippet 'var index, value, iteratee = <%= firstArg %>, ' + // assign the `result` variable an initial value 'result<% if (init) { %> = <%= init %><% } %>;\n' + // add code to exit early or do so if the first argument is falsey '<%= exit %>;\n' + // add code after the exit snippet but before the iteration branches '<%= top %>;\n' + // the following branch is for iterating arrays and array-like objects '<% if (arrayBranch) { %>' + 'var length = iteratee.length; index = -1;' + ' <% if (objectBranch) { %>\nif (length === +length) {<% } %>' + // add support for accessing string characters by index if needed ' <% if (noCharByIndex) { %>\n' + ' if (toString.call(iteratee) == stringClass) {\n' + ' iteratee = iteratee.split(\'\')\n' + ' }' + ' <% } %>\n' + ' <%= arrayBranch.beforeLoop %>;\n' + ' while (++index < length) {\n' + ' value = iteratee[index];\n' + ' <%= arrayBranch.inLoop %>\n' + ' }' + ' <% if (objectBranch) { %>\n}<% } %>' + '<% } %>' + // the following branch is for iterating an object's own/inherited properties '<% if (objectBranch) { %>' + ' <% if (arrayBranch) { %>\nelse {' + // add support for iterating over `arguments` objects if needed ' <% } else if (noArgsEnum) { %>\n' + ' var length = iteratee.length; index = -1;\n' + ' if (length && isArguments(iteratee)) {\n' + ' while (++index < length) {\n' + ' value = iteratee[index += \'\'];\n' + ' <%= objectBranch.inLoop %>\n' + ' }\n' + ' } else {' + ' <% } %>' + // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 // (if the prototype or a property on the prototype has been set) // incorrectly sets a function's `prototype` property [[Enumerable]] // value to `true`. Because of this Lo-Dash standardizes on skipping // the the `prototype` property of functions regardless of its // [[Enumerable]] value. ' <% if (!hasDontEnumBug) { %>\n' + ' var skipProto = typeof iteratee == \'function\' && \n' + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + ' <% } %>' + // iterate own properties using `Object.keys` if it's fast ' <% if (isKeysFast && useHas) { %>\n' + ' var ownIndex = -1,\n' + ' ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' + ' length = ownProps.length;\n\n' + ' <%= objectBranch.beforeLoop %>;\n' + ' while (++ownIndex < length) {\n' + ' index = ownProps[ownIndex];\n' + ' <% if (!hasDontEnumBug) { %>if (!(skipProto && index == \'prototype\')) {\n <% } %>' + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>\n' + ' <% if (!hasDontEnumBug) { %>}\n<% } %>' + ' }' + // else using a for-in loop ' <% } else { %>\n' + ' <%= objectBranch.beforeLoop %>;\n' + ' for (index in iteratee) {<%' + ' if (!hasDontEnumBug || useHas) { %>\n if (<%' + ' if (!hasDontEnumBug) { %>!(skipProto && index == \'prototype\')<% }' + ' if (!hasDontEnumBug && useHas) { %> && <% }' + ' if (useHas) { %>hasOwnProperty.call(iteratee, index)<% }' + ' %>) {' + ' <% } %>\n' + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>;' + ' <% if (!hasDontEnumBug || useHas) { %>\n }<% } %>\n' + ' }' + ' <% } %>' + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an // existing property and the `constructor` property of a prototype // defaults to non-enumerable, Lo-Dash skips the `constructor` // property when it infers it's iterating over a `prototype` object. ' <% if (hasDontEnumBug) { %>\n\n' + ' var ctor = iteratee.constructor;\n' + ' <% for (var k = 0; k < 7; k++) { %>\n' + ' index = \'<%= shadowed[k] %>\';\n' + ' if (<%' + ' if (shadowed[k] == \'constructor\') {' + ' %>!(ctor && ctor.prototype === iteratee) && <%' + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>\n' + ' }' + ' <% } %>' + ' <% } %>' + ' <% if (arrayBranch || noArgsEnum) { %>\n}<% } %>' + '<% } %>\n' + // add code to the bottom of the iteration function '<%= bottom %>;\n' + // finally, return the `result` 'return result' ); /** * Reusable iterator options shared by * `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`, * `reject`, `some`, and `sortBy`. */ var baseIteratorOptions = { 'args': 'collection, callback, thisArg', 'init': 'collection', 'top': 'if (!callback) {\n' + ' callback = identity\n' + '}\n' + 'else if (thisArg) {\n' + ' callback = bindIterator(callback, thisArg)\n' + '}', 'inLoop': 'if (callback(value, index, collection) === false) return result' }; /** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */ var countByIteratorOptions = { 'init': '{}', 'top': 'var prop;\n' + 'if (typeof callback != \'function\') {\n' + ' var valueProp = callback;\n' + ' callback = function(value) { return value[valueProp] }\n' + '}\n' + 'else if (thisArg) {\n' + ' callback = bindIterator(callback, thisArg)\n' + '}', 'inLoop': 'prop = callback(value, index, collection);\n' + '(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)' }; /** Reusable iterator options for `every` and `some` */ var everyIteratorOptions = { 'init': 'true', 'inLoop': 'if (!callback(value, index, collection)) return !result' }; /** Reusable iterator options for `defaults` and `extend` */ var extendIteratorOptions = { 'useHas': false, 'useStrict': false, 'args': 'object', 'init': 'object', 'top': 'for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {\n' + ' if (iteratee = arguments[argsIndex]) {', 'inLoop': 'result[index] = value', 'bottom': ' }\n}' }; /** Reusable iterator options for `filter`, `reject`, and `where` */ var filterIteratorOptions = { 'init': '[]', 'inLoop': 'callback(value, index, collection) && result.push(value)' }; /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ var forEachIteratorOptions = { 'top': 'if (thisArg) callback = bindIterator(callback, thisArg)' }; /** Reusable iterator options for `forIn` and `forOwn` */ var forOwnIteratorOptions = { 'inLoop': { 'object': baseIteratorOptions.inLoop } }; /** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ var mapIteratorOptions = { 'init': '', 'exit': 'if (!collection) return []', 'beforeLoop': { 'array': 'result = Array(length)', 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') }, 'inLoop': { 'array': 'result[index] = callback(value, index, collection)', 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(callback(value, index, collection))' } }; /** Reusable iterator options for `omit` and `pick` */ var omitIteratorOptions = { 'useHas': false, 'args': 'object, callback, thisArg', 'init': '{}', 'top': 'var isFunc = typeof callback == \'function\';\n' + 'if (!isFunc) {\n' + ' var props = concat.apply(ArrayProto, arguments)\n' + '} else if (thisArg) {\n' + ' callback = bindIterator(callback, thisArg)\n' + '}', 'inLoop': 'if (isFunc\n' + ' ? !callback(value, index, object)\n' + ' : indexOf(props, index) < 0\n' + ') result[index] = value' }; /*--------------------------------------------------------------------------*/ /** * Creates a bound iterator function that, when called, invokes `func` with * the `this` binding of `thisArg` and the arguments (value, index, object). * * @private * @param {Function} func The function to bind. * @param {Mixed} [thisArg] The `this` binding of `func`. * @returns {Function} Returns the new bound function. */ function bindIterator(func, thisArg) { return function(value, index, object) { return func.call(thisArg, value, index, object); }; } /** * Creates a function optimized for searching large arrays for a given `value`, * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`. * * @private * @param {Array} array The array to search. * @param {Mixed} value The value to search for. * @param {Number} [fromIndex=0] The index to start searching from. * @param {Number} [largeSize=30] The length at which an array is considered large. * @returns {Boolean} Returns `true` if `value` is found, else `false`. */ function cachedContains(array, fromIndex, largeSize) { fromIndex || (fromIndex = 0); var length = array.length, isLarge = (length - fromIndex) >= (largeSize || largeArraySize), cache = isLarge ? {} : array; if (isLarge) { // init value cache var key, index = fromIndex - 1; while (++index < length) { // manually coerce `value` to string because `hasOwnProperty`, in some // older versions of Firefox, coerces objects incorrectly key = array[index] + ''; (hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = [])).push(array[index]); } } return function(value) { if (isLarge) { var key = value + ''; return hasOwnProperty.call(cache, key) && indexOf(cache[key], value) > -1; } return indexOf(cache, value, fromIndex) > -1; } } /** * Used by `sortBy` to compare transformed `collection` values, stable sorting * them in ascending order. * * @private * @param {Object} a The object to compare to `b`. * @param {Object} b The object to compare to `a`. * @returns {Number} Returns the sort order indicator of `1` or `-1`. */ function compareAscending(a, b) { var ai = a.index, bi = b.index; a = a.criteria; b = b.criteria; // ensure a stable sort in V8 and other engines // http://code.google.com/p/v8/issues/detail?id=90 if (a !== b) { if (a > b || a === undefined) { return 1; } if (a < b || b === undefined) { return -1; } } return ai < bi ? -1 : 1; } /** * Creates a function that, when called, invokes `func` with the `this` * binding of `thisArg` and prepends any `partailArgs` to the arguments passed * to the bound function. * * @private * @param {Function|String} func The function to bind or the method name. * @param {Mixed} [thisArg] The `this` binding of `func`. * @param {Array} partialArgs An array of arguments to be partially applied. * @returns {Function} Returns the new bound function. */ function createBound(func, thisArg, partialArgs) { var isFunc = isFunction(func), isPartial = !partialArgs, methodName = func; // juggle arguments if (isPartial) { partialArgs = thisArg; } function bound() { // `Function#bind` spec // http://es5.github.com/#x15.3.4.5 var args = arguments, thisBinding = isPartial ? this : thisArg; if (!isFunc) { func = thisArg[methodName]; } if (partialArgs.length) { args = args.length ? partialArgs.concat(slice.call(args)) : partialArgs; } if (this instanceof bound) { // get `func` instance if `bound` is invoked in a `new` expression noop.prototype = func.prototype; thisBinding = new noop; // mimic the constructor's `return` behavior // http://es5.github.com/#x13.2.2 var result = func.apply(thisBinding, args); return result && objectTypes[typeof result] ? result : thisBinding } return func.apply(thisBinding, args); } return bound; } /** * Creates compiled iteration functions. The iteration function will be created * to iterate over only objects if the first argument of `options.args` is * "object" or `options.inLoop.array` is falsey. * * @private * @param {Object} [options1, options2, ...] The compile options objects. * * useHas - A boolean to specify whether or not to use `hasOwnProperty` checks * in the object loop. * * useStrict - A boolean to specify whether or not to include the ES5 * "use strict" directive. * * args - A string of comma separated arguments the iteration function will * accept. * * init - A string to specify the initial value of the `result` variable. * * exit - A string of code to use in place of the default exit-early check * of `if (!arguments[0]) return result`. * * top - A string of code to execute after the exit-early check but before * the iteration branches. * * beforeLoop - A string or object containing an "array" or "object" property * of code to execute before the array or object loops. * * inLoop - A string or object containing an "array" or "object" property * of code to execute in the array or object loops. * * bottom - A string of code to execute after the iteration branches but * before the `result` is returned. * * @returns {Function} Returns the compiled function. */ function createIterator() { var object, prop, value, index = -1, length = arguments.length; // merge options into a template data object var data = { 'bottom': '', 'exit': '', 'init': '', 'top': '', 'arrayBranch': { 'beforeLoop': '' }, 'objectBranch': { 'beforeLoop': '' } }; while (++index < length) { object = arguments[index]; for (prop in object) { value = (value = object[prop]) == null ? '' : value; // keep this regexp explicit for the build pre-process if (/beforeLoop|inLoop/.test(prop)) { if (typeof value == 'string') { value = { 'array': value, 'object': value }; } data.arrayBranch[prop] = value.array || ''; data.objectBranch[prop] = value.object || ''; } else { data[prop] = value; } } } // set additional template `data` values var args = data.args, firstArg = /^[^,]+/.exec(args)[0], useStrict = data.useStrict; data.firstArg = firstArg; data.hasDontEnumBug = hasDontEnumBug; data.isKeysFast = isKeysFast; data.noArgsEnum = noArgsEnum; data.shadowed = shadowed; data.useHas = data.useHas !== false; data.useStrict = useStrict == null ? isStrictFast : useStrict; if (data.noCharByIndex == null) { data.noCharByIndex = noCharByIndex; } if (!data.exit) { data.exit = 'if (!' + firstArg + ') return result'; } if (firstArg != 'collection' || !data.arrayBranch.inLoop) { data.arrayBranch = null; } // create the function factory var factory = Function( 'arrayLikeClasses, ArrayProto, bind, bindIterator, compareAscending, concat, ' + 'forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' + 'isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, ' + 'slice, stringClass, toString', 'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + 'return callee' ); // return the compiled function return factory( arrayLikeClasses, ArrayProto, bind, bindIterator, compareAscending, concat, forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, slice, stringClass, toString ); } /** * Used by `template` to escape characters for inclusion in compiled * string literals. * * @private * @param {String} match The matched character to escape. * @returns {String} Returns the escaped character. */ function escapeStringChar(match) { return '\\' + stringEscapes[match]; } /** * Used by `escape` to convert characters to HTML entities. * * @private * @param {String} match The matched character to escape. * @returns {String} Returns the escaped character. */ function escapeHtmlChar(match) { return htmlEscapes[match]; } /** * A no-operation function. * * @private */ function noop() { // no operation performed } /** * Used by `unescape` to convert HTML entities to characters. * * @private * @param {String} match The matched character to unescape. * @returns {String} Returns the unescaped character. */ function unescapeHtmlChar(match) { return htmlUnescapes[match]; } /*--------------------------------------------------------------------------*/ /** * Checks if `value` is an `arguments` object. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. * @example * * (function() { return _.isArguments(arguments); })(1, 2, 3); * // => true * * _.isArguments([1, 2, 3]); * // => false */ function isArguments(value) { return toString.call(value) == argsClass; } // fallback for browsers that can't detect `arguments` objects by [[Class]] if (noArgsClass) { isArguments = function(value) { return !!(value && hasOwnProperty.call(value, 'callee')); }; } /** * Checks if `value` is an array. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. * @example * * (function() { return _.isArray(arguments); })(); * // => false * * _.isArray([1, 2, 3]); * // => true */ var isArray = nativeIsArray || function(value) { return toString.call(value) == arrayClass; }; /** * Checks if `value` is a function. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. * @example * * _.isFunction(''.concat); * // => true */ function isFunction(value) { return typeof value == 'function'; } // fallback for older versions of Chrome and Safari if (isFunction(/x/)) { isFunction = function(value) { return toString.call(value) == funcClass; }; } /** * A fallback implementation of `isPlainObject` that checks if a given `value` * is an object created by the `Object` constructor, assuming objects created * by the `Object` constructor have no inherited enumerable properties and that * there are no `Object.prototype` extensions. * * @private * @param {Mixed} value The value to check. * @param {Boolean} [skipArgsCheck=false] Internally used to skip checks for * `arguments` objects. * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. */ function isPlainFallback(value, skipArgsCheck) { // avoid non-objects and false positives for `arguments` objects var result = false; if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) { return result; } // IE < 9 presents DOM nodes as `Object` objects except they have `toString` // methods that are `typeof` "string" and still can coerce nodes to strings. // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) var ctor = value.constructor; if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && (!isFunction(ctor) || ctor instanceof ctor)) { // IE < 9 iterates inherited properties before own properties. If the first // iterated property is an object's own property then there are no inherited // enumerable properties. if (iteratesOwnLast) { forIn(value, function(value, key, object) { result = !hasOwnProperty.call(object, key); return false; }); return result === false; } // In most environments an object's own properties are iterated before // its inherited properties. If the last iterated property is an object's // own property then there are no inherited enumerable properties. forIn(value, function(value, key) { result = key; }); return result === false || hasOwnProperty.call(value, result); } return result; } /** * Checks if a given `value` is an object created by the `Object` constructor. * * @private * @param {Mixed} value The value to check. * @param {Boolean} [skipArgsCheck=false] Internally used to skip checks for * `arguments` objects. * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. */ var isPlainObject = objectTypes.__proto__ != ObjectProto ? isPlainFallback : function(value, skipArgsCheck) { if (!value) { return false; } var valueOf = value.valueOf, objProto = typeof valueOf == 'function' && (objProto = valueOf.__proto__) && objProto.__proto__; return objProto ? value == objProto || (value.__proto__ == objProto && (skipArgsCheck || !isArguments(value))) : isPlainFallback(value); }; /** * A shim implementation of `Object.keys` that produces an array of the given * object's own enumerable property names. * * @private * @param {Object} object The object to inspect. * @returns {Array} Returns a new array of property names. */ var shimKeys = createIterator({ 'args': 'object', 'init': '[]', 'inLoop': 'result.push(index)' }); /*--------------------------------------------------------------------------*/ /** * Creates a clone of `value`. If `deep` is `true`, all nested objects will * also be cloned otherwise they will be assigned by reference. Functions, DOM * nodes, `arguments` objects, and objects created by constructors other than * `Object` are **not** cloned. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to clone. * @param {Boolean} deep A flag to indicate a deep clone. * @param {Object} [guard] Internally used to allow this method to work with * others like `_.map` without using their callback `index` argument for `deep`. * @param {Array} [stackA=[]] Internally used to track traversed source objects. * @param {Array} [stackB=[]] Internally used to associate clones with their * source counterparts. * @returns {Mixed} Returns the cloned `value`. * @example * * var stooges = [ * { 'name': 'moe', 'age': 40 }, * { 'name': 'larry', 'age': 50 }, * { 'name': 'curly', 'age': 60 } * ]; * * _.clone({ 'name': 'moe' }); * // => { 'name': 'moe' } * * var shallow = _.clone(stooges); * shallow[0] === stooges[0]; * // => true * * var deep = _.clone(stooges, true); * shallow[0] === stooges[0]; * // => false */ function clone(value, deep, guard, stackA, stackB) { if (value == null) { return value; } if (guard) { deep = false; } // inspect [[Class]] var isObj = objectTypes[typeof value]; if (isObj) { // don't clone `arguments` objects, functions, or non-object Objects var className = toString.call(value); if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) { return value; } var isArr = className == arrayClass; isObj = isArr || (className == objectClass ? isPlainObject(value, true) : isObj); } // shallow clone if (!isObj || !deep) { // don't clone functions return isObj ? (isArr ? slice.call(value) : extend({}, value)) : value; } var ctor = value.constructor; switch (className) { case boolClass: return new ctor(value == true); case dateClass: return new ctor(+value); case numberClass: case stringClass: return new ctor(value); case regexpClass: return ctor(value.source, reFlags.exec(value)); } // check for circular references and return corresponding clone stackA || (stackA = []); stackB || (stackB = []); var length = stackA.length; while (length--) { if (stackA[length] == value) { return stackB[length]; } } // init cloned object var result = isArr ? ctor(length = value.length) : {}; // add the source value to the stack of traversed objects // and associate it with its clone stackA.push(value); stackB.push(result); // recursively populate clone (susceptible to call stack limits) if (isArr) { var index = -1; while (++index < length) { result[index] = clone(value[index], deep, null, stackA, stackB); } } else { forOwn(value, function(objValue, key) { result[key] = clone(objValue, deep, null, stackA, stackB); }); } return result; } /** * Assigns enumerable properties of the default object(s) to the `destination` * object for all `destination` properties that resolve to `null`/`undefined`. * Once a property is set, additional defaults of the same property will be * ignored. * * @static * @memberOf _ * @category Objects * @param {Object} object The destination object. * @param {Object} [default1, default2, ...] The default objects. * @returns {Object} Returns the destination object. * @example * * var iceCream = { 'flavor': 'chocolate' }; * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } */ var defaults = createIterator(extendIteratorOptions, { 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop }); /** * Assigns enumerable properties of the source object(s) to the `destination` * object. Subsequent sources will overwrite propery assignments of previous * sources. * * @static * @memberOf _ * @category Objects * @param {Object} object The destination object. * @param {Object} [source1, source2, ...] The source objects. * @returns {Object} Returns the destination object. * @example * * _.extend({ 'name': 'moe' }, { 'age': 40 }); * // => { 'name': 'moe', 'age': 40 } */ var extend = createIterator(extendIteratorOptions); /** * Iterates over `object`'s own and inherited enumerable properties, executing * the `callback` for each property. The `callback` is bound to `thisArg` and * invoked with three arguments; (value, key, object). Callbacks may exit iteration * early by explicitly returning `false`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to iterate over. * @param {Function} callback The function called per iteration. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Object} Returns `object`. * @example * * function Dog(name) { * this.name = name; * } * * Dog.prototype.bark = function() { * alert('Woof, woof!'); * }; * * _.forIn(new Dog('Dagny'), function(value, key) { * alert(key); * }); * // => alerts 'name' and 'bark' (order is not guaranteed) */ var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { 'useHas': false }); /** * Iterates over `object`'s own enumerable properties, executing the `callback` * for each property. The `callback` is bound to `thisArg` and invoked with three * arguments; (value, key, object). Callbacks may exit iteration early by explicitly * returning `false`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to iterate over. * @param {Function} callback The function called per iteration. * @param {Mixed} [thisArg] The `this` binding for the callback. * @returns {Object} Returns `object`. * @example * * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { * alert(key); * }); * // => alerts '0', '1', and 'length' (order is not guaranteed) */ var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); /** * Creates a sorted array of all enumerable properties, own and inherited, * of `object` that have function values. * * @static * @memberOf _ * @alias methods * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns a new array of property names that have function values. * @example * * _.functions(_); * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] */ var functions = createIterator({ 'useHas': false, 'args': 'object', 'init': '[]', 'inLoop': 'if (isFunction(value)) result.push(index)', 'bottom': 'result.sort()' }); /** * Checks if the specified object `property` exists and is a direct property, * instead of an inherited property. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to check. * @param {String} property The property to check for. * @returns {Boolean} Returns `true` if key is a direct property, else `false`. * @example * * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); * // => true */ function has(object, property) { return object ? hasOwnProperty.call(object, property) : false; } /** * Creates an object composed of the inverted keys and values of the given `object`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to invert. * @returns {Object} Returns the created inverted object. * @example * * _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed) */ var invert = createIterator({ 'args': 'object', 'init': '{}', 'inLoop': 'result[value] = index' }); /** * Checks if `value` is a boolean (`true` or `false`) value. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. * @example * * _.isBoolean(null); * // => false */ function isBoolean(value) { return value === true || value === false || toString.call(value) == boolClass; } /** * Checks if `value` is a date. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. * @example * * _.isDate(new Date); * // => true */ function isDate(value) { return toString.call(value) == dateClass; } /** * Checks if `value` is a DOM element. * * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. * @example * * _.isElement(document.body); * // => true */ function isElement(value) { return value ? value.nodeType === 1 : false; } /** * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a * length of `0` and objects with no own enumerable properties are considered * "empty". * * @static * @memberOf _ * @category Objects * @param {Array|Object|String} value The value to inspect. * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. * @example * * _.isEmpty([1, 2, 3]); * // => false * * _.isEmpty({}); * // => true * * _.isEmpty(''); * // => true */ var isEmpty = createIterator({ 'args': 'value', 'init': 'true', 'top': 'var className = toString.call(value),\n' + ' length = value.length;\n' + 'if (arrayLikeClasses[className]' + (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + ' (className == objectClass && length === +length &&\n' + ' isFunction(value.splice))' + ') return !length', 'inLoop': { 'object': 'return false' } }); /** * Performs a deep comparison between two values to determine if they are * equivalent to each other. * * @static * @memberOf _ * @category Objects * @param {Mixed} a The value to compare. * @param {Mixed} b The other value to compare. * @param {Object} [stackA=[]] Internally used track traversed `a` objects. * @param {Object} [stackB=[]] Internally used track traversed `b` objects. * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. * @example * * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; * * moe == clone; * // => false * * _.isEqual(moe, clone); * // => true */ function isEqual(a, b, stackA, stackB) { // a strict comparison is necessary because `null == undefined` if (a == null || b == null) { return a === b; } // exit early for identical values if (a === b) { // treat `+0` vs. `-0` as not equal return a !== 0 || (1 / a == 1 / b); } // unwrap any LoDash wrapped values if (objectTypes[typeof a] || objectTypes[typeof b]) { a = a.__wrapped__ || a; b = b.__wrapped__ || b; } // compare [[Class]] names var className = toString.call(a); if (className != toString.call(b)) { return false; } switch (className) { case boolClass: case dateClass: // coerce dates and booleans to numbers, dates to milliseconds and booleans // to `1` or `0`, treating invalid dates coerced to `NaN` as not equal return +a == +b; case numberClass: // treat `NaN` vs. `NaN` as equal return a != +a ? b != +b // but treat `+0` vs. `-0` as not equal : (a == 0 ? (1 / a == 1 / b) : a == +b); case regexpClass: case stringClass: // coerce regexes to strings (http://es5.github.com/#x15.10.6.4) // treat string primitives and their corresponding object instances as equal return a == b + ''; } // exit early, in older browsers, if `a` is array-like but not `b` var isArr = arrayLikeClasses[className]; if (noArgsClass && !isArr && (isArr = isArguments(a)) && !isArguments(b)) { return false; } // exit for functions and DOM nodes if (!isArr && (className != objectClass || (noNodeClass && ( (typeof a.toString != 'function' && typeof (a + '') == 'string') || (typeof b.toString != 'function' && typeof (b + '') == 'string'))))) { return false; } // assume cyclic structures are equal // the algorithm for detecting cyclic structures is adapted from ES 5.1 // section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3) stackA || (stackA = []); stackB || (stackB = []); var length = stackA.length; while (length--) { if (stackA[length] == a) { return stackB[length] == b; } } var index = -1, result = true, size = 0; // add `a` and `b` to the stack of traversed objects stackA.push(a); stackB.push(b); // recursively compare objects and arrays (susceptible to call stack limits) if (isArr) { // compare lengths to determine if a deep comparison is necessary size = a.length; result = size == b.length; if (result) { // deep compare the contents, ignoring non-numeric properties while (size--) { if (!(result = isEqual(a[size], b[size], stackA, stackB))) { break; } } } return result; } var ctorA = a.constructor, ctorB = b.constructor; // non `Object` object instances with different constructors are not equal if (ctorA != ctorB && !( isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB )) { return false; } // deep compare objects for (var prop in a) { if (hasOwnProperty.call(a, prop)) { // count the number of properties. size++; // deep compare each property value. if (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) { return false; } } } // ensure both objects have the same number of properties for (prop in b) { // The JS engine in Adobe products, like InDesign, has a bug that causes // `!size--` to throw an error so it must be wrapped in parentheses. // https://github.com/documentcloud/underscore/issues/355 if (hasOwnProperty.call(b, prop) && !(size--)) { // `size` will be `-1` if `b` has more properties than `a` return false; } } // handle JScript [[DontEnum]] bug if (hasDontEnumBug) { while (++index < 7) { prop = shadowed[index]; if (hasOwnProperty.call(a, prop) && !(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stackA, stackB))) { return false; } } } return true; } /** * Checks if `value` is a finite number. * * Note: This is not the same as native `isFinite`, which will return true for * booleans and other values. See http://es5.github.com/#x15.1.2.5. * * @deprecated * @static * @memberOf _ * @category Objects * @param {Mixed} value The value to check. * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. * @example * * _.isFinite(-101); * // => true * * _.isFinite('10'); * // => false * * _.isFinite(Infinity); * // => false */ function isFinite(value) { return nativeIsFinite(value) && toString.call(value) == numberClass;