lodash-2.4.1/0000755000175000017500000000000012247303154011133 5ustar ovdovdlodash-2.4.1/CONTRIBUTING.md0000644000175000017500000000426512247303154013373 0ustar ovdovd# Contributing to Lo-Dash If you’d like to contribute a feature or bug fix, you can [fork](https://help.github.com/articles/fork-a-repo) Lo-Dash, commit your changes, and [send a pull request](https://help.github.com/articles/using-pull-requests). Please make sure to [search the issue tracker](https://github.com/lodash/lodash/issues) first; your issue may have already been discussed or fixed in `master`. ## Tests Include updated unit tests in the `test` directory as part of your pull request. You can run the tests from the command line via `node test/test`, or open `test/index.html` in a web browser. The `test/run-test.sh` script attempts to run the tests in [Rhino](https://developer.mozilla.org/en-US/docs/Rhino), [Narwhal](https://github.com/280north/narwhal), [RingoJS](http://ringojs.org/), [PhantomJS](http://phantomjs.org/), and [Node](http://nodejs.org/), before running them in your default browser. The [Backbone](http://backbonejs.org/) and [Underscore](http://http://underscorejs.org/) test suites are included as well. ## Contributor License Agreement Lo-Dash is a member of the [Dojo Foundation](http://dojofoundation.org/). As such, we request that all contributors sign the Dojo Foundation [contributor license agreement](http://dojofoundation.org/about/claForm). For more information about CLAs, please check out Alex Russell’s excellent post, [“Why Do I Need to Sign This?”](http://infrequently.org/2008/06/why-do-i-need-to-sign-this/). ## Coding Guidelines In addition to the following guidelines, please follow the conventions already established in the code. - **Spacing**:
Use two spaces for indentation. No tabs. - **Naming**:
Keep variable and method names concise and descriptive.
Variable names `index`, `collection`, and `callback` are preferable to `i`, `arr`, and `fn`. - **Quotes**:
Single-quoted strings are preferred to double-quoted strings; however, please use a double-quoted string if the value contains a single-quote character to avoid unnecessary escaping. - **Comments**:
Please use single-line comments to annotate significant additions, and [JSDoc-style](http://www.2ality.com/2011/08/jsdoc-intro.html) comments for new methods. lodash-2.4.1/doc/0000755000175000017500000000000012247303154011700 5ustar ovdovdlodash-2.4.1/doc/parse.php0000644000175000017500000000214112247303154013521 0ustar ovdovdversion; // generate Markdown $markdown = docdown(array( 'path' => '../' . $filePath, 'title' => 'Lo-Dash v' . $version . '', 'toc' => 'categories', 'url' => 'https://github.com/lodash/lodash/blob/master/lodash.js' )); // save to a `.md` file file_put_contents($outputName . '.md', $markdown); // print header('Content-Type: text/plain;charset=utf-8'); echo $markdown . PHP_EOL; ?>lodash-2.4.1/doc/README.md0000644000175000017500000044705512247303154013176 0ustar ovdovd# Lo-Dash v2.4.1 ## `Arrays` * `_.compact` * `_.difference` * `_.drop` -> `rest` * `_.findIndex` * `_.findLastIndex` * `_.first` * `_.flatten` * `_.head` -> `first` * `_.indexOf` * `_.initial` * `_.intersection` * `_.last` * `_.lastIndexOf` * `_.object` -> `zipObject` * `_.pull` * `_.range` * `_.remove` * `_.rest` * `_.sortedIndex` * `_.tail` -> `rest` * `_.take` -> `first` * `_.union` * `_.uniq` * `_.unique` -> `uniq` * `_.unzip` -> `zip` * `_.without` * `_.xor` * `_.zip` * `_.zipObject` ## `Chaining` * `_` * `_.chain` * `_.tap` * `_.prototype.chain` * `_.prototype.toString` * `_.prototype.value` -> `valueOf` * `_.prototype.valueOf` ## `Collections` * `_.all` -> `every` * `_.any` -> `some` * `_.at` * `_.collect` -> `map` * `_.contains` * `_.countBy` * `_.detect` -> `find` * `_.each` -> `forEach` * `_.eachRight` -> `forEachRight` * `_.every` * `_.filter` * `_.find` * `_.findLast` * `_.findWhere` -> `find` * `_.foldl` -> `reduce` * `_.foldr` -> `reduceRight` * `_.forEach` * `_.forEachRight` * `_.groupBy` * `_.include` -> `contains` * `_.indexBy` * `_.inject` -> `reduce` * `_.invoke` * `_.map` * `_.max` * `_.min` * `_.pluck` * `_.reduce` * `_.reduceRight` * `_.reject` * `_.sample` * `_.select` -> `filter` * `_.shuffle` * `_.size` * `_.some` * `_.sortBy` * `_.toArray` * `_.where` ## `Functions` * `_.after` * `_.bind` * `_.bindAll` * `_.bindKey` * `_.compose` * `_.curry` * `_.debounce` * `_.defer` * `_.delay` * `_.memoize` * `_.once` * `_.partial` * `_.partialRight` * `_.throttle` * `_.wrap` ## `Objects` * `_.assign` * `_.clone` * `_.cloneDeep` * `_.create` * `_.defaults` * `_.extend` -> `assign` * `_.findKey` * `_.findLastKey` * `_.forIn` * `_.forInRight` * `_.forOwn` * `_.forOwnRight` * `_.functions` * `_.has` * `_.invert` * `_.isArguments` * `_.isArray` * `_.isBoolean` * `_.isDate` * `_.isElement` * `_.isEmpty` * `_.isEqual` * `_.isFinite` * `_.isFunction` * `_.isNaN` * `_.isNull` * `_.isNumber` * `_.isObject` * `_.isPlainObject` * `_.isRegExp` * `_.isString` * `_.isUndefined` * `_.keys` * `_.mapValues` * `_.merge` * `_.methods` -> `functions` * `_.omit` * `_.pairs` * `_.pick` * `_.transform` * `_.values` ## `Utilities` * `_.now` * `_.constant` * `_.createCallback` * `_.escape` * `_.identity` * `_.mixin` * `_.noConflict` * `_.noop` * `_.parseInt` * `_.property` * `_.random` * `_.result` * `_.runInContext` * `_.template` * `_.times` * `_.unescape` * `_.uniqueId` ## `Methods` * `_.templateSettings.imports._` ## `Properties` * `_.VERSION` * `_.support` * `_.support.argsClass` * `_.support.argsObject` * `_.support.enumErrorProps` * `_.support.enumPrototypes` * `_.support.funcDecomp` * `_.support.funcNames` * `_.support.nonEnumArgs` * `_.support.nonEnumShadows` * `_.support.ownLast` * `_.support.spliceObjects` * `_.support.unindexedChars` * `_.templateSettings` * `_.templateSettings.escape` * `_.templateSettings.evaluate` * `_.templateSettings.interpolate` * `_.templateSettings.variable` * `_.templateSettings.imports` ## `“Arrays” Methods` ### `_.compact(array)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4479 "View in source") [Ⓣ][1] Creates an array with all falsey values removed. The values `false`, `null`, `0`, `""`, `undefined`, and `NaN` are all falsey. #### Arguments 1. `array` *(Array)*: The array to compact. #### Returns *(Array)*: Returns a new array of filtered values. #### Example ```js _.compact([0, 1, false, 2, '', 3]); // => [1, 2, 3] ``` * * * ### `_.difference(array, [values])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4508 "View in source") [Ⓣ][1] Creates an array excluding all values of the provided arrays using strict equality for comparisons, i.e. `===`. #### Arguments 1. `array` *(Array)*: The array to process. 2. `[values]` *(...Array)*: The arrays of values to exclude. #### Returns *(Array)*: Returns a new array of filtered values. #### Example ```js _.difference([1, 2, 3, 4, 5], [5, 2, 10]); // => [1, 3, 4] ``` * * * ### `_.findIndex(array, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4553 "View in source") [Ⓣ][1] This method is like `_.find` except that it returns the index of the first element that passes the callback check, instead of the element itself. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to search. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(number)*: Returns the index of the found element, else `-1`. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36, 'blocked': false }, { 'name': 'fred', 'age': 40, 'blocked': true }, { 'name': 'pebbles', 'age': 1, 'blocked': false } ]; _.findIndex(characters, function(chr) { return chr.age < 20; }); // => 2 // using "_.where" callback shorthand _.findIndex(characters, { 'age': 36 }); // => 0 // using "_.pluck" callback shorthand _.findIndex(characters, 'blocked'); // => 1 ``` * * * ### `_.findLastIndex(array, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4607 "View in source") [Ⓣ][1] This method is like `_.findIndex` except that it iterates over elements of a `collection` from right to left. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to search. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(number)*: Returns the index of the found element, else `-1`. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36, 'blocked': true }, { 'name': 'fred', 'age': 40, 'blocked': false }, { 'name': 'pebbles', 'age': 1, 'blocked': true } ]; _.findLastIndex(characters, function(chr) { return chr.age > 30; }); // => 1 // using "_.where" callback shorthand _.findLastIndex(characters, { 'age': 36 }); // => 0 // using "_.pluck" callback shorthand _.findLastIndex(characters, 'blocked'); // => 2 ``` * * * ### `_.first(array, [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4669 "View in source") [Ⓣ][1] Gets the first element or first `n` elements of an array. If a callback is provided elements at the beginning of the array are returned as long as the callback returns truey. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.head, _.take* #### Arguments 1. `array` *(Array)*: The array to query. 2. `[callback]` *(Function|Object|number|string)*: The function called per element or the number of elements to return. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the first element(s) of `array`. #### Example ```js _.first([1, 2, 3]); // => 1 _.first([1, 2, 3], 2); // => [1, 2] _.first([1, 2, 3], function(num) { return num < 3; }); // => [1, 2] var characters = [ { 'name': 'barney', 'blocked': true, 'employer': 'slate' }, { 'name': 'fred', 'blocked': false, 'employer': 'slate' }, { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } ]; // using "_.pluck" callback shorthand _.first(characters, 'blocked'); // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }] // using "_.where" callback shorthand _.pluck(_.first(characters, { 'employer': 'slate' }), 'name'); // => ['barney', 'fred'] ``` * * * ### `_.flatten(array, [isShallow=false], [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4729 "View in source") [Ⓣ][1] Flattens a nested array *(the nesting can be to any depth)*. If `isShallow` is truey, the array will only be flattened a single level. If a callback is provided each element of the array is passed through the callback before flattening. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to flatten. 2. `[isShallow=false]` *(boolean)*: A flag to restrict flattening to a single level. 3. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a new flattened array. #### Example ```js _.flatten([1, [2], [3, [[4]]]]); // => [1, 2, 3, 4]; _.flatten([1, [2], [3, [[4]]]], true); // => [1, 2, 3, [[4]]]; var characters = [ { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] }, { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } ]; // using "_.pluck" callback shorthand _.flatten(characters, 'pets'); // => ['hoppy', 'baby puss', 'dino'] ``` * * * ### `_.indexOf(array, value, [fromIndex=0])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4766 "View in source") [Ⓣ][1] Gets the index at which the first occurrence of `value` is found using strict equality for comparisons, i.e. `===`. If the array is already sorted providing `true` for `fromIndex` will run a faster binary search. #### Arguments 1. `array` *(Array)*: The array to search. 2. `value` *(*)*: The value to search for. 3. `[fromIndex=0]` *(boolean|number)*: The index to search from or `true` to perform a binary search on a sorted array. #### Returns *(number)*: Returns the index of the matched value or `-1`. #### Example ```js _.indexOf([1, 2, 3, 1, 2, 3], 2); // => 1 _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); // => 4 _.indexOf([1, 1, 2, 2, 3, 3], 2, true); // => 2 ``` * * * ### `_.initial(array, [callback=1], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4827 "View in source") [Ⓣ][1] Gets all but the last element or last `n` elements of an array. If a callback is provided elements at the end of the array are excluded from the result as long as the callback returns truey. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to query. 2. `[callback=1]` *(Function|Object|number|string)*: The function called per element or the number of elements to exclude. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a slice of `array`. #### Example ```js _.initial([1, 2, 3]); // => [1, 2] _.initial([1, 2, 3], 2); // => [1] _.initial([1, 2, 3], function(num) { return num > 1; }); // => [1] var characters = [ { 'name': 'barney', 'blocked': false, 'employer': 'slate' }, { 'name': 'fred', 'blocked': true, 'employer': 'slate' }, { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } ]; // using "_.pluck" callback shorthand _.initial(characters, 'blocked'); // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }] // using "_.where" callback shorthand _.pluck(_.initial(characters, { 'employer': 'na' }), 'name'); // => ['barney', 'fred'] ``` * * * ### `_.intersection([array])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4857 "View in source") [Ⓣ][1] Creates an array of unique values present in all provided arrays using strict equality for comparisons, i.e. `===`. #### Arguments 1. `[array]` *(...Array)*: The arrays to inspect. #### Returns *(Array)*: Returns an array of shared values. #### Example ```js _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]); // => [1, 2] ``` * * * ### `_.last(array, [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4957 "View in source") [Ⓣ][1] Gets the last element or last `n` elements of an array. If a callback is provided elements at the end of the array are returned as long as the callback returns truey. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to query. 2. `[callback]` *(Function|Object|number|string)*: The function called per element or the number of elements to return. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the last element(s) of `array`. #### Example ```js _.last([1, 2, 3]); // => 3 _.last([1, 2, 3], 2); // => [2, 3] _.last([1, 2, 3], function(num) { return num > 1; }); // => [2, 3] var characters = [ { 'name': 'barney', 'blocked': false, 'employer': 'slate' }, { 'name': 'fred', 'blocked': true, 'employer': 'slate' }, { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } ]; // using "_.pluck" callback shorthand _.pluck(_.last(characters, 'blocked'), 'name'); // => ['fred', 'pebbles'] // using "_.where" callback shorthand _.last(characters, { 'employer': 'na' }); // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }] ``` * * * ### `_.lastIndexOf(array, value, [fromIndex=array.length-1])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5003 "View in source") [Ⓣ][1] Gets the index at which the last occurrence of `value` is found using strict equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the offset from the end of the collection. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to search. 2. `value` *(*)*: The value to search for. 3. `[fromIndex=array.length-1]` *(number)*: The index to search from. #### Returns *(number)*: Returns the index of the matched value or `-1`. #### Example ```js _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); // => 4 _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); // => 1 ``` * * * ### `_.pull(array, [value])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5033 "View in source") [Ⓣ][1] Removes all provided values from the given array using strict equality for comparisons, i.e. `===`. #### Arguments 1. `array` *(Array)*: The array to modify. 2. `[value]` *(...*)*: The values to remove. #### Returns *(Array)*: Returns `array`. #### Example ```js var array = [1, 2, 3, 1, 2, 3]; _.pull(array, 2, 3); console.log(array); // => [1, 1] ``` * * * ### `_.range([start=0], end, [step=1])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5084 "View in source") [Ⓣ][1] Creates an array of numbers *(positive and/or negative)* progressing from `start` up to but not including `end`. If `start` is less than `stop` a zero-length range is created unless a negative `step` is specified. #### Arguments 1. `[start=0]` *(number)*: The start of the range. 2. `end` *(number)*: The end of the range. 3. `[step=1]` *(number)*: The value to increment or decrement by. #### Returns *(Array)*: Returns a new range array. #### Example ```js _.range(4); // => [0, 1, 2, 3] _.range(1, 5); // => [1, 2, 3, 4] _.range(0, 20, 5); // => [0, 5, 10, 15] _.range(0, -4, -1); // => [0, -1, -2, -3] _.range(1, 4, 0); // => [1, 1, 1] _.range(0); // => [] ``` * * * ### `_.remove(array, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5137 "View in source") [Ⓣ][1] Removes all elements from an array that the callback returns truey for and returns an array of removed elements. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to modify. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a new array of removed elements. #### Example ```js var array = [1, 2, 3, 4, 5, 6]; var evens = _.remove(array, function(num) { return num % 2 == 0; }); console.log(array); // => [1, 3, 5] console.log(evens); // => [2, 4, 6] ``` * * * ### `_.rest(array, [callback=1], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5206 "View in source") [Ⓣ][1] The opposite of `_.initial` this method gets all but the first element or first `n` elements of an array. If a callback function is provided elements at the beginning of the array are excluded from the result as long as the callback returns truey. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.drop, _.tail* #### Arguments 1. `array` *(Array)*: The array to query. 2. `[callback=1]` *(Function|Object|number|string)*: The function called per element or the number of elements to exclude. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a slice of `array`. #### Example ```js _.rest([1, 2, 3]); // => [2, 3] _.rest([1, 2, 3], 2); // => [3] _.rest([1, 2, 3], function(num) { return num < 3; }); // => [3] var characters = [ { 'name': 'barney', 'blocked': true, 'employer': 'slate' }, { 'name': 'fred', 'blocked': false, 'employer': 'slate' }, { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } ]; // using "_.pluck" callback shorthand _.pluck(_.rest(characters, 'blocked'), 'name'); // => ['fred', 'pebbles'] // using "_.where" callback shorthand _.rest(characters, { 'employer': 'slate' }); // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }] ``` * * * ### `_.sortedIndex(array, value, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5270 "View in source") [Ⓣ][1] Uses a binary search to determine the smallest index at which a value should be inserted into a given sorted array in order to maintain the sort order of the array. If a callback is provided it will be executed for `value` and each element of `array` to compute their sort ranking. The callback is bound to `thisArg` and invoked with one argument; *(value)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `array` *(Array)*: The array to inspect. 2. `value` *(*)*: The value to evaluate. 3. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(number)*: Returns the index at which `value` should be inserted into `array`. #### Example ```js _.sortedIndex([20, 30, 50], 40); // => 2 // using "_.pluck" callback shorthand _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); // => 2 var dict = { 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 } }; _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { return dict.wordToNumber[word]; }); // => 2 _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { return this.wordToNumber[word]; }, dict); // => 2 ``` * * * ### `_.union([array])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5301 "View in source") [Ⓣ][1] Creates an array of unique values, in order, of the provided arrays using strict equality for comparisons, i.e. `===`. #### Arguments 1. `[array]` *(...Array)*: The arrays to inspect. #### Returns *(Array)*: Returns an array of combined values. #### Example ```js _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]); // => [1, 2, 3, 5, 4] ``` * * * ### `_.uniq(array, [isSorted=false], [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5349 "View in source") [Ⓣ][1] Creates a duplicate-value-free version of an array using strict equality for comparisons, i.e. `===`. If the array is sorted, providing `true` for `isSorted` will use a faster algorithm. If a callback is provided each element of `array` is passed through the callback before uniqueness is computed. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, array)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.unique* #### Arguments 1. `array` *(Array)*: The array to process. 2. `[isSorted=false]` *(boolean)*: A flag to indicate that `array` is sorted. 3. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a duplicate-value-free array. #### Example ```js _.uniq([1, 2, 1, 3, 1]); // => [1, 2, 3] _.uniq([1, 1, 2, 2, 3], true); // => [1, 2, 3] _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); }); // => ['A', 'b', 'C'] _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math); // => [1, 2.5, 3] // using "_.pluck" callback shorthand _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); // => [{ 'x': 1 }, { 'x': 2 }] ``` * * * ### `_.without(array, [value])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5377 "View in source") [Ⓣ][1] Creates an array excluding all provided values using strict equality for comparisons, i.e. `===`. #### Arguments 1. `array` *(Array)*: The array to filter. 2. `[value]` *(...*)*: The values to exclude. #### Returns *(Array)*: Returns a new array of filtered values. #### Example ```js _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); // => [2, 3, 4] ``` * * * ### `_.xor([array])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5398 "View in source") [Ⓣ][1] Creates an array that is the symmetric difference of the provided arrays. See http://en.wikipedia.org/wiki/Symmetric_difference. #### Arguments 1. `[array]` *(...Array)*: The arrays to inspect. #### Returns *(Array)*: Returns an array of values. #### Example ```js _.xor([1, 2, 3], [5, 2, 1, 4]); // => [3, 5, 4] _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]); // => [1, 4, 5] ``` * * * ### `_.zip([array])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5429 "View in source") [Ⓣ][1] Creates an array of grouped elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on. #### Aliases *_.unzip* #### Arguments 1. `[array]` *(...Array)*: Arrays to process. #### Returns *(Array)*: Returns a new array of grouped elements. #### Example ```js _.zip(['fred', 'barney'], [30, 40], [true, false]); // => [['fred', 30, true], ['barney', 40, false]] ``` * * * ### `_.zipObject(keys, [values=[]])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5459 "View in source") [Ⓣ][1] Creates an object composed from arrays of `keys` and `values`. Provide either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]` or two arrays, one of `keys` and one of corresponding `values`. #### Aliases *_.object* #### Arguments 1. `keys` *(Array)*: The array of keys. 2. `[values=[]]` *(Array)*: The array of values. #### Returns *(Object)*: Returns an object composed of the given keys and corresponding values. #### Example ```js _.zipObject(['fred', 'barney'], [30, 40]); // => { 'fred': 30, 'barney': 40 } ``` * * * ## `“Chaining” Methods` ### `_(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L647 "View in source") [Ⓣ][1] Creates a `lodash` object which wraps the given value to enable intuitive method chaining. In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
`concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, and `unshift` Chaining is supported in custom builds as long as the `value` method is implicitly or explicitly included in the build. The chainable wrapper functions are:
`after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`, `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, and `zip` The non-chainable wrapper functions are:
`clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`, `template`, `unescape`, `uniqueId`, and `value` The wrapper functions `first` and `last` return wrapped values when `n` is provided, otherwise they return unwrapped values. Explicit chaining can be enabled by using the `_.chain` method. #### Arguments 1. `value` *(*)*: The value to wrap in a `lodash` instance. #### Returns *(Object)*: Returns a `lodash` instance. #### Example ```js var wrapped = _([1, 2, 3]); // returns an unwrapped value wrapped.reduce(function(sum, num) { return sum + num; }); // => 6 // returns a wrapped value var squares = wrapped.map(function(num) { return num * num; }); _.isArray(squares); // => false _.isArray(squares.value()); // => true ``` * * * ### `_.chain(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6788 "View in source") [Ⓣ][1] Creates a `lodash` object that wraps the given value with explicit method chaining enabled. #### Arguments 1. `value` *(*)*: The value to wrap. #### Returns *(Object)*: Returns the wrapper object. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }, { 'name': 'pebbles', 'age': 1 } ]; var youngest = _.chain(characters) .sortBy('age') .map(function(chr) { return chr.name + ' is ' + chr.age; }) .first() .value(); // => 'pebbles is 1' ``` * * * ### `_.tap(value, interceptor)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6814 "View in source") [Ⓣ][1] Invokes `interceptor` with the `value` as the first argument and then returns `value`. The purpose of this method is to "tap into" a method chain in order to perform operations on intermediate results within the chain. #### Arguments 1. `value` *(*)*: The value to provide to `interceptor`. 2. `interceptor` *(Function)*: The function to invoke. #### Returns *(*)*: Returns `value`. #### Example ```js _([1, 2, 3, 4]) .tap(function(array) { array.pop(); }) .reverse() .value(); // => [3, 2, 1] ``` * * * ### `_.prototype.chain()` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6844 "View in source") [Ⓣ][1] Enables explicit method chaining on the wrapper object. #### Returns *(*)*: Returns the wrapper object. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; // without explicit chaining _(characters).first(); // => { 'name': 'barney', 'age': 36 } // with explicit chaining _(characters).chain() .first() .pick('age') .value(); // => { 'age': 36 } ``` * * * ### `_.prototype.toString()` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6861 "View in source") [Ⓣ][1] Produces the `toString` result of the wrapped value. #### Returns *(string)*: Returns the string result. #### Example ```js _([1, 2, 3]).toString(); // => '1,2,3' ``` * * * ### `_.prototype.valueOf()` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6878 "View in source") [Ⓣ][1] Extracts the wrapped value. #### Aliases *_.prototype.value* #### Returns *(*)*: Returns the wrapped value. #### Example ```js _([1, 2, 3]).valueOf(); // => [1, 2, 3] ``` * * * ## `“Collections” Methods` ### `_.at(collection, [index])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3296 "View in source") [Ⓣ][1] Creates an array of elements from the specified indexes, or keys, of the `collection`. Indexes may be specified as individual arguments or as arrays of indexes. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[index]` *(...(number|number[]|string|string[])*: The indexes of `collection` to retrieve, specified as individual indexes or arrays of indexes. #### Returns *(Array)*: Returns a new array of elements corresponding to the provided indexes. #### Example ```js _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]); // => ['a', 'c', 'e'] _.at(['fred', 'barney', 'pebbles'], 0, 2); // => ['fred', 'pebbles'] ``` * * * ### `_.contains(collection, target, [fromIndex=0])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3339 "View in source") [Ⓣ][1] Checks if a given value is present in a collection using strict equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the offset from the end of the collection. #### Aliases *_.include* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `target` *(*)*: The value to check for. 3. `[fromIndex=0]` *(number)*: The index to search from. #### Returns *(boolean)*: Returns `true` if the `target` element is found, else `false`. #### Example ```js _.contains([1, 2, 3], 1); // => true _.contains([1, 2, 3], 1, 2); // => false _.contains({ 'name': 'fred', 'age': 40 }, 'fred'); // => true _.contains('pebbles', 'eb'); // => true ``` * * * ### `_.countBy(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3394 "View in source") [Ⓣ][1] Creates an object composed of keys generated from the results of running each element of `collection` through the callback. The corresponding value of each key is the number of times the key was returned by the callback. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns the composed aggregate object. #### Example ```js _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); // => { '4': 1, '6': 2 } _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); // => { '4': 1, '6': 2 } _.countBy(['one', 'two', 'three'], 'length'); // => { '3': 2, '5': 1 } ``` * * * ### `_.every(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3439 "View in source") [Ⓣ][1] Checks if the given callback returns truey value for **all** elements of a collection. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.all* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(boolean)*: Returns `true` if all elements passed the callback check, else `false`. #### Example ```js _.every([true, 1, null, 'yes']); // => false var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; // using "_.pluck" callback shorthand _.every(characters, 'age'); // => true // using "_.where" callback shorthand _.every(characters, { 'age': 36 }); // => false ``` * * * ### `_.filter(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3500 "View in source") [Ⓣ][1] Iterates over elements of a collection, returning an array of all elements the callback returns truey for. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.select* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a new array of elements that passed the callback check. #### Example ```js var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); // => [2, 4, 6] var characters = [ { 'name': 'barney', 'age': 36, 'blocked': false }, { 'name': 'fred', 'age': 40, 'blocked': true } ]; // using "_.pluck" callback shorthand _.filter(characters, 'blocked'); // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] // using "_.where" callback shorthand _.filter(characters, { 'age': 36 }); // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] ``` * * * ### `_.find(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3567 "View in source") [Ⓣ][1] Iterates over elements of a collection, returning the first element that the callback returns truey for. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.detect, _.findWhere* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the found element, else `undefined`. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36, 'blocked': false }, { 'name': 'fred', 'age': 40, 'blocked': true }, { 'name': 'pebbles', 'age': 1, 'blocked': false } ]; _.find(characters, function(chr) { return chr.age < 40; }); // => { 'name': 'barney', 'age': 36, 'blocked': false } // using "_.where" callback shorthand _.find(characters, { 'age': 1 }); // => { 'name': 'pebbles', 'age': 1, 'blocked': false } // using "_.pluck" callback shorthand _.find(characters, 'blocked'); // => { 'name': 'fred', 'age': 40, 'blocked': true } ``` * * * ### `_.findLast(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3612 "View in source") [Ⓣ][1] This method is like `_.find` except that it iterates over elements of a `collection` from right to left. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the found element, else `undefined`. #### Example ```js _.findLast([1, 2, 3, 4], function(num) { return num % 2 == 1; }); // => 3 ``` * * * ### `_.forEach(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3650 "View in source") [Ⓣ][1] Iterates over elements of a collection, executing the callback for each element. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. Callbacks may exit iteration early by explicitly returning `false`. Note: As with other "Collections" methods, objects with a `length` property are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` may be used for object iteration. #### Aliases *_.each* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array, Object, string)*: Returns `collection`. #### Example ```js _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); // => logs each number and returns '1,2,3' _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); // => logs each number and returns the object (property order is not guaranteed across environments) ``` * * * ### `_.forEachRight(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3683 "View in source") [Ⓣ][1] This method is like `_.forEach` except that it iterates over elements of a `collection` from right to left. #### Aliases *_.eachRight* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array, Object, string)*: Returns `collection`. #### Example ```js _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(','); // => logs each number from right to left and returns '3,2,1' ``` * * * ### `_.groupBy(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3744 "View in source") [Ⓣ][1] Creates an object composed of keys generated from the results of running each element of a collection through the callback. The corresponding value of each key is an array of the elements responsible for generating the key. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false` #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns the composed aggregate object. #### Example ```js _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); // => { '4': [4.2], '6': [6.1, 6.4] } _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); // => { '4': [4.2], '6': [6.1, 6.4] } // using "_.pluck" callback shorthand _.groupBy(['one', 'two', 'three'], 'length'); // => { '3': ['one', 'two'], '5': ['three'] } ``` * * * ### `_.indexBy(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3787 "View in source") [Ⓣ][1] Creates an object composed of keys generated from the results of running each element of the collection through the given callback. The corresponding value of each key is the last element responsible for generating the key. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns the composed aggregate object. #### Example ```js var keys = [ { 'dir': 'left', 'code': 97 }, { 'dir': 'right', 'code': 100 } ]; _.indexBy(keys, 'dir'); // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } _.indexBy(keys, function(key) { return String.fromCharCode(key.code); }); // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String); // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } ``` * * * ### `_.invoke(collection, methodName, [arg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3813 "View in source") [Ⓣ][1] Invokes the method named by `methodName` on each element in the `collection` returning an array of the results of each invoked method. Additional arguments will be provided to each invoked method. If `methodName` is a function it will be invoked for, and `this` bound to, each element in the `collection`. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `methodName` *(Function|string)*: The name of the method to invoke or the function invoked per iteration. 3. `[arg]` *(...*)*: Arguments to invoke the method with. #### Returns *(Array)*: Returns a new array of the results of each invoked method. #### Example ```js _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); // => [[1, 5, 7], [1, 2, 3]] _.invoke([123, 456], String.prototype.split, ''); // => [['1', '2', '3'], ['4', '5', '6']] ``` * * * ### `_.map(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3865 "View in source") [Ⓣ][1] Creates an array of values by running each element in the collection through the callback. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.collect* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a new array of the results of each `callback` execution. #### Example ```js _.map([1, 2, 3], function(num) { return num * 3; }); // => [3, 6, 9] _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); // => [3, 6, 9] (property order is not guaranteed across environments) var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; // using "_.pluck" callback shorthand _.map(characters, 'name'); // => ['barney', 'fred'] ``` * * * ### `_.max(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3923 "View in source") [Ⓣ][1] Retrieves the maximum value of a collection. If the collection is empty or falsey `-Infinity` is returned. If a callback is provided it will be executed for each value in the collection to generate the criterion by which the value is ranked. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the maximum value. #### Example ```js _.max([4, 2, 8, 6]); // => 8 var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; _.max(characters, function(chr) { return chr.age; }); // => { 'name': 'fred', 'age': 40 }; // using "_.pluck" callback shorthand _.max(characters, 'age'); // => { 'name': 'fred', 'age': 40 }; ``` * * * ### `_.min(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3998 "View in source") [Ⓣ][1] Retrieves the minimum value of a collection. If the collection is empty or falsey `Infinity` is returned. If a callback is provided it will be executed for each value in the collection to generate the criterion by which the value is ranked. The callback is bound to `thisArg` and invoked with three arguments; *(value, index, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the minimum value. #### Example ```js _.min([4, 2, 8, 6]); // => 2 var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; _.min(characters, function(chr) { return chr.age; }); // => { 'name': 'barney', 'age': 36 }; // using "_.pluck" callback shorthand _.min(characters, 'age'); // => { 'name': 'barney', 'age': 36 }; ``` * * * ### `_.pluck(collection, property)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4053 "View in source") [Ⓣ][1] Retrieves the value of a specified property from all elements in the collection. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `property` *(string)*: The name of the property to pluck. #### Returns *(Array)*: Returns a new array of property values. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; _.pluck(characters, 'name'); // => ['barney', 'fred'] ``` * * * ### `_.reduce(collection, [callback=identity], [accumulator], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4085 "View in source") [Ⓣ][1] Reduces a collection to a value which is the accumulated result of running each element in the collection through the callback, where each successive callback execution consumes the return value of the previous execution. If `accumulator` is not provided the first element of the collection will be used as the initial `accumulator` value. The callback is bound to `thisArg` and invoked with four arguments; *(accumulator, value, index|key, collection)*. #### Aliases *_.foldl, _.inject* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[accumulator]` *(*)*: Initial value of the accumulator. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the accumulated value. #### Example ```js var sum = _.reduce([1, 2, 3], function(sum, num) { return sum + num; }); // => 6 var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { result[key] = num * 3; return result; }, {}); // => { 'a': 3, 'b': 6, 'c': 9 } ``` * * * ### `_.reduceRight(collection, [callback=identity], [accumulator], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4128 "View in source") [Ⓣ][1] This method is like `_.reduce` except that it iterates over elements of a `collection` from right to left. #### Aliases *_.foldr* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[accumulator]` *(*)*: Initial value of the accumulator. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the accumulated value. #### Example ```js var list = [[0, 1], [2, 3], [4, 5]]; var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); // => [4, 5, 2, 3, 0, 1] ``` * * * ### `_.reject(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4177 "View in source") [Ⓣ][1] The opposite of `_.filter` this method returns the elements of a collection that the callback does **not** return truey for. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a new array of elements that failed the callback check. #### Example ```js var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); // => [1, 3, 5] var characters = [ { 'name': 'barney', 'age': 36, 'blocked': false }, { 'name': 'fred', 'age': 40, 'blocked': true } ]; // using "_.pluck" callback shorthand _.reject(characters, 'blocked'); // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] // using "_.where" callback shorthand _.reject(characters, { 'age': 36 }); // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] ``` * * * ### `_.sample(collection, [n])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4203 "View in source") [Ⓣ][1] Retrieves a random element or `n` random elements from a collection. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to sample. 2. `[n]` *(number)*: The number of elements to sample. #### Returns *(Array)*: Returns the random sample(s) of `collection`. #### Example ```js _.sample([1, 2, 3, 4]); // => 2 _.sample([1, 2, 3, 4], 2); // => [3, 1] ``` * * * ### `_.shuffle(collection)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4231 "View in source") [Ⓣ][1] Creates an array of shuffled values, using a version of the Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to shuffle. #### Returns *(Array)*: Returns a new shuffled collection. #### Example ```js _.shuffle([1, 2, 3, 4, 5, 6]); // => [4, 1, 6, 3, 5, 2] ``` * * * ### `_.size(collection)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4264 "View in source") [Ⓣ][1] Gets the size of the `collection` by returning `collection.length` for arrays and array-like objects or the number of own enumerable properties for objects. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to inspect. #### Returns *(number)*: Returns `collection.length` or number of own enumerable properties. #### Example ```js _.size([1, 2]); // => 2 _.size({ 'one': 1, 'two': 2, 'three': 3 }); // => 3 _.size('pebbles'); // => 7 ``` * * * ### `_.some(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4311 "View in source") [Ⓣ][1] Checks if the callback returns a truey value for **any** element of a collection. The function returns as soon as it finds a passing value and does not iterate over the entire collection. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Aliases *_.any* #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(boolean)*: Returns `true` if any element passed the callback check, else `false`. #### Example ```js _.some([null, 0, 'yes', false], Boolean); // => true var characters = [ { 'name': 'barney', 'age': 36, 'blocked': false }, { 'name': 'fred', 'age': 40, 'blocked': true } ]; // using "_.pluck" callback shorthand _.some(characters, 'blocked'); // => true // using "_.where" callback shorthand _.some(characters, { 'age': 1 }); // => false ``` * * * ### `_.sortBy(collection, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4381 "View in source") [Ⓣ][1] Creates an array of elements, sorted in ascending order by the results of running each element in a collection through the callback. This method performs a stable sort, that is, it will preserve the original sort order of equal elements. The callback is bound to `thisArg` and invoked with three arguments; *(value, index|key, collection)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an array of property names is provided for `callback` the collection will be sorted by each property value. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `[callback=identity]` *(Array|Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a new array of sorted elements. #### Example ```js _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); // => [3, 1, 2] _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); // => [3, 1, 2] var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }, { 'name': 'barney', 'age': 26 }, { 'name': 'fred', 'age': 30 } ]; // using "_.pluck" callback shorthand _.map(_.sortBy(characters, 'age'), _.values); // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]] // sorting by multiple properties _.map(_.sortBy(characters, ['name', 'age']), _.values); // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]] ``` * * * ### `_.toArray(collection)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4427 "View in source") [Ⓣ][1] Converts the `collection` to an array. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to convert. #### Returns *(Array)*: Returns the new converted array. #### Example ```js (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); // => [2, 3, 4] ``` * * * ### `_.where(collection, props)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L4461 "View in source") [Ⓣ][1] Performs a deep comparison of each element in a `collection` to the given `properties` object, returning an array of all elements that have equivalent property values. #### Arguments 1. `collection` *(Array|Object|string)*: The collection to iterate over. 2. `props` *(Object)*: The object of property values to filter by. #### Returns *(Array)*: Returns a new array of elements that have the given properties. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }, { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } ]; _.where(characters, { 'age': 36 }); // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }] _.where(characters, { 'pets': ['dino'] }); // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }] ``` * * * ## `“Functions” Methods` ### `_.after(n, func)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5504 "View in source") [Ⓣ][1] Creates a function that executes `func`, with the `this` binding and arguments of the created function, only after being called `n` times. #### Arguments 1. `n` *(number)*: The number of times the function must be called before `func` is executed. 2. `func` *(Function)*: The function to restrict. #### Returns *(Function)*: Returns the new restricted function. #### Example ```js var saves = ['profile', 'settings']; var done = _.after(saves.length, function() { console.log('Done saving!'); }); _.forEach(saves, function(type) { asyncSave({ 'type': type, 'complete': done }); }); // => logs 'Done saving!', after all saves have completed ``` * * * ### `_.bind(func, [thisArg], [arg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5537 "View in source") [Ⓣ][1] Creates a function that, when called, invokes `func` with the `this` binding of `thisArg` and prepends any additional `bind` arguments to those provided to the bound function. #### Arguments 1. `func` *(Function)*: The function to bind. 2. `[thisArg]` *(*)*: The `this` binding of `func`. 3. `[arg]` *(...*)*: Arguments to be partially applied. #### Returns *(Function)*: Returns the new bound function. #### Example ```js var func = function(greeting) { return greeting + ' ' + this.name; }; func = _.bind(func, { 'name': 'fred' }, 'hi'); func(); // => 'hi fred' ``` * * * ### `_.bindAll(object, [methodName])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5567 "View in source") [Ⓣ][1] Binds methods of an object to the object itself, overwriting the existing method. Method names may be specified as individual arguments or as arrays of method names. If no method names are provided all the function properties of `object` will be bound. #### Arguments 1. `object` *(Object)*: The object to bind and assign the bound methods to. 2. `[methodName]` *(...string)*: The object method names to bind, specified as individual method names or arrays of method names. #### Returns *(Object)*: Returns `object`. #### Example ```js var view = { 'label': 'docs', 'onClick': function() { console.log('clicked ' + this.label); } }; _.bindAll(view); jQuery('#docs').on('click', view.onClick); // => logs 'clicked docs', when the button is clicked ``` * * * ### `_.bindKey(object, key, [arg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5613 "View in source") [Ⓣ][1] Creates a function that, when called, invokes the method at `object[key]` and prepends any additional `bindKey` arguments to those provided to the bound function. This method differs from `_.bind` by allowing bound functions to reference methods that will be redefined or don't yet exist. See http://michaux.ca/articles/lazy-function-definition-pattern. #### Arguments 1. `object` *(Object)*: The object the method belongs to. 2. `key` *(string)*: The key of the method. 3. `[arg]` *(...*)*: Arguments to be partially applied. #### Returns *(Function)*: Returns the new bound function. #### Example ```js var object = { 'name': 'fred', 'greet': function(greeting) { return greeting + ' ' + this.name; } }; var func = _.bindKey(object, 'greet', 'hi'); func(); // => 'hi fred' object.greet = function(greeting) { return greeting + 'ya ' + this.name + '!'; }; func(); // => 'hiya fred!' ``` * * * ### `_.compose([func])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5649 "View in source") [Ⓣ][1] Creates a function that is the composition of the provided functions, where each function consumes the return value of the function that follows. For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. Each function is executed with the `this` binding of the composed function. #### Arguments 1. `[func]` *(...Function)*: Functions to compose. #### Returns *(Function)*: Returns the new composed function. #### Example ```js var realNameMap = { 'pebbles': 'penelope' }; var format = function(name) { name = realNameMap[name.toLowerCase()] || name; return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase(); }; var greet = function(formatted) { return 'Hiya ' + formatted + '!'; }; var welcome = _.compose(greet, format); welcome('pebbles'); // => 'Hiya Penelope!' ``` * * * ### `_.curry(func, [arity=func.length])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5697 "View in source") [Ⓣ][1] Creates a function which accepts one or more arguments of `func` that when invoked either executes `func` returning its result, if all `func` arguments have been provided, or returns a function that accepts one or more of the remaining `func` arguments, and so on. The arity of `func` can be specified if `func.length` is not sufficient. #### Arguments 1. `func` *(Function)*: The function to curry. 2. `[arity=func.length]` *(number)*: The arity of `func`. #### Returns *(Function)*: Returns the new curried function. #### Example ```js var curried = _.curry(function(a, b, c) { console.log(a + b + c); }); curried(1)(2)(3); // => 6 curried(1, 2)(3); // => 6 curried(1, 2, 3); // => 6 ``` * * * ### `_.debounce(func, wait, [options], [options.maxWait])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5741 "View in source") [Ⓣ][1] Creates a function that will delay the execution of `func` until after `wait` milliseconds have elapsed since the last time it was invoked. Provide an options object to indicate that `func` should be invoked on the leading and/or trailing edge of the `wait` timeout. Subsequent calls to the debounced function will return the result of the last `func` call. Note: If `leading` and `trailing` options are `true` `func` will be called on the trailing edge of the timeout only if the the debounced function is invoked more than once during the `wait` timeout. #### Arguments 1. `func` *(Function)*: The function to debounce. 2. `wait` *(number)*: The number of milliseconds to delay. 3. `[options]` *(Object)*: The options object. 4. `[options.leading=false]` *(boolean)*: Specify execution on the leading edge of the timeout. 5. `[options.maxWait]` *(number)*: The maximum time `func` is allowed to be delayed before it's called. 6. `[options.trailing=true]` *(boolean)*: Specify execution on the trailing edge of the timeout. #### Returns *(Function)*: Returns the new debounced function. #### Example ```js // avoid costly calculations while the window size is in flux var lazyLayout = _.debounce(calculateLayout, 150); jQuery(window).on('resize', lazyLayout); // execute `sendMail` when the click event is fired, debouncing subsequent calls jQuery('#postbox').on('click', _.debounce(sendMail, 300, { 'leading': true, 'trailing': false }); // ensure `batchLog` is executed once after 1 second of debounced calls var source = new EventSource('/stream'); source.addEventListener('message', _.debounce(batchLog, 250, { 'maxWait': 1000 }, false); ``` * * * ### `_.defer(func, [arg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5857 "View in source") [Ⓣ][1] Defers executing the `func` function until the current call stack has cleared. Additional arguments will be provided to `func` when it is invoked. #### Arguments 1. `func` *(Function)*: The function to defer. 2. `[arg]` *(...*)*: Arguments to invoke the function with. #### Returns *(number)*: Returns the timer id. #### Example ```js _.defer(function(text) { console.log(text); }, 'deferred'); // logs 'deferred' after one or more milliseconds ``` * * * ### `_.delay(func, wait, [arg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5881 "View in source") [Ⓣ][1] Executes the `func` function after `wait` milliseconds. Additional arguments will be provided to `func` when it is invoked. #### Arguments 1. `func` *(Function)*: The function to delay. 2. `wait` *(number)*: The number of milliseconds to delay execution. 3. `[arg]` *(...*)*: Arguments to invoke the function with. #### Returns *(number)*: Returns the timer id. #### Example ```js _.delay(function(text) { console.log(text); }, 1000, 'later'); // => logs 'later' after one second ``` * * * ### `_.memoize(func, [resolver])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5926 "View in source") [Ⓣ][1] Creates a function that memoizes the result of `func`. If `resolver` is provided it will be used to determine the cache key for storing the result based on the arguments provided to the memoized function. By default, the first argument provided to the memoized function is used as the cache key. The `func` is executed with the `this` binding of the memoized function. The result cache is exposed as the `cache` property on the memoized function. #### Arguments 1. `func` *(Function)*: The function to have its output memoized. 2. `[resolver]` *(Function)*: A function used to resolve the cache key. #### Returns *(Function)*: Returns the new memoizing function. #### Example ```js var fibonacci = _.memoize(function(n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); }); fibonacci(9) // => 34 var data = { 'fred': { 'name': 'fred', 'age': 40 }, 'pebbles': { 'name': 'pebbles', 'age': 1 } }; // modifying the result cache var get = _.memoize(function(name) { return data[name]; }, _.identity); get('pebbles'); // => { 'name': 'pebbles', 'age': 1 } get.cache.pebbles.name = 'penelope'; get('pebbles'); // => { 'name': 'penelope', 'age': 1 } ``` * * * ### `_.once(func)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5959 "View in source") [Ⓣ][1] Creates a function that is restricted to execute `func` once. Repeat calls to the function will return the value of the first call. The `func` is executed with the `this` binding of the created function. #### Arguments 1. `func` *(Function)*: The function to restrict. #### Returns *(Function)*: Returns the new restricted function. #### Example ```js var initialize = _.once(createApplication); initialize(); initialize(); // `initialize` executes `createApplication` once ``` * * * ### `_.partial(func, [arg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L5997 "View in source") [Ⓣ][1] Creates a function that, when called, invokes `func` with any additional `partial` arguments prepended to those provided to the new function. This method is similar to `_.bind` except it does **not** alter the `this` binding. #### Arguments 1. `func` *(Function)*: The function to partially apply arguments to. 2. `[arg]` *(...*)*: Arguments to be partially applied. #### Returns *(Function)*: Returns the new partially applied function. #### Example ```js var greet = function(greeting, name) { return greeting + ' ' + name; }; var hi = _.partial(greet, 'hi'); hi('fred'); // => 'hi fred' ``` * * * ### `_.partialRight(func, [arg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6028 "View in source") [Ⓣ][1] This method is like `_.partial` except that `partial` arguments are appended to those provided to the new function. #### Arguments 1. `func` *(Function)*: The function to partially apply arguments to. 2. `[arg]` *(...*)*: Arguments to be partially applied. #### Returns *(Function)*: Returns the new partially applied function. #### Example ```js var defaultsDeep = _.partialRight(_.merge, _.defaults); var options = { 'variable': 'data', 'imports': { 'jq': $ } }; defaultsDeep(options, _.templateSettings); options.variable // => 'data' options.imports // => { '_': _, 'jq': $ } ``` * * * ### `_.throttle(func, wait, [options])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6063 "View in source") [Ⓣ][1] Creates a function that, when executed, will only call the `func` function at most once per every `wait` milliseconds. Provide an options object to indicate that `func` should be invoked on the leading and/or trailing edge of the `wait` timeout. Subsequent calls to the throttled function will return the result of the last `func` call. Note: If `leading` and `trailing` options are `true` `func` will be called on the trailing edge of the timeout only if the the throttled function is invoked more than once during the `wait` timeout. #### Arguments 1. `func` *(Function)*: The function to throttle. 2. `wait` *(number)*: The number of milliseconds to throttle executions to. 3. `[options]` *(Object)*: The options object. 4. `[options.leading=true]` *(boolean)*: Specify execution on the leading edge of the timeout. 5. `[options.trailing=true]` *(boolean)*: Specify execution on the trailing edge of the timeout. #### Returns *(Function)*: Returns the new throttled function. #### Example ```js // avoid excessively updating the position while scrolling var throttled = _.throttle(updatePosition, 100); jQuery(window).on('scroll', throttled); // execute `renewToken` when the click event is fired, but not more than once every 5 minutes jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { 'trailing': false })); ``` * * * ### `_.wrap(value, wrapper)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6104 "View in source") [Ⓣ][1] Creates a function that provides `value` to the wrapper function as its first argument. Additional arguments provided to the function are appended to those provided to the wrapper function. The wrapper is executed with the `this` binding of the created function. #### Arguments 1. `value` *(*)*: The value to wrap. 2. `wrapper` *(Function)*: The wrapper function. #### Returns *(Function)*: Returns the new function. #### Example ```js var p = _.wrap(_.escape, function(func, text) { return '

' + func(text) + '

'; }); p('Fred, Wilma, & Pebbles'); // => '

Fred, Wilma, & Pebbles

' ``` * * * ## `“Objects” Methods` ### `_.assign(object, [source], [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2084 "View in source") [Ⓣ][1] Assigns own enumerable properties of source object(s) to the destination object. Subsequent sources will overwrite property assignments of previous sources. If a callback is provided it will be executed to produce the assigned values. The callback is bound to `thisArg` and invoked with two arguments; *(objectValue, sourceValue)*. #### Aliases *_.extend* #### Arguments 1. `object` *(Object)*: The destination object. 2. `[source]` *(...Object)*: The source objects. 3. `[callback]` *(Function)*: The function to customize assigning values. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns the destination object. #### Example ```js _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); // => { 'name': 'fred', 'employer': 'slate' } var defaults = _.partialRight(_.assign, function(a, b) { return typeof a == 'undefined' ? b : a; }); var object = { 'name': 'barney' }; defaults(object, { 'name': 'fred', 'employer': 'slate' }); // => { 'name': 'barney', 'employer': 'slate' } ``` * * * ### `_.clone(value, [isDeep=false], [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2137 "View in source") [Ⓣ][1] Creates a clone of `value`. If `isDeep` is `true` nested objects will also be cloned, otherwise they will be assigned by reference. If a callback is provided it will be executed to produce the cloned values. If the callback returns `undefined` cloning will be handled by the method instead. The callback is bound to `thisArg` and invoked with one argument; *(value)*. #### Arguments 1. `value` *(*)*: The value to clone. 2. `[isDeep=false]` *(boolean)*: Specify a deep clone. 3. `[callback]` *(Function)*: The function to customize cloning values. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the cloned value. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; var shallow = _.clone(characters); shallow[0] === characters[0]; // => true var deep = _.clone(characters, true); deep[0] === characters[0]; // => false _.mixin({ 'clone': _.partialRight(_.clone, function(value) { return _.isElement(value) ? value.cloneNode(false) : undefined; }) }); var clone = _.clone(document.body); clone.childNodes.length; // => 0 ``` * * * ### `_.cloneDeep(value, [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2189 "View in source") [Ⓣ][1] Creates a deep clone of `value`. If a callback is provided it will be executed to produce the cloned values. If the callback returns `undefined` cloning will be handled by the method instead. The callback is bound to `thisArg` and invoked with one argument; *(value)*. Note: This method is loosely based on the structured clone algorithm. Functions and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and objects created by constructors other than `Object` are cloned to plain `Object` objects. See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm. #### Arguments 1. `value` *(*)*: The value to deep clone. 2. `[callback]` *(Function)*: The function to customize cloning values. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the deep cloned value. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; var deep = _.cloneDeep(characters); deep[0] === characters[0]; // => false var view = { 'label': 'docs', 'node': element }; var clone = _.cloneDeep(view, function(value) { return _.isElement(value) ? value.cloneNode(true) : undefined; }); clone.node == view.node; // => false ``` * * * ### `_.create(prototype, [properties])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2224 "View in source") [Ⓣ][1] Creates an object that inherits from the given `prototype` object. If a `properties` object is provided its own enumerable properties are assigned to the created object. #### Arguments 1. `prototype` *(Object)*: The object to inherit from. 2. `[properties]` *(Object)*: The properties to assign to the object. #### Returns *(Object)*: Returns the new object. #### Example ```js function Shape() { this.x = 0; this.y = 0; } function Circle() { Shape.call(this); } Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle }); var circle = new Circle; circle instanceof Circle; // => true circle instanceof Shape; // => true ``` * * * ### `_.defaults(object, [source])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2249 "View in source") [Ⓣ][1] Assigns own enumerable properties of source object(s) to the destination object for all destination properties that resolve to `undefined`. Once a property is set, additional defaults of the same property will be ignored. #### Arguments 1. `object` *(Object)*: The destination object. 2. `[source]` *(...Object)*: The source objects. #### Returns *(Object)*: Returns the destination object. #### Example ```js var object = { 'name': 'barney' }; _.defaults(object, { 'name': 'fred', 'employer': 'slate' }); // => { 'name': 'barney', 'employer': 'slate' } ``` * * * ### `_.findKey(object, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2292 "View in source") [Ⓣ][1] This method is like `_.findIndex` except that it returns the key of the first element that passes the callback check, instead of the element itself. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `object` *(Object)*: The object to search. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(string, undefined)*: Returns the key of the found element, else `undefined`. #### Example ```js var characters = { 'barney': { 'age': 36, 'blocked': false }, 'fred': { 'age': 40, 'blocked': true }, 'pebbles': { 'age': 1, 'blocked': false } }; _.findKey(characters, function(chr) { return chr.age < 40; }); // => 'barney' (property order is not guaranteed across environments) // using "_.where" callback shorthand _.findKey(characters, { 'age': 1 }); // => 'pebbles' // using "_.pluck" callback shorthand _.findKey(characters, 'blocked'); // => 'fred' ``` * * * ### `_.findLastKey(object, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2345 "View in source") [Ⓣ][1] This method is like `_.findKey` except that it iterates over elements of a `collection` in the opposite order. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `object` *(Object)*: The object to search. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(string, undefined)*: Returns the key of the found element, else `undefined`. #### Example ```js var characters = { 'barney': { 'age': 36, 'blocked': true }, 'fred': { 'age': 40, 'blocked': false }, 'pebbles': { 'age': 1, 'blocked': true } }; _.findLastKey(characters, function(chr) { return chr.age < 40; }); // => returns `pebbles`, assuming `_.findKey` returns `barney` // using "_.where" callback shorthand _.findLastKey(characters, { 'age': 40 }); // => 'fred' // using "_.pluck" callback shorthand _.findLastKey(characters, 'blocked'); // => 'pebbles' ``` * * * ### `_.forIn(object, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2388 "View in source") [Ⓣ][1] Iterates over own and inherited enumerable properties of an object, 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`. #### Arguments 1. `object` *(Object)*: The object to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns `object`. #### Example ```js function Shape() { this.x = 0; this.y = 0; } Shape.prototype.move = function(x, y) { this.x += x; this.y += y; }; _.forIn(new Shape, function(value, key) { console.log(key); }); // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) ``` * * * ### `_.forInRight(object, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2420 "View in source") [Ⓣ][1] This method is like `_.forIn` except that it iterates over elements of a `collection` in the opposite order. #### Arguments 1. `object` *(Object)*: The object to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns `object`. #### Example ```js function Shape() { this.x = 0; this.y = 0; } Shape.prototype.move = function(x, y) { this.x += x; this.y += y; }; _.forInRight(new Shape, function(value, key) { console.log(key); }); // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move' ``` * * * ### `_.forOwn(object, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2458 "View in source") [Ⓣ][1] Iterates over own enumerable properties of an object, 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`. #### Arguments 1. `object` *(Object)*: The object to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns `object`. #### Example ```js _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { console.log(key); }); // => logs '0', '1', and 'length' (property order is not guaranteed across environments) ``` * * * ### `_.forOwnRight(object, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2478 "View in source") [Ⓣ][1] This method is like `_.forOwn` except that it iterates over elements of a `collection` in the opposite order. #### Arguments 1. `object` *(Object)*: The object to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns `object`. #### Example ```js _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { console.log(key); }); // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length' ``` * * * ### `_.functions(object)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2507 "View in source") [Ⓣ][1] Creates a sorted array of property names of all enumerable properties, own and inherited, of `object` that have function values. #### Aliases *_.methods* #### Arguments 1. `object` *(Object)*: The object to inspect. #### Returns *(Array)*: Returns an array of property names that have function values. #### Example ```js _.functions(_); // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] ``` * * * ### `_.has(object, key)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2532 "View in source") [Ⓣ][1] Checks if the specified property name exists as a direct property of `object`, instead of an inherited property. #### Arguments 1. `object` *(Object)*: The object to inspect. 2. `key` *(string)*: The name of the property to check. #### Returns *(boolean)*: Returns `true` if key is a direct property, else `false`. #### Example ```js _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); // => true ``` * * * ### `_.invert(object)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2549 "View in source") [Ⓣ][1] Creates an object composed of the inverted keys and values of the given object. #### Arguments 1. `object` *(Object)*: The object to invert. #### Returns *(Object)*: Returns the created inverted object. #### Example ```js _.invert({ 'first': 'fred', 'second': 'barney' }); // => { 'fred': 'first', 'barney': 'second' } ``` * * * ### `_.isArguments(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L1909 "View in source") [Ⓣ][1] Checks if `value` is an `arguments` object. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is an `arguments` object, else `false`. #### Example ```js (function() { return _.isArguments(arguments); })(1, 2, 3); // => true _.isArguments([1, 2, 3]); // => false ``` * * * ### `_.isArray(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L1938 "View in source") [Ⓣ][1] Checks if `value` is an array. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is an array, else `false`. #### Example ```js (function() { return _.isArray(arguments); })(); // => false _.isArray([1, 2, 3]); // => true ``` * * * ### `_.isBoolean(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2575 "View in source") [Ⓣ][1] Checks if `value` is a boolean value. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is a boolean value, else `false`. #### Example ```js _.isBoolean(null); // => false ``` * * * ### `_.isDate(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2593 "View in source") [Ⓣ][1] Checks if `value` is a date. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is a date, else `false`. #### Example ```js _.isDate(new Date); // => true ``` * * * ### `_.isElement(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2610 "View in source") [Ⓣ][1] Checks if `value` is a DOM element. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is a DOM element, else `false`. #### Example ```js _.isElement(document.body); // => true ``` * * * ### `_.isEmpty(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2635 "View in source") [Ⓣ][1] 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". #### Arguments 1. `value` *(Array|Object|string)*: The value to inspect. #### Returns *(boolean)*: Returns `true` if the `value` is empty, else `false`. #### Example ```js _.isEmpty([1, 2, 3]); // => false _.isEmpty({}); // => true _.isEmpty(''); // => true ``` * * * ### `_.isEqual(a, b, [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2692 "View in source") [Ⓣ][1] Performs a deep comparison between two values to determine if they are equivalent to each other. If a callback is provided it will be executed to compare values. If the callback returns `undefined` comparisons will be handled by the method instead. The callback is bound to `thisArg` and invoked with two arguments; *(a, b)*. #### Arguments 1. `a` *(*)*: The value to compare. 2. `b` *(*)*: The other value to compare. 3. `[callback]` *(Function)*: The function to customize comparing values. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(boolean)*: Returns `true` if the values are equivalent, else `false`. #### Example ```js var object = { 'name': 'fred' }; var copy = { 'name': 'fred' }; object == copy; // => false _.isEqual(object, copy); // => true var words = ['hello', 'goodbye']; var otherWords = ['hi', 'goodbye']; _.isEqual(words, otherWords, function(a, b) { var reGreet = /^(?:hello|hi)$/i, aGreet = _.isString(a) && reGreet.test(a), bGreet = _.isString(b) && reGreet.test(b); return (aGreet || bGreet) ? (aGreet == bGreet) : undefined; }); // => true ``` * * * ### `_.isFinite(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2724 "View in source") [Ⓣ][1] Checks if `value` is, or can be coerced to, a finite number. Note: This is not the same as native `isFinite` which will return true for booleans and empty strings. See http://es5.github.io/#x15.1.2.5. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is finite, else `false`. #### Example ```js _.isFinite(-101); // => true _.isFinite('10'); // => true _.isFinite(true); // => false _.isFinite(''); // => false _.isFinite(Infinity); // => false ``` * * * ### `_.isFunction(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2741 "View in source") [Ⓣ][1] Checks if `value` is a function. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is a function, else `false`. #### Example ```js _.isFunction(_); // => true ``` * * * ### `_.isNaN(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2804 "View in source") [Ⓣ][1] Checks if `value` is `NaN`. Note: This is not the same as native `isNaN` which will return `true` for `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is `NaN`, else `false`. #### Example ```js _.isNaN(NaN); // => true _.isNaN(new Number(NaN)); // => true isNaN(undefined); // => true _.isNaN(undefined); // => false ``` * * * ### `_.isNull(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2826 "View in source") [Ⓣ][1] Checks if `value` is `null`. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is `null`, else `false`. #### Example ```js _.isNull(null); // => true _.isNull(undefined); // => false ``` * * * ### `_.isNumber(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2845 "View in source") [Ⓣ][1] Checks if `value` is a number. Note: `NaN` is considered a number. See http://es5.github.io/#x8.5. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is a number, else `false`. #### Example ```js _.isNumber(8.4 * 5); // => true ``` * * * ### `_.isObject(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2771 "View in source") [Ⓣ][1] Checks if `value` is the language type of Object. *(e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)* #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is an object, else `false`. #### Example ```js _.isObject({}); // => true _.isObject([1, 2, 3]); // => true _.isObject(1); // => false ``` * * * ### `_.isPlainObject(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2874 "View in source") [Ⓣ][1] Checks if `value` is an object created by the `Object` constructor. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if `value` is a plain object, else `false`. #### Example ```js function Shape() { this.x = 0; this.y = 0; } _.isPlainObject(new Shape); // => false _.isPlainObject([1, 2, 3]); // => false _.isPlainObject({ 'x': 0, 'y': 0 }); // => true ``` * * * ### `_.isRegExp(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2899 "View in source") [Ⓣ][1] Checks if `value` is a regular expression. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is a regular expression, else `false`. #### Example ```js _.isRegExp(/fred/); // => true ``` * * * ### `_.isString(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2916 "View in source") [Ⓣ][1] Checks if `value` is a string. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is a string, else `false`. #### Example ```js _.isString('fred'); // => true ``` * * * ### `_.isUndefined(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2934 "View in source") [Ⓣ][1] Checks if `value` is `undefined`. #### Arguments 1. `value` *(*)*: The value to check. #### Returns *(boolean)*: Returns `true` if the `value` is `undefined`, else `false`. #### Example ```js _.isUndefined(void 0); // => true ``` * * * ### `_.keys(object)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L1972 "View in source") [Ⓣ][1] Creates an array composed of the own enumerable property names of an object. #### Arguments 1. `object` *(Object)*: The object to inspect. #### Returns *(Array)*: Returns an array of property names. #### Example ```js _.keys({ 'one': 1, 'two': 2, 'three': 3 }); // => ['one', 'two', 'three'] (property order is not guaranteed across environments) ``` * * * ### `_.mapValues(object, [callback=identity], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L2974 "View in source") [Ⓣ][1] Creates an object with the same keys as `object` and values generated by running each own enumerable property of `object` through the callback. The callback is bound to `thisArg` and invoked with three arguments; *(value, key, object)*. If a property name is provided for `callback` the created "_.pluck" style callback will return the property value of the given element. If an object is provided for `callback` the created "_.where" style callback will return `true` for elements that have the properties of the given object, else `false`. #### Arguments 1. `object` *(Object)*: The object to iterate over. 2. `[callback=identity]` *(Function|Object|string)*: The function called per iteration. If a property name or object is provided it will be used to create a "_.pluck" or "_.where" style callback, respectively. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Array)*: Returns a new object with values of the results of each `callback` execution. #### Example ```js _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; }); // => { 'a': 3, 'b': 6, 'c': 9 } var characters = { 'fred': { 'name': 'fred', 'age': 40 }, 'pebbles': { 'name': 'pebbles', 'age': 1 } }; // using "_.pluck" callback shorthand _.mapValues(characters, 'age'); // => { 'fred': 40, 'pebbles': 1 } ``` * * * ### `_.merge(object, [source], [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3035 "View in source") [Ⓣ][1] Recursively merges own enumerable properties of the source object(s), that don't resolve to `undefined` into the destination object. Subsequent sources will overwrite property assignments of previous sources. If a callback is provided it will be executed to produce the merged values of the destination and source properties. If the callback returns `undefined` merging will be handled by the method instead. The callback is bound to `thisArg` and invoked with two arguments; *(objectValue, sourceValue)*. #### Arguments 1. `object` *(Object)*: The destination object. 2. `[source]` *(...Object)*: The source objects. 3. `[callback]` *(Function)*: The function to customize merging properties. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns the destination object. #### Example ```js var names = { 'characters': [ { 'name': 'barney' }, { 'name': 'fred' } ] }; var ages = { 'characters': [ { 'age': 36 }, { 'age': 40 } ] }; _.merge(names, ages); // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] } var food = { 'fruits': ['apple'], 'vegetables': ['beet'] }; var otherFood = { 'fruits': ['banana'], 'vegetables': ['carrot'] }; _.merge(food, otherFood, function(a, b) { return _.isArray(a) ? a.concat(b) : undefined; }); // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] } ``` * * * ### `_.omit(object, [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3091 "View in source") [Ⓣ][1] Creates a shallow clone of `object` excluding the specified properties. Property names may be specified as individual arguments or as arrays of property names. If a callback is provided it will be executed for each property of `object` omitting the properties the callback returns truey for. The callback is bound to `thisArg` and invoked with three arguments; *(value, key, object)*. #### Arguments 1. `object` *(Object)*: The source object. 2. `[callback]` *(Function|...string|string[])*: The properties to omit or the function called per iteration. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns an object without the omitted properties. #### Example ```js _.omit({ 'name': 'fred', 'age': 40 }, 'age'); // => { 'name': 'fred' } _.omit({ 'name': 'fred', 'age': 40 }, function(value) { return typeof value == 'number'; }); // => { 'name': 'fred' } ``` * * * ### `_.pairs(object)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3132 "View in source") [Ⓣ][1] Creates a two dimensional array of an object's key-value pairs, i.e. `[[key1, value1], [key2, value2]]`. #### Arguments 1. `object` *(Object)*: The object to inspect. #### Returns *(Array)*: Returns new array of key-value pairs. #### Example ```js _.pairs({ 'barney': 36, 'fred': 40 }); // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments) ``` * * * ### `_.pick(object, [callback], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3172 "View in source") [Ⓣ][1] Creates a shallow clone of `object` composed of the specified properties. Property names may be specified as individual arguments or as arrays of property names. If a callback is provided it will be executed for each property of `object` picking the properties the callback returns truey for. The callback is bound to `thisArg` and invoked with three arguments; *(value, key, object)*. #### Arguments 1. `object` *(Object)*: The source object. 2. `[callback]` *(Function|...string|string[])*: The function called per iteration or property names to pick, specified as individual property names or arrays of property names. 3. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(Object)*: Returns an object composed of the picked properties. #### Example ```js _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name'); // => { 'name': 'fred' } _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) { return key.charAt(0) != '_'; }); // => { 'name': 'fred' } ``` * * * ### `_.transform(object, [callback=identity], [accumulator], [thisArg])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3227 "View in source") [Ⓣ][1] An alternative to `_.reduce` this method transforms `object` to a new `accumulator` object which is the result of running each of its own enumerable properties through a callback, with each callback execution potentially mutating the `accumulator` object. The callback is bound to `thisArg` and invoked with four arguments; *(accumulator, value, key, object)*. Callbacks may exit iteration early by explicitly returning `false`. #### Arguments 1. `object` *(Array|Object)*: The object to iterate over. 2. `[callback=identity]` *(Function)*: The function called per iteration. 3. `[accumulator]` *(*)*: The custom accumulator value. 4. `[thisArg]` *(*)*: The `this` binding of `callback`. #### Returns *(*)*: Returns the accumulated value. #### Example ```js var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) { num *= num; if (num % 2) { return result.push(num) < 3; } }); // => [1, 9, 25] var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { result[key] = num * 3; }); // => { 'a': 3, 'b': 6, 'c': 9 } ``` * * * ### `_.values(object)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L3261 "View in source") [Ⓣ][1] Creates an array composed of the own enumerable property values of `object`. #### Arguments 1. `object` *(Object)*: The object to inspect. #### Returns *(Array)*: Returns an array of property values. #### Example ```js _.values({ 'one': 1, 'two': 2, 'three': 3 }); // => [1, 2, 3] (property order is not guaranteed across environments) ``` * * * ## `“Utilities” Methods` ### `_.now` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6351 "View in source") [Ⓣ][1] *(unknown)*: Gets the number of milliseconds that have elapsed since the Unix epoch *(1 January `1970 00`:00:00 UTC)*. #### Example ```js var stamp = _.now(); _.defer(function() { console.log(_.now() - stamp); }); // => logs the number of milliseconds it took for the deferred function to be called ``` * * * ### `_.constant(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6125 "View in source") [Ⓣ][1] Creates a function that returns `value`. #### Arguments 1. `value` *(*)*: The value to return from the new function. #### Returns *(Function)*: Returns the new function. #### Example ```js var object = { 'name': 'fred' }; var getter = _.constant(object); getter() === object; // => true ``` * * * ### `_.createCallback([func=identity], [thisArg], [argCount])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6162 "View in source") [Ⓣ][1] Produces a callback bound to an optional `thisArg`. If `func` is a property name the created callback will return the property value for a given element. If `func` is an object the created callback will return `true` for elements that contain the equivalent object properties, otherwise it will return `false`. #### Arguments 1. `[func=identity]` *(*)*: The value to convert to a callback. 2. `[thisArg]` *(*)*: The `this` binding of the created callback. 3. `[argCount]` *(number)*: The number of arguments the callback accepts. #### Returns *(Function)*: Returns a callback function. #### Example ```js var characters = [ { 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 } ]; // wrap to create custom callback shorthands _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) { var match = /^(.+?)__([gl]t)(.+)$/.exec(callback); return !match ? func(callback, thisArg) : function(object) { return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3]; }; }); _.filter(characters, 'age__gt38'); // => [{ 'name': 'fred', 'age': 40 }] ``` * * * ### `_.escape(string)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6211 "View in source") [Ⓣ][1] Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their corresponding HTML entities. #### Arguments 1. `string` *(string)*: The string to escape. #### Returns *(string)*: Returns the escaped string. #### Example ```js _.escape('Fred, Wilma, & Pebbles'); // => 'Fred, Wilma, & Pebbles' ``` * * * ### `_.identity(value)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6229 "View in source") [Ⓣ][1] This method returns the first argument provided to it. #### Arguments 1. `value` *(*)*: Any value. #### Returns *(*)*: Returns `value`. #### Example ```js var object = { 'name': 'fred' }; _.identity(object) === object; // => true ``` * * * ### `_.mixin([object=lodash], source, [options])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6261 "View in source") [Ⓣ][1] Adds function properties of a source object to the destination object. If `object` is a function methods will be added to its prototype as well. #### Arguments 1. `[object=lodash]` *(Function|Object)*: object The destination object. 2. `source` *(Object)*: The object of functions to add. 3. `[options]` *(Object)*: The options object. 4. `[options.chain=true]` *(boolean)*: Specify whether the functions added are chainable. #### Example ```js function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); } _.mixin({ 'capitalize': capitalize }); _.capitalize('fred'); // => 'Fred' _('fred').capitalize().value(); // => 'Fred' _.mixin({ 'capitalize': capitalize }, { 'chain': false }); _('fred').capitalize(); // => 'Fred' ``` * * * ### `_.noConflict()` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6317 "View in source") [Ⓣ][1] Reverts the '_' variable to its previous value and returns a reference to the `lodash` function. #### Returns *(Function)*: Returns the `lodash` function. #### Example ```js var lodash = _.noConflict(); ``` * * * ### `_.noop()` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6334 "View in source") [Ⓣ][1] A no-operation function. #### Example ```js var object = { 'name': 'fred' }; _.noop(object) === undefined; // => true ``` * * * ### `_.parseInt(value, [radix])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6374 "View in source") [Ⓣ][1] Converts the given value into an integer of the specified radix. If `radix` is `undefined` or `0` a `radix` of `10` is used unless the `value` is a hexadecimal, in which case a `radix` of `16` is used. Note: This method avoids differences in native ES3 and ES5 `parseInt` implementations. See http://es5.github.io/#E. #### Arguments 1. `value` *(string)*: The value to parse. 2. `[radix]` *(number)*: The radix used to interpret the value to parse. #### Returns *(number)*: Returns the new integer value. #### Example ```js _.parseInt('08'); // => 8 ``` * * * ### `_.property(key)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6403 "View in source") [Ⓣ][1] Creates a "_.pluck" style function, which returns the `key` value of a given object. #### Arguments 1. `key` *(string)*: The name of the property to retrieve. #### Returns *(Function)*: Returns the new function. #### Example ```js var characters = [ { 'name': 'fred', 'age': 40 }, { 'name': 'barney', 'age': 36 } ]; var getName = _.property('name'); _.map(characters, getName); // => ['barney', 'fred'] _.sortBy(characters, getName); // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] ``` * * * ### `_.random([min=0], [max=1], [floating=false])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6436 "View in source") [Ⓣ][1] Produces a random number between `min` and `max` *(inclusive)*. If only one argument is provided a number between `0` and the given number will be returned. If `floating` is truey or either `min` or `max` are floats a floating-point number will be returned instead of an integer. #### Arguments 1. `[min=0]` *(number)*: The minimum possible value. 2. `[max=1]` *(number)*: The maximum possible value. 3. `[floating=false]` *(boolean)*: Specify returning a floating-point number. #### Returns *(number)*: Returns a random number. #### Example ```js _.random(0, 5); // => an integer between 0 and 5 _.random(5); // => also an integer between 0 and 5 _.random(5, true); // => a floating-point number between 0 and 5 _.random(1.2, 5.2); // => a floating-point number between 1.2 and 5.2 ``` * * * ### `_.result(object, key)` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6494 "View in source") [Ⓣ][1] Resolves the value of property `key` on `object`. If `key` is a function it will be invoked with the `this` binding of `object` and its result returned, else the property value is returned. If `object` is falsey then `undefined` is returned. #### Arguments 1. `object` *(Object)*: The object to inspect. 2. `key` *(string)*: The name of the property to resolve. #### Returns *(*)*: Returns the resolved value. #### Example ```js var object = { 'cheese': 'crumpets', 'stuff': function() { return 'nonsense'; } }; _.result(object, 'cheese'); // => 'crumpets' _.result(object, 'stuff'); // => 'nonsense' ``` * * * ### `_.runInContext([context=root])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L469 "View in source") [Ⓣ][1] Create a new `lodash` function using the given context object. #### Arguments 1. `[context=root]` *(Object)*: The context object. #### Returns *(Function)*: Returns the `lodash` function. * * * ### `_.template(text, data, [options], [options.escape], [options.evaluate], [options.imports], [options.interpolate], [sourceURL], [variable])` # [Ⓢ](https://github.com/lodash/lodash/blob/master/lodash.js#L6587 "View in source") [Ⓣ][1] A micro-templating method that handles arbitrary delimiters, preserves whitespace, and correctly escapes quotes within interpolated code. Note: In the development build, `_.template` utilizes sourceURLs for easier debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl For more information on precompiling templates see:
http://lodash.com/custom-builds For more information on Chrome extension sandboxes see:
http://developer.chrome.com/stable/extensions/sandboxingEval.html #### Arguments 1. `text` *(string)*: The template text. 2. `data` *(Object)*: The data object used to populate the text. 3. `[options]` *(Object)*: The options object. 4. `[options.escape]` *(RegExp)*: The "escape" delimiter. 5. `[options.evaluate]` *(RegExp)*: The "evaluate" delimiter. 6. `[options.imports]` *(Object)*: An object to import into the template as local variables. 7. `[options.interpolate]` *(RegExp)*: The "interpolate" delimiter. 8. `[sourceURL]` *(string)*: The sourceURL of the template's compiled source. 9. `[variable]` *(string)*: The data object variable name. #### Returns *(Function, string)*: Returns a compiled function when no `data` object is given, else it returns the interpolated text. #### Example ```js // using the "interpolate" delimiter to create a compiled template var compiled = _.template('hello <%= name %>'); compiled({ 'name': 'fred' }); // => 'hello fred' // using the "escape" delimiter to escape HTML in data property values _.template('<%- value %>', { 'value': ' ``` Using [`npm`](http://npmjs.org/): ```bash npm i --save lodash {sudo} npm i -g lodash npm ln lodash ``` In [Node.js](http://nodejs.org/) & [Ringo](http://ringojs.org/): ```js var _ = require('lodash'); // or as Underscore var _ = require('lodash/dist/lodash.underscore'); ``` **Notes:** * Don’t assign values to [special variable](http://nodejs.org/api/repl.html#repl_repl_features) `_` when in the REPL * If Lo-Dash is installed globally, run [`npm ln lodash`](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) in your project’s root directory *before* requiring it In [Rhino](http://www.mozilla.org/rhino/): ```js load('lodash.js'); ``` In an AMD loader: ```js require({ 'packages': [ { 'name': 'lodash', 'location': 'path/to/lodash', 'main': 'lodash' } ] }, ['lodash'], function(_) { console.log(_.VERSION); }); ``` ## Author | [![twitter/jdalton](http://gravatar.com/avatar/299a3d891ff1920b69c364d061007043?s=70)](https://twitter.com/jdalton "Follow @jdalton on Twitter") | |---| | [John-David Dalton](http://allyoucanleet.com/) | ## Contributors | [![twitter/blainebublitz](http://gravatar.com/avatar/ac1c67fd906c9fecd823ce302283b4c1?s=70)](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [![twitter/kitcambridge](http://gravatar.com/avatar/6662a1d02f351b5ef2f8b4d815804661?s=70)](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [![twitter/mathias](http://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") | |---|---|---| | [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lodash/lodash/trend.png)](https://bitdeli.com/free "Bitdeli Badge") lodash-2.4.1/.travis.yml0000644000175000017500000001235112247303154013246 0ustar ovdovdlanguage: node_js node_js: - "0.6" - "0.8" - "0.10" env: global: - BIN="node" BUILD=false COMPAT=false MAKE=false OPTION="" SAUCE_LABS=false SAUCE_USERNAME="lodash" - secure: "tg1JFsIFnxzLaTboFPOnm+aJCuMm5+JdhLlESlqg9x3fwro++7KCnwHKLNovhchaPe4otC43ZMB/nfWhDnDm11dKbm/V6HlTkED+dadTsaLxVDg6J+7yK41QhokBPJOxLV78iDaNaAQVYEirAgZ0yn8kFubxmNKV+bpCGQNc9yU=" matrix: - BUILD="compat" - BUILD="modern" - BUILD="legacy" - BUILD="mobile" - BIN="phantomjs" BUILD="compat" - BIN="phantomjs" BUILD="legacy" - BIN="phantomjs" BUILD="mobile" matrix: include: - node_js: "0.10" env: BIN="istanbul" - node_js: "0.10" env: BIN="narwhal" BUILD="compat" - node_js: "0.10" env: BIN="narwhal" BUILD="legacy" - node_js: "0.10" env: BIN="rhino" BUILD="compat" - node_js: "0.10" env: BIN="rhino" BUILD="legacy" - node_js: "0.10" env: BIN="rhino" BUILD="compat" OPTION="-require" - node_js: "0.10" env: BIN="rhino" BUILD="legacy" OPTION="-require" - node_js: "0.10" env: BIN="ringo" BUILD="compat" - node_js: "0.10" env: BIN="ringo" BUILD="legacy" - node_js: "0.8" env: SAUCE_LABS=true BUILD="compat" - node_js: "0.8" env: SAUCE_LABS=true BUILD="modern" - node_js: "0.8" env: SAUCE_LABS=true BUILD="legacy" - node_js: "0.8" env: SAUCE_LABS=true BUILD="mobile" - node_js: "0.8" env: SAUCE_LABS=true BUILD="underscore" git: depth: 10 branches: only: - master before_install: - "([ $BUILD == 'legacy' ] || [ $BUILD == 'mobile' ] || [ $BUILD == 'modern' ]) && MAKE=true || true" - "([ $BUILD == 'compat' ] || [ $BUILD == 'legacy' ]) && COMPAT=true || true" - "[ $SAUCE_LABS != false ] && npm i ecstatic@\"~0.4.0\" request@\"~2.27.0\" sauce-tunnel@\"~1.1.0\" || true" - "[ $BIN == 'istanbul' ] && npm i -g coveralls@\"~2.5.0\" istanbul@\"~0.1.0\" || true" - "[ $BIN == 'narwhal' ] && wget https://github.com/280north/narwhal/archive/v0.3.2.zip && sudo unzip v0.3.2 -d /opt/ && rm v0.3.2.zip || true" - "[ $BIN == 'narwhal' ] && sudo ln -s /opt/narwhal-0.3.2/bin/narwhal /usr/local/bin/narwhal && sudo chmod +x /usr/local/bin/narwhal || true" - "[ $BIN == 'rhino' ] && sudo mkdir /opt/rhino-1.7R5 && sudo wget -O /opt/rhino-1.7R5/js.jar https://oss.sonatype.org/content/repositories/snapshots/org/mozilla/rhino/1.7R5-SNAPSHOT/rhino-1.7R5-20120629.144839-4.jar || true" - "[ $BIN == 'rhino' ] && echo -e '#!/bin/sh\\njava -jar /opt/rhino-1.7R5/js.jar $@' | sudo tee /usr/local/bin/rhino && sudo chmod +x /usr/local/bin/rhino || true" - "[ $BIN == 'ringo' ] && wget http://ringojs.org/downloads/ringojs-0.9.zip && sudo unzip ringojs-0.9 -d /opt && rm ringojs-0.9.zip || true" - "[ $BIN == 'ringo' ] && sudo ln -s /opt/ringojs-0.9/bin/ringo /usr/local/bin/ringo && sudo chmod +x /usr/local/bin/ringo || true" - "[ $MAKE != false ] && git clone --depth=10 --branch=master git://github.com/lodash/lodash-cli.git ./node_modules/lodash-cli || true" - "[ $MAKE != false ] && mkdir ./node_modules/lodash-cli/node_modules && cd ./node_modules/lodash-cli/node_modules/ && ln -s ../../../ ./lodash && cd ../ && npm i . && cd ../../ || true" - "[ $MAKE != false ] && node ./node_modules/lodash-cli/bin/lodash $BUILD -o ./dist/lodash.$BUILD.js || true" script: - "[ $BIN == 'istanbul' ] && $BIN cover -x \"**/vendor/**\" --report lcovonly ./test/test.js -- ./dist/lodash.js && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage || true" - "([ $SAUCE_LABS != false ] || [ $BUILD == false ]) && true || cd ./test" - "([ $SAUCE_LABS != false ] || [ $BUILD == false ]) && true || $BIN $OPTION ./test.js ../dist/lodash.$BUILD.js" - "([ $SAUCE_LABS != false ] || [ $BUILD == false ]) && true || $BIN $OPTION ./test.js ../dist/lodash.$BUILD.min.js" - "([ $SAUCE_LABS == false ] || [ $BUILD == 'underscore' ]) && true || node ./test/saucelabs.js runner=\"test/index.html?build=lodash-$BUILD\" tags=\"$BUILD,production\"" - "([ $SAUCE_LABS == false ] || [ $COMPAT == false ]) && true || node ./test/saucelabs.js runner=\"test/index.html?build=lodash-$BUILD\" tags=\"$BUILD,production,ie-compat\" compatMode=7" - "([ $SAUCE_LABS == false ] || [ $BUILD == 'underscore' ]) && true || node ./test/saucelabs.js runner=\"test/index.html?build=../dist/lodash.$BUILD.js\" tags=\"$BUILD,development\"" - "([ $SAUCE_LABS == false ] || [ $COMPAT == false ]) && true || node ./test/saucelabs.js runner=\"test/index.html?build=../dist/lodash.$BUILD.js\" tags=\"$BUILD,development,ie-compat\" compatMode=7" - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js name=\"backbone tests\" runner=\"test/backbone.html?build=lodash-$BUILD\" tags=\"$BUILD,production,backbone\"" - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js name=\"backbone tests\" runner=\"test/backbone.html?build=../dist/lodash.$BUILD.js\" tags=\"$BUILD,development,backbone\"" - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js name=\"underscore tests\" runner=\"test/underscore.html?build=lodash-$BUILD\" tags=\"$BUILD,production,underscore\"" - "[ $SAUCE_LABS == false ] && true || node ./test/saucelabs.js name=\"underscore tests\" runner=\"test/underscore.html?build=../dist/lodash.$BUILD.js\" tags=\"$BUILD,development,underscore\"" lodash-2.4.1/component.json0000644000175000017500000000051412247303154014030 0ustar ovdovd{ "name": "lodash", "repo": "lodash/lodash", "version": "2.4.1", "description": "A utility library delivering consistency, customization, performance, & extras.", "license": "MIT", "keywords": ["amd", "browser", "client", "functional", "server", "util"], "scripts": [ "index.js", "dist/lodash.compat.js" ] } lodash-2.4.1/perf/0000755000175000017500000000000012247303154012067 5ustar ovdovdlodash-2.4.1/perf/asset/0000755000175000017500000000000012247303154013206 5ustar ovdovdlodash-2.4.1/perf/asset/perf-ui.js0000644000175000017500000001457212247303154015124 0ustar ovdovd;(function(window) { 'use strict'; /** The base path of the builds */ var basePath = '../'; /** The Lo-Dash build to load */ var build = (build = /build=([^&]+)/.exec(location.search)) && decodeURIComponent(build[1]); /** The other library to load */ var other = (other = /other=([^&]+)/.exec(location.search)) && decodeURIComponent(other[1]); /** The `ui` object */ var ui = {}; /*--------------------------------------------------------------------------*/ /** * Registers an event listener on an element. * * @private * @param {Element} element The element. * @param {string} eventName The name of the event. * @param {Function} handler The event handler. * @returns {Element} The element. */ function addListener(element, eventName, handler) { if (typeof element.addEventListener != 'undefined') { element.addEventListener(eventName, handler, false); } else if (typeof element.attachEvent != 'undefined') { element.attachEvent('on' + eventName, handler); } } /*--------------------------------------------------------------------------*/ // initialize controls addListener(window, 'load', function() { function eventHandler(event) { var buildIndex = buildList.selectedIndex, otherIndex = otherList.selectedIndex, search = location.search.replace(/^\?|&?(?:build|other)=[^&]*&?/g, ''); if (event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } location.href = location.href.split('?')[0] + '?' + (search ? search + '&' : '') + 'build=' + (buildIndex < 0 ? build : buildList[buildIndex].value) + '&' + 'other=' + (otherIndex < 0 ? other : otherList[otherIndex].value); } var span1 = document.createElement('span'); span1.style.cssText = 'float:right'; span1.innerHTML = '' + ''; var span2 = document.createElement('span'); span2.style.cssText = 'float:right'; span2.innerHTML = '' + ''; var buildList = span1.lastChild, otherList = span2.lastChild, toolbar = document.getElementById('perf-toolbar'); toolbar.appendChild(span2); toolbar.appendChild(span1); buildList.selectedIndex = (function() { switch (build) { case 'lodash-compat': return 0; case 'lodash-legacy': return 1; case 'lodash-mobile': return 2; case 'lodash-underscore': return 4; case 'lodash-custom-dev': return 5; case 'lodash-custom': return 6; case 'lodash-modern': case null: return 3; } return -1; }()); otherList.selectedIndex = (function() { switch (other) { case 'underscore-dev': return 0; case 'lodash-compat': return 2; case 'lodash-legacy': return 3; case 'lodash-mobile': return 4; case 'lodash-modern': return 5; case 'lodash-underscore': return 6; case 'lodash-custom-dev': return 7; case 'lodash-custom': return 8; case 'underscore': case null: return 1; } return -1; }()); addListener(buildList, 'change', eventHandler); addListener(otherList, 'change', eventHandler); }); // expose Lo-Dash build file path ui.buildPath = (function() { var result; switch (build) { case 'lodash-compat': result = 'dist/lodash.compat.min.js'; break; case 'lodash-legacy': result = 'dist/lodash.legacy.min.js'; break; case 'lodash-mobile': result = 'dist/lodash.mobile.min.js'; break; case 'lodash-underscore': result = 'dist/lodash.underscore.min.js'; break; case 'lodash-custom-dev': result = 'lodash.custom.js'; break; case 'lodash-custom': result = 'lodash.custom.min.js'; break; case null: build = 'lodash-modern'; case 'lodash-modern': result = 'dist/lodash.min.js'; break; default: return build; } return basePath + result; }()); // expose other library file path ui.otherPath = (function() { var result; switch (other) { case 'lodash-compat': result = 'dist/lodash.compat.min.js'; break; case 'lodash-legacy': result = 'dist/lodash.legacy.min.js'; break; case 'lodash-mobile': result = 'dist/lodash.mobile.min.js'; break; case 'lodash-modern': result = 'dist/lodash.min.js'; break; case 'lodash-underscore': result = 'dist/lodash.underscore.min.js'; break; case 'lodash-custom-dev': result = 'lodash.custom.js'; break; case 'lodash-custom': result = 'lodash.custom.min.js'; break; case 'underscore-dev': result = 'vendor/underscore/underscore.js'; break; case null: other = 'underscore'; case 'underscore': result = 'vendor/underscore/underscore-min.js'; break; default: return other; } return basePath + result; }()); // expose `ui.urlParams` properties ui.urlParams = { 'build': build, 'other': other }; // expose `ui` window.ui = ui; }(this)); lodash-2.4.1/perf/perf.js0000644000175000017500000015715112247303154013373 0ustar ovdovd;(function(root) { /** Use a single "load" function */ var load = typeof require == 'function' ? require : root.load; /** The file path of the Lo-Dash file to test */ var filePath = (function() { var min = 0; var result = root.phantom ? phantom.args : (root.system ? (min = 1, system.args) : (root.process ? (min = 2, process.argv) : (root.arguments || [])) ); var last = result[result.length - 1]; result = (result.length > min && !/perf(?:\.js)?$/.test(last)) ? last : '../lodash.js'; try { result = require('fs').realpathSync(result); } catch(e) { } return result; }()); /** Load Lo-Dash */ var lodash = root.lodash || (root.lodash = ( lodash = load(filePath) || root._, lodash = lodash._ || lodash, lodash.noConflict() )); /** Load Benchmark.js */ var Benchmark = root.Benchmark || (root.Benchmark = ( Benchmark = load('../vendor/benchmark.js/benchmark.js') || root.Benchmark, Benchmark = Benchmark.Benchmark || Benchmark, Benchmark.runInContext(lodash.extend({}, root, { '_': lodash })) )); /** Load Underscore */ var _ = root._ || (root._ = ( _ = load('../vendor/underscore/underscore.js') || root._, _._ || _ )); /** Used to access the Firebug Lite panel (set by `run`) */ var fbPanel; /** Used to match path separators */ var rePathSeparator = /[\/\\]/; /** Used to detect primitive types */ var rePrimitive = /^(?:boolean|number|string|undefined)$/; /** Used to match RegExp special characters */ var reSpecialChars = /[.*+?^=!:${}()|[\]\/\\]/g; /** Used to score performance */ var score = { 'a': [], 'b': [] }; /** Used to queue benchmark suites */ var suites = []; /** Used to resolve a value's internal [[Class]] */ var toString = Object.prototype.toString; /** The `ui` object */ var ui = root.ui || (root.ui = { 'buildPath': basename(filePath, '.js'), 'otherPath': 'underscore' }); /** The Lo-Dash build basename */ var buildName = root.buildName = basename(ui.buildPath, '.js'); /** The other library basename */ var otherName = root.otherName = (function() { var result = basename(ui.otherPath, '.js'); return result + (result == buildName ? ' (2)' : ''); }()); /** Detect if in a browser environment */ var isBrowser = isHostType(root, 'document') && isHostType(root, 'navigator'); /** Detect Java environment */ var isJava = !isBrowser && /Java/.test(toString.call(root.java)); /** Add `console.log()` support for Narwhal, Rhino, and RingoJS */ var console = root.console || (root.console = { 'log': root.print }); /*--------------------------------------------------------------------------*/ /** * Gets the basename of the given `filePath`. If the file `extension` is passed, * it will be removed from the basename. * * @private * @param {string} path The file path to inspect. * @param {string} extension The extension to remove. * @returns {string} Returns the basename. */ function basename(filePath, extension) { var result = (filePath || '').split(rePathSeparator).pop(); return (arguments.length < 2) ? result : result.replace(RegExp(extension.replace(reSpecialChars, '\\$&') + '$'), ''); } /** * Computes the geometric mean (log-average) of an array of values. * See http://en.wikipedia.org/wiki/Geometric_mean#Relationship_with_arithmetic_mean_of_logarithms. * * @private * @param {Array} array The array of values. * @returns {number} The geometric mean. */ function getGeometricMean(array) { return Math.pow(Math.E, lodash.reduce(array, function(sum, x) { return sum + Math.log(x); }, 0) / array.length) || 0; } /** * Gets the Hz, i.e. operations per second, of `bench` adjusted for the * margin of error. * * @private * @param {Object} bench The benchmark object. * @returns {number} Returns the adjusted Hz. */ function getHz(bench) { var result = 1 / (bench.stats.mean + bench.stats.moe); return isFinite(result) ? result : 0; } /** * Host objects can return type values that are different from their actual * data type. The objects we are concerned with usually return non-primitive * types of "object", "function", or "unknown". * * @private * @param {*} object The owner of the property. * @param {string} property The property to check. * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`. */ function isHostType(object, property) { if (object == null) { return false; } var type = typeof object[property]; return !rePrimitive.test(type) && (type != 'object' || !!object[property]); } /** * Logs text to the console. * * @private * @param {string} text The text to log. */ function log(text) { console.log(text + ''); if (fbPanel) { // scroll the Firebug Lite panel down fbPanel.scrollTop = fbPanel.scrollHeight; } } /** * Runs all benchmark suites. * * @private (@public in the browser) */ function run() { fbPanel = (fbPanel = root.document && document.getElementById('FirebugUI')) && (fbPanel = (fbPanel = fbPanel.contentWindow || fbPanel.contentDocument).document || fbPanel) && fbPanel.getElementById('fbPanel1'); log('\nSit back and relax, this may take a while.'); suites[0].run({ 'async': !isJava }); } /*--------------------------------------------------------------------------*/ lodash.extend(Benchmark.Suite.options, { 'onStart': function() { log('\n' + this.name + ':'); }, 'onCycle': function(event) { log(event.target); }, 'onComplete': function() { for (var index = 0, length = this.length; index < length; index++) { var bench = this[index]; if (bench.error) { var errored = true; } } if (errored) { log('There was a problem, skipping...'); } else { var formatNumber = Benchmark.formatNumber, fastest = this.filter('fastest'), fastestHz = getHz(fastest[0]), slowest = this.filter('slowest'), slowestHz = getHz(slowest[0]), aHz = getHz(this[0]), bHz = getHz(this[1]); if (fastest.length > 1) { log('It\'s too close to call.'); aHz = bHz = slowestHz; } else { var percent = ((fastestHz / slowestHz) - 1) * 100; log( fastest[0].name + ' is ' + formatNumber(percent < 1 ? percent.toFixed(2) : Math.round(percent)) + '% faster.' ); } // add score adjusted for margin of error score.a.push(aHz); score.b.push(bHz); } // remove current suite from queue suites.shift(); if (suites.length) { // run next suite suites[0].run({ 'async': !isJava }); } else { var aMeanHz = getGeometricMean(score.a), bMeanHz = getGeometricMean(score.b), fastestMeanHz = Math.max(aMeanHz, bMeanHz), slowestMeanHz = Math.min(aMeanHz, bMeanHz), xFaster = fastestMeanHz / slowestMeanHz, percentFaster = formatNumber(Math.round((xFaster - 1) * 100)), message = 'is ' + percentFaster + '% ' + (xFaster == 1 ? '' : '(' + formatNumber(xFaster.toFixed(2)) + 'x) ') + 'faster than'; // report results if (aMeanHz >= bMeanHz) { log('\n' + buildName + ' ' + message + ' ' + otherName + '.'); } else { log('\n' + otherName + ' ' + message + ' ' + buildName + '.'); } } } }); /*--------------------------------------------------------------------------*/ lodash.extend(Benchmark.options, { 'async': true, 'setup': '\ var _ = global._,\ lodash = global.lodash,\ belt = this.name == buildName ? lodash : _;\ \ var index,\ date = new Date,\ limit = 20,\ regexp = /x/,\ object = {},\ objects = Array(limit),\ numbers = Array(limit),\ fourNumbers = [5, 25, 10, 30],\ nestedNumbers = [1, [2], [3, [[4]]]],\ nestedObjects = [{}, [{}], [{}, [[{}]]]],\ twoNumbers = [12, 23];\ \ for (index = 0; index < limit; index++) {\ numbers[index] = index;\ object["key" + index] = index;\ objects[index] = { "num": index };\ }\ \ if (typeof bind != "undefined") {\ var thisArg = { "name": "fred" };\ \ var func = function(greeting, punctuation) {\ return greeting + " " + this.name + (punctuation || ".");\ };\ \ var _boundNormal = _.bind(func, thisArg),\ _boundMultiple = _boundNormal,\ _boundPartial = _.bind(func, thisArg, "hi");\ \ var lodashBoundNormal = lodash.bind(func, thisArg),\ lodashBoundMultiple = lodashBoundNormal,\ lodashBoundPartial = lodash.bind(func, thisArg, "hi");\ \ for (index = 0; index < 10; index++) {\ _boundMultiple = _.bind(_boundMultiple, { "name": "fred" + index });\ lodashBoundMultiple = lodash.bind(lodashBoundMultiple, { "name": "fred" + index });\ }\ }\ \ if (typeof bindAll != "undefined") {\ var bindAllCount = -1,\ bindAllObjects = Array(this.count);\ \ var funcNames = belt.reject(belt.functions(belt).slice(0, 40), function(funcName) {\ return /^_/.test(funcName);\ });\ \ // potentially expensive\n\ for (index = 0; index < this.count; index++) {\ bindAllObjects[index] = belt.reduce(funcNames, function(object, funcName) {\ object[funcName] = belt[funcName];\ return object;\ }, {});\ }\ }\ if (typeof chaining != "undefined") {\ var _chaining = _.chain ? _(numbers).chain() : _(numbers),\ lodashChaining = lodash(numbers);\ }\ if (typeof compact != "undefined") {\ var uncompacted = numbers.slice();\ uncompacted[2] = false;\ uncompacted[6] = null;\ uncompacted[18] = "";\ }\ \ if (typeof countBy != "undefined" || typeof omit != "undefined") {\ var wordToNumber = {\ "one": 1,\ "two": 2,\ "three": 3,\ "four": 4,\ "five": 5,\ "six": 6,\ "seven": 7,\ "eight": 8,\ "nine": 9,\ "ten": 10,\ "eleven": 11,\ "twelve": 12,\ "thirteen": 13,\ "fourteen": 14,\ "fifteen": 15,\ "sixteen": 16,\ "seventeen": 17,\ "eighteen": 18,\ "nineteen": 19,\ "twenty": 20,\ "twenty-one": 21,\ "twenty-two": 22,\ "twenty-three": 23,\ "twenty-four": 24,\ "twenty-five": 25,\ "twenty-six": 26,\ "twenty-seven": 27,\ "twenty-eight": 28,\ "twenty-nine": 29,\ "thirty": 30,\ "thirty-one": 31,\ "thirty-two": 32,\ "thirty-three": 33,\ "thirty-four": 34,\ "thirty-five": 35,\ "thirty-six": 36,\ "thirty-seven": 37,\ "thirty-eight": 38,\ "thirty-nine": 39,\ "forty": 40\ };\ \ var words = belt.keys(wordToNumber).slice(0, limit);\ }\ \ if (typeof isEqual != "undefined") {\ var objectOfPrimitives = {\ "boolean": true,\ "number": 1,\ "string": "a"\ };\ \ var objectOfObjects = {\ "boolean": new Boolean(true),\ "number": new Number(1),\ "string": new String("a")\ };\ \ var object2 = {},\ objects2 = Array(limit),\ numbers2 = Array(limit),\ nestedNumbers2 = [1, [2], [3, [[4]]]],\ nestedNumbers3 = [1, [2], [5, [[6]]]],\ simpleObject = { "a": 1 },\ simpleObject2 = { "a": 2 },\ simpleObjects = [simpleObject],\ simpleObjects2 = [simpleObject2],\ twoNumbers2 = [18, 27];\ \ for (index = 0; index < limit; index++) {\ object2["key" + index] = index;\ objects2[index] = { "num": index };\ numbers2[index] = index;\ }\ }\ \ if (typeof multiArrays != "undefined") {\ var twentyValues = Array(20),\ twentyValues2 = Array(20),\ twentyFiveValues = Array(25),\ twentyFiveValues2 = Array(25),\ thirtyValues = Array(30),\ thirtyValues2 = Array(30),\ fortyValues = Array(40),\ fortyValues2 = Array(40),\ fiftyValues = Array(50),\ fiftyValues2 = Array(50),\ seventyFiveValues = Array(75),\ seventyFiveValues2 = Array(75),\ oneHundredValues = Array(100),\ oneHundredValues2 = Array(100),\ twoHundredValues = Array(200),\ twoHundredValues2 = Array(200),\ lowerChars = "abcdefghijklmnopqrstuvwxyz".split(""),\ upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");\ \ for (index = 0; index < 200; index++) {\ if (index < 15) {\ twentyValues[index] = lowerChars[index];\ twentyValues2[index] = upperChars[index];\ }\ if (index < 20) {\ twentyValues[index] =\ twentyValues2[index] = index;\ \ twentyFiveValues[index] = lowerChars[index];\ twentyFiveValues2[index] = upperChars[index];\ }\ if (index < 25) {\ twentyFiveValues[index] =\ twentyFiveValues2[index] = index;\ \ thirtyValues[index] =\ fortyValues[index] =\ fiftyValues[index] =\ seventyFiveValues[index] =\ oneHundredValues[index] =\ twoHundredValues[index] = lowerChars[index];\ \ thirtyValues2[index] =\ fortyValues2[index] =\ fiftyValues2[index] =\ seventyFiveValues2[index] =\ oneHundredValues2[index] =\ twoHundredValues2[index] = upperChars[index];\ }\ else {\ if (index < 30) {\ thirtyValues[index] =\ thirtyValues2[index] = index;\ }\ if (index < 40) {\ fortyValues[index] =\ fortyValues2[index] = index;\ }\ if (index < 50) {\ fiftyValues[index] =\ fiftyValues2[index] = index;\ }\ if (index < 75) {\ seventyFiveValues[index] =\ seventyFiveValues2[index] = index;\ }\ if (index < 100) {\ oneHundredValues[index] =\ oneHundredValues2[index] = index;\ }\ twoHundredValues[index] =\ twoHundredValues2[index] = index;\ }\ }\ }\ \ if (typeof partial != "undefined") {\ var func = function(greeting, punctuation) {\ return greeting + " fred" + (punctuation || ".");\ };\ \ var _partial = _.partial(func, "hi"),\ lodashPartial = lodash.partial(func, "hi");\ }\ \ if (typeof template != "undefined") {\ var tplData = {\ "header1": "Header1",\ "header2": "Header2",\ "header3": "Header3",\ "header4": "Header4",\ "header5": "Header5",\ "header6": "Header6",\ "list": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]\ };\ \ var tpl =\ "
" +\ "

<%= header1 %>

" +\ "

<%= header2 %>

" +\ "

<%= header3 %>

" +\ "

<%= header4 %>

" +\ "
<%= header5 %>
" +\ "
<%= header6 %>
" +\ "" +\ "
";\ \ var tplVerbose =\ "
" +\ "

<%= data.header1 %>

" +\ "

<%= data.header2 %>

" +\ "

<%= data.header3 %>

" +\ "

<%= data.header4 %>

" +\ "
<%= data.header5 %>
" +\ "
<%= data.header6 %>
" +\ "" +\ "
";\ \ var settingsObject = { "variable": "data" };\ \ var _tpl = _.template(tpl),\ _tplVerbose = _.template(tplVerbose, null, settingsObject);\ \ var lodashTpl = lodash.template(tpl),\ lodashTplVerbose = lodash.template(tplVerbose, null, settingsObject);\ }\ if (typeof where != "undefined") {\ var _findWhere = _.findWhere || _.find,\ lodashFindWhere = lodash.findWhere || lodash.find,\ whereObject = { "num": 9 };\ }\ if (typeof wrap != "undefined") {\ var add = function(a, b) {\ return a + b;\ };\ \ var average = function(func, a, b) {\ return (func(a, b) / 2).toFixed(2);\ };\ \ var _wrapped = _.wrap(add, average);\ lodashWrapped = lodash.wrap(add, average);\ }\ if (typeof zip != "undefined") {\ var unzipped = [["a", "b", "c"], [1, 2, 3], [true, false, true]];\ }' }); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_(...)` with a number') .add(buildName, '\ lodash(2)' ) .add(otherName, '\ _(2)' ) ); suites.push( Benchmark.Suite('`_(...)` with an array') .add(buildName, '\ lodash(numbers)' ) .add(otherName, '\ _(numbers)' ) ); suites.push( Benchmark.Suite('`_(...)` with an object') .add(buildName, '\ lodash(object)' ) .add(otherName, '\ _(object)' ) ); // avoid Underscore induced `OutOfMemoryError` in Rhino, Narwhal, and Ringo if (!isJava) { suites.push( Benchmark.Suite('`_(...).tap(...)`') .add(buildName, { 'fn': 'lodashChaining.tap(lodash.identity)', 'teardown': 'function chaining(){}' }) .add(otherName, { 'fn': '_chaining.tap(_.identity)', 'teardown': 'function chaining(){}' }) ); } /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.bind`') .add(buildName, { 'fn': 'lodash.bind(func, { "name": "fred" })', 'teardown': 'function bind(){}' }) .add(otherName, { 'fn': '_.bind(func, { "name": "fred" })', 'teardown': 'function bind(){}' }) ); suites.push( Benchmark.Suite('bound call with arguments') .add(buildName, { 'fn': 'lodashBoundNormal("hi", "!")', 'teardown': 'function bind(){}' }) .add(otherName, { 'fn': '_boundNormal("hi", "!")', 'teardown': 'function bind(){}' }) ); suites.push( Benchmark.Suite('bound and partially applied call with arguments') .add(buildName, { 'fn': 'lodashBoundPartial("!")', 'teardown': 'function bind(){}' }) .add(otherName, { 'fn': '_boundPartial("!")', 'teardown': 'function bind(){}' }) ); suites.push( Benchmark.Suite('bound multiple times') .add(buildName, { 'fn': 'lodashBoundMultiple()', 'teardown': 'function bind(){}' }) .add(otherName, { 'fn': '_boundMultiple()', 'teardown': 'function bind(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.bindAll` iterating arguments') .add(buildName, { 'fn': 'lodash.bindAll.apply(lodash, [bindAllObjects[++bindAllCount]].concat(funcNames))', 'teardown': 'function bindAll(){}' }) .add(otherName, { 'fn': '_.bindAll.apply(_, [bindAllObjects[++bindAllCount]].concat(funcNames))', 'teardown': 'function bindAll(){}' }) ); suites.push( Benchmark.Suite('`_.bindAll` iterating the `object`') .add(buildName, { 'fn': 'lodash.bindAll(bindAllObjects[++bindAllCount])', 'teardown': 'function bindAll(){}' }) .add(otherName, { 'fn': '_.bindAll(bindAllObjects[++bindAllCount])', 'teardown': 'function bindAll(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.clone` with an object') .add(buildName, '\ lodash.clone(object)' ) .add(otherName, '\ _.clone(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.compact`') .add(buildName, { 'fn': 'lodash.compact(uncompacted)', 'teardown': 'function compact(){}' }) .add(otherName, { 'fn': '_.compact(uncompacted)', 'teardown': 'function compact(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.contains` iterating an array') .add(buildName, '\ lodash.contains(numbers, limit - 1)' ) .add(otherName, '\ _.contains(numbers, limit - 1)' ) ); suites.push( Benchmark.Suite('`_.contains` iterating an object') .add(buildName, '\ lodash.contains(object, limit - 1)' ) .add(otherName, '\ _.contains(object, limit - 1)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.countBy` with `callback` iterating an array') .add(buildName, '\ lodash.countBy(numbers, function(num) { return num >> 1; })' ) .add(otherName, '\ _.countBy(numbers, function(num) { return num >> 1; })' ) ); suites.push( Benchmark.Suite('`_.countBy` with `property` name iterating an array') .add(buildName, { 'fn': 'lodash.countBy(words, "length")', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '_.countBy(words, "length")', 'teardown': 'function countBy(){}' }) ); suites.push( Benchmark.Suite('`_.countBy` with `callback` iterating an object') .add(buildName, { 'fn': 'lodash.countBy(wordToNumber, function(num) { return num >> 1; })', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '_.countBy(wordToNumber, function(num) { return num >> 1; })', 'teardown': 'function countBy(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.defaults`') .add(buildName, '\ lodash.defaults({ "key2": 2, "key6": 6, "key18": 18 }, object)' ) .add(otherName, '\ _.defaults({ "key2": 2, "key6": 6, "key18": 18 }, object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.difference`') .add(buildName, '\ lodash.difference(numbers, twoNumbers, fourNumbers)' ) .add(otherName, '\ _.difference(numbers, twoNumbers, fourNumbers)' ) ); suites.push( Benchmark.Suite('`_.difference` iterating 75 elements') .add(buildName, { 'fn': 'lodash.difference(seventyFiveValues, seventyFiveValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.difference(seventyFiveValues, seventyFiveValues2)', 'teardown': 'function multiArrays(){}' }) ); suites.push( Benchmark.Suite('`_.difference` iterating 200 elements') .add(buildName, { 'fn': 'lodash.difference(twoHundredValues, twoHundredValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.difference(twoHundredValues, twoHundredValues2)', 'teardown': 'function multiArrays(){}' }) ); suites.push( Benchmark.Suite('`_.difference` iterating 20 and 40 elements') .add(buildName, { 'fn': 'lodash.difference(twentyValues, fortyValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.difference(twentyValues, fortyValues2)', 'teardown': 'function multiArrays(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.each` iterating an array') .add(buildName, '\ var result = [];\ lodash.each(numbers, function(num) {\ result.push(num * 2);\ })' ) .add(otherName, '\ var result = [];\ _.each(numbers, function(num) {\ result.push(num * 2);\ })' ) ); suites.push( Benchmark.Suite('`_.each` iterating an array with `thisArg` (slow path)') .add(buildName, '\ var result = [];\ lodash.each(numbers, function(num, index) {\ result.push(num + this["key" + index]);\ }, object)' ) .add(otherName, '\ var result = [];\ _.each(numbers, function(num, index) {\ result.push(num + this["key" + index]);\ }, object)' ) ); suites.push( Benchmark.Suite('`_.each` iterating an object') .add(buildName, '\ var result = [];\ lodash.each(object, function(num) {\ result.push(num * 2);\ })' ) .add(otherName, '\ var result = [];\ _.each(object, function(num) {\ result.push(num * 2);\ })' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.every` iterating an array') .add(buildName, '\ lodash.every(numbers, function(num) {\ return num + "";\ })' ) .add(otherName, '\ _.every(numbers, function(num) {\ return num + "";\ })' ) ); suites.push( Benchmark.Suite('`_.every` iterating an object') .add(buildName, '\ lodash.every(object, function(num) {\ return num + "";\ })' ) .add(otherName, '\ _.every(object, function(num) {\ return num + "";\ })' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.extend`') .add(buildName, '\ lodash.extend({}, object)' ) .add(otherName, '\ _.extend({}, object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.filter` iterating an array') .add(buildName, '\ lodash.filter(numbers, function(num) {\ return num % 2;\ })' ) .add(otherName, '\ _.filter(numbers, function(num) {\ return num % 2;\ })' ) ); suites.push( Benchmark.Suite('`_.filter` iterating an array with `thisArg` (slow path)') .add(buildName, '\ lodash.filter(numbers, function(num, index) {\ return this["key" + index] % 2;\ }, object)' ) .add(otherName, '\ _.filter(numbers, function(num, index) {\ return this["key" + index] % 2;\ }, object)' ) ); suites.push( Benchmark.Suite('`_.filter` iterating an object') .add(buildName, '\ lodash.filter(object, function(num) {\ return num % 2\ })' ) .add(otherName, '\ _.filter(object, function(num) {\ return num % 2\ })' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.find` iterating an array') .add(buildName, '\ lodash.find(numbers, function(num) {\ return num === (limit - 1);\ })' ) .add(otherName, '\ _.find(numbers, function(num) {\ return num === (limit - 1);\ })' ) ); suites.push( Benchmark.Suite('`_.find` iterating an object') .add(buildName, '\ lodash.find(object, function(value, key) {\ return /\D9$/.test(key);\ })' ) .add(otherName, '\ _.find(object, function(value, key) {\ return /\D9$/.test(key);\ })' ) ); // avoid Underscore induced `OutOfMemoryError` in Rhino, Narwhal, and Ringo if (!isJava) { suites.push( Benchmark.Suite('`_.find` with `properties`') .add(buildName, { 'fn': 'lodashFindWhere(objects, whereObject)', 'teardown': 'function where(){}' }) .add(otherName, { 'fn': '_findWhere(objects, whereObject)', 'teardown': 'function where(){}' }) ); } /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.flatten`') .add(buildName, '\ lodash.flatten(nestedNumbers)' ) .add(otherName, '\ _.flatten(nestedNumbers)' ) ); suites.push( Benchmark.Suite('`_.flatten` with objects') .add(buildName, '\ lodash.flatten(nestedObjects)' ) .add(otherName, '\ _.flatten(nestedObjects)' ) ); suites.push( Benchmark.Suite('`_.flatten` with `shallow`') .add(buildName, '\ lodash.flatten(nestedNumbers, true)' ) .add(otherName, '\ _.flatten(nestedNumbers, true)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.functions`') .add(buildName, '\ lodash.functions(lodash)' ) .add(otherName, '\ _.functions(lodash)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.groupBy` with `callback` iterating an array') .add(buildName, '\ lodash.groupBy(numbers, function(num) { return num >> 1; })' ) .add(otherName, '\ _.groupBy(numbers, function(num) { return num >> 1; })' ) ); suites.push( Benchmark.Suite('`_.groupBy` with `property` name iterating an array') .add(buildName, { 'fn': 'lodash.groupBy(words, "length")', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '_.groupBy(words, "length")', 'teardown': 'function countBy(){}' }) ); suites.push( Benchmark.Suite('`_.groupBy` with `callback` iterating an object') .add(buildName, { 'fn': 'lodash.groupBy(wordToNumber, function(num) { return num >> 1; })', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '_.groupBy(wordToNumber, function(num) { return num >> 1; })', 'teardown': 'function countBy(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.indexBy` with `callback` iterating an array') .add(buildName, '\ lodash.indexBy(numbers, function(num) { return num >> 1; })' ) .add(otherName, '\ _.indexBy(numbers, function(num) { return num >> 1; })' ) ); suites.push( Benchmark.Suite('`_.indexBy` with `property` name iterating an array') .add(buildName, { 'fn': 'lodash.indexBy(words, "length")', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '_.indexBy(words, "length")', 'teardown': 'function countBy(){}' }) ); suites.push( Benchmark.Suite('`_.indexBy` with `callback` iterating an object') .add(buildName, { 'fn': 'lodash.indexBy(wordToNumber, function(num) { return num >> 1; })', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '_.indexBy(wordToNumber, function(num) { return num >> 1; })', 'teardown': 'function countBy(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.indexOf`') .add(buildName, { 'fn': 'lodash.indexOf(twoHundredValues, 199)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.indexOf(twoHundredValues, 199)', 'teardown': 'function multiArrays(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.intersection`') .add(buildName, '\ lodash.intersection(numbers, twoNumbers, fourNumbers)' ) .add(otherName, '\ _.intersection(numbers, twoNumbers, fourNumbers)' ) ); suites.push( Benchmark.Suite('`_.intersection` iterating 75 elements') .add(buildName, { 'fn': 'lodash.intersection(seventyFiveValues, seventyFiveValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.intersection(seventyFiveValues, seventyFiveValues2)', 'teardown': 'function multiArrays(){}' }) ); suites.push( Benchmark.Suite('`_.intersection` iterating 200 elements') .add(buildName, { 'fn': 'lodash.intersection(twoHundredValues, twoHundredValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.intersection(twoHundredValues, twoHundredValues2)', 'teardown': 'function multiArrays(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.invert`') .add(buildName, '\ lodash.invert(object)' ) .add(otherName, '\ _.invert(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.invoke` iterating an array') .add(buildName, '\ lodash.invoke(numbers, "toFixed", "2")' ) .add(otherName, '\ _.invoke(numbers, "toFixed", "2")' ) ); suites.push( Benchmark.Suite('`_.invoke` with a function for `methodName` iterating an array') .add(buildName, '\ lodash.invoke(numbers, String.prototype.split, "")' ) .add(otherName, '\ _.invoke(numbers, String.prototype.split, "")' ) ); suites.push( Benchmark.Suite('`_.invoke` iterating an object') .add(buildName, '\ lodash.invoke(object, "toFixed", "2")' ) .add(otherName, '\ _.invoke(object, "toFixed", "2")' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.isEqual` comparing primitives and objects (edge case)') .add(buildName, { 'fn': 'lodash.isEqual(objectOfPrimitives, objectOfObjects)', 'teardown': 'function isEqual(){}' }) .add(otherName, { 'fn': '_.isEqual(objectOfPrimitives, objectOfObjects)', 'teardown': 'function isEqual(){}' }) ); suites.push( Benchmark.Suite('`_.isEqual` comparing arrays') .add(buildName, { 'fn': '\ lodash.isEqual(numbers, numbers2);\ lodash.isEqual(twoNumbers, twoNumbers2)', 'teardown': 'function isEqual(){}' }) .add(otherName, { 'fn': '\ _.isEqual(numbers, numbers2);\ _.isEqual(twoNumbers, twoNumbers2)', 'teardown': 'function isEqual(){}' }) ); suites.push( Benchmark.Suite('`_.isEqual` comparing nested arrays') .add(buildName, { 'fn': '\ lodash.isEqual(nestedNumbers, nestedNumbers2);\ lodash.isEqual(nestedNumbers2, nestedNumbers3)', 'teardown': 'function isEqual(){}' }) .add(otherName, { 'fn': '\ _.isEqual(nestedNumbers, nestedNumbers2);\ _.isEqual(nestedNumbers2, nestedNumbers3)', 'teardown': 'function isEqual(){}' }) ); suites.push( Benchmark.Suite('`_.isEqual` comparing arrays of objects') .add(buildName, { 'fn': '\ lodash.isEqual(objects, objects2);\ lodash.isEqual(simpleObjects, simpleObjects2)', 'teardown': 'function isEqual(){}' }) .add(otherName, { 'fn': '\ _.isEqual(objects, objects2);\ _.isEqual(simpleObjects, simpleObjects2)', 'teardown': 'function isEqual(){}' }) ); suites.push( Benchmark.Suite('`_.isEqual` comparing objects') .add(buildName, { 'fn': '\ lodash.isEqual(object, object2);\ lodash.isEqual(simpleObject, simpleObject2)', 'teardown': 'function isEqual(){}' }) .add(otherName, { 'fn': '\ _.isEqual(object, object2);\ _.isEqual(simpleObject, simpleObject2)', 'teardown': 'function isEqual(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.isArguments`, `_.isDate`, `_.isFunction`, `_.isNumber`, `_.isObject`, `_.isRegExp`') .add(buildName, '\ lodash.isArguments(arguments);\ lodash.isArguments(object);\ lodash.isDate(date);\ lodash.isDate(object);\ lodash.isFunction(lodash);\ lodash.isFunction(object);\ lodash.isNumber(1);\ lodash.isNumber(object);\ lodash.isObject(object);\ lodash.isObject(1);\ lodash.isRegExp(regexp);\ lodash.isRegExp(object)' ) .add(otherName, '\ _.isArguments(arguments);\ _.isArguments(object);\ _.isDate(date);\ _.isDate(object);\ _.isFunction(_);\ _.isFunction(object);\ _.isNumber(1);\ _.isNumber(object);\ _.isObject(object);\ _.isObject(1);\ _.isRegExp(regexp);\ _.isRegExp(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.keys` (uses native `Object.keys` if available)') .add(buildName, '\ lodash.keys(object)' ) .add(otherName, '\ _.keys(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.lastIndexOf`') .add(buildName, '\ lodash.lastIndexOf(numbers, 9)' ) .add(otherName, '\ _.lastIndexOf(numbers, 9)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.map` iterating an array') .add(buildName, '\ lodash.map(objects, function(value) {\ return value.num;\ })' ) .add(otherName, '\ _.map(objects, function(value) {\ return value.num;\ })' ) ); suites.push( Benchmark.Suite('`_.map` with `thisArg` iterating an array (slow path)') .add(buildName, '\ lodash.map(objects, function(value, index) {\ return this["key" + index] + value.num;\ }, object)' ) .add(otherName, '\ _.map(objects, function(value, index) {\ return this["key" + index] + value.num;\ }, object)' ) ); suites.push( Benchmark.Suite('`_.map` iterating an object') .add(buildName, '\ lodash.map(object, function(value) {\ return value;\ })' ) .add(otherName, '\ _.map(object, function(value) {\ return value;\ })' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.max`') .add(buildName, '\ lodash.max(numbers)' ) .add(otherName, '\ _.max(numbers)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.min`') .add(buildName, '\ lodash.min(numbers)' ) .add(otherName, '\ _.min(numbers)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.omit` iterating 20 properties, omitting 2 keys') .add(buildName, '\ lodash.omit(object, "key6", "key13")' ) .add(otherName, '\ _.omit(object, "key6", "key13")' ) ); suites.push( Benchmark.Suite('`_.omit` iterating 40 properties, omitting 20 keys') .add(buildName, { 'fn': 'lodash.omit(wordToNumber, words)', 'teardown': 'function omit(){}' }) .add(otherName, { 'fn': '_.omit(wordToNumber, words)', 'teardown': 'function omit(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.pairs`') .add(buildName, '\ lodash.pairs(object)' ) .add(otherName, '\ _.pairs(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.partial`') .add(buildName, { 'fn': 'lodash.partial(func, "hi")', 'teardown': 'function partial(){}' }) .add(otherName, { 'fn': '_.partial(func, "hi")', 'teardown': 'function partial(){}' }) ); suites.push( Benchmark.Suite('partially applied call with arguments') .add(buildName, { 'fn': 'lodashPartial("!")', 'teardown': 'function partial(){}' }) .add(otherName, { 'fn': '_partial("!")', 'teardown': 'function partial(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.pick`') .add(buildName, '\ lodash.pick(object, "key6", "key13")' ) .add(otherName, '\ _.pick(object, "key6", "key13")' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.pluck`') .add(buildName, '\ lodash.pluck(objects, "num")' ) .add(otherName, '\ _.pluck(objects, "num")' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.reduce` iterating an array') .add(buildName, '\ lodash.reduce(numbers, function(result, value, index) {\ result[index] = value;\ return result;\ }, {})' ) .add(otherName, '\ _.reduce(numbers, function(result, value, index) {\ result[index] = value;\ return result;\ }, {})' ) ); suites.push( Benchmark.Suite('`_.reduce` iterating an object') .add(buildName, '\ lodash.reduce(object, function(result, value, key) {\ result.push(key, value);\ return result;\ }, [])' ) .add(otherName, '\ _.reduce(object, function(result, value, key) {\ result.push(key, value);\ return result;\ }, [])' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.reduceRight` iterating an array') .add(buildName, '\ lodash.reduceRight(numbers, function(result, value, index) {\ result[index] = value;\ return result;\ }, {})' ) .add(otherName, '\ _.reduceRight(numbers, function(result, value, index) {\ result[index] = value;\ return result;\ }, {})' ) ); suites.push( Benchmark.Suite('`_.reduceRight` iterating an object') .add(buildName, '\ lodash.reduceRight(object, function(result, value, key) {\ result.push(key, value);\ return result;\ }, [])' ) .add(otherName, '\ _.reduceRight(object, function(result, value, key) {\ result.push(key, value);\ return result;\ }, [])' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.reject` iterating an array') .add(buildName, '\ lodash.reject(numbers, function(num) {\ return num % 2;\ })' ) .add(otherName, '\ _.reject(numbers, function(num) {\ return num % 2;\ })' ) ); suites.push( Benchmark.Suite('`_.reject` iterating an array with `thisArg` (slow path)') .add(buildName, '\ lodash.reject(numbers, function(num, index) {\ return this["key" + index] % 2;\ }, object)' ) .add(otherName, '\ _.reject(numbers, function(num, index) {\ return this["key" + index] % 2;\ }, object)' ) ); suites.push( Benchmark.Suite('`_.reject` iterating an object') .add(buildName, '\ lodash.reject(object, function(num) {\ return num % 2\ })' ) .add(otherName, '\ _.reject(object, function(num) {\ return num % 2\ })' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.sample` with an `n`') .add(buildName, '\ lodash.sample(numbers, limit / 2)' ) .add(otherName, '\ _.sample(numbers, limit / 2)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.shuffle`') .add(buildName, '\ lodash.shuffle(numbers)' ) .add(otherName, '\ _.shuffle(numbers)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.size` with an object') .add(buildName, '\ lodash.size(object)' ) .add(otherName, '\ _.size(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.some` iterating an array') .add(buildName, '\ lodash.some(numbers, function(num) {\ return num == (limit - 1);\ })' ) .add(otherName, '\ _.some(numbers, function(num) {\ return num == (limit - 1);\ })' ) ); suites.push( Benchmark.Suite('`_.some` with `thisArg` iterating an array (slow path)') .add(buildName, '\ lodash.some(objects, function(value, index) {\ return this["key" + index] == (limit - 1);\ }, object)' ) .add(otherName, '\ _.some(objects, function(value, index) {\ return this["key" + index] == (limit - 1);\ }, object)' ) ); suites.push( Benchmark.Suite('`_.some` iterating an object') .add(buildName, '\ lodash.some(object, function(num) {\ return num == (limit - 1);\ })' ) .add(otherName, '\ _.some(object, function(num) {\ return num == (limit - 1);\ })' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.sortBy` with `callback`') .add(buildName, '\ lodash.sortBy(numbers, function(num) { return Math.sin(num); })' ) .add(otherName, '\ _.sortBy(numbers, function(num) { return Math.sin(num); })' ) ); suites.push( Benchmark.Suite('`_.sortBy` with `callback` and `thisArg` (slow path)') .add(buildName, '\ lodash.sortBy(numbers, function(num) { return this.sin(num); }, Math)' ) .add(otherName, '\ _.sortBy(numbers, function(num) { return this.sin(num); }, Math)' ) ); suites.push( Benchmark.Suite('`_.sortBy` with `property` name') .add(buildName, { 'fn': 'lodash.sortBy(words, "length")', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '_.sortBy(words, "length")', 'teardown': 'function countBy(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.sortedIndex` with `callback`') .add(buildName, { 'fn': '\ lodash.sortedIndex(words, "twenty-five", function(value) {\ return wordToNumber[value];\ })', 'teardown': 'function countBy(){}' }) .add(otherName, { 'fn': '\ _.sortedIndex(words, "twenty-five", function(value) {\ return wordToNumber[value];\ })', 'teardown': 'function countBy(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.template` (slow path)') .add(buildName, { 'fn': 'lodash.template(tpl, tplData)', 'teardown': 'function template(){}' }) .add(otherName, { 'fn': '_.template(tpl, tplData)', 'teardown': 'function template(){}' }) ); suites.push( Benchmark.Suite('compiled template') .add(buildName, { 'fn': 'lodashTpl(tplData)', 'teardown': 'function template(){}' }) .add(otherName, { 'fn': '_tpl(tplData)', 'teardown': 'function template(){}' }) ); suites.push( Benchmark.Suite('compiled template without a with-statement') .add(buildName, { 'fn': 'lodashTplVerbose(tplData)', 'teardown': 'function template(){}' }) .add(otherName, { 'fn': '_tplVerbose(tplData)', 'teardown': 'function template(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.times`') .add(buildName, '\ var result = [];\ lodash.times(limit, function(n) { result.push(n); })' ) .add(otherName, '\ var result = [];\ _.times(limit, function(n) { result.push(n); })' ) ); suites.push( Benchmark.Suite('`_.times` with `thisArg`') .add(buildName, '\ var result = [];\ lodash.times(limit, function(n) { result.push(this.sin(n)); }, Math)' ) .add(otherName, '\ var result = [];\ _.times(limit, function(n) { result.push(this.sin(n)); }, Math)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.toArray` with an array (edge case)') .add(buildName, '\ lodash.toArray(numbers)' ) .add(otherName, '\ _.toArray(numbers)' ) ); suites.push( Benchmark.Suite('`_.toArray` with an object') .add(buildName, '\ lodash.toArray(object)' ) .add(otherName, '\ _.toArray(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.union`') .add(buildName, '\ lodash.union(numbers, twoNumbers, fourNumbers)' ) .add(otherName, '\ _.union(numbers, twoNumbers, fourNumbers)' ) ); suites.push( Benchmark.Suite('`_.union` iterating an array of 75 elements') .add(buildName, { 'fn': 'lodash.union(twentyFiveValues, fiftyValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.union(twentyFiveValues, fiftyValues2)', 'teardown': 'function multiArrays(){}' }) ); suites.push( Benchmark.Suite('`_.union` iterating an array of 200 elements') .add(buildName, { 'fn': 'lodash.union(oneHundredValues, oneHundredValues2)', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.union(oneHundredValues, oneHundredValues2)', 'teardown': 'function multiArrays(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.uniq`') .add(buildName, '\ lodash.uniq(numbers.concat(twoNumbers, fourNumbers))' ) .add(otherName, '\ _.uniq(numbers.concat(twoNumbers, fourNumbers))' ) ); suites.push( Benchmark.Suite('`_.uniq` with `callback`') .add(buildName, '\ lodash.uniq(numbers.concat(twoNumbers, fourNumbers), function(num) {\ return num % 2;\ })' ) .add(otherName, '\ _.uniq(numbers.concat(twoNumbers, fourNumbers), function(num) {\ return num % 2;\ })' ) ); suites.push( Benchmark.Suite('`_.uniq` iterating an array of 75 elements') .add(buildName, { 'fn': 'lodash.uniq(twentyFiveValues.concat(fiftyValues2))', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.uniq(twentyFiveValues.concat(fiftyValues2))', 'teardown': 'function multiArrays(){}' }) ); suites.push( Benchmark.Suite('`_.uniq` iterating an array of 200 elements') .add(buildName, { 'fn': 'lodash.uniq(oneHundredValues.concat(oneHundredValues2))', 'teardown': 'function multiArrays(){}' }) .add(otherName, { 'fn': '_.uniq(oneHundredValues.concat(oneHundredValues2))', 'teardown': 'function multiArrays(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.values`') .add(buildName, '\ lodash.values(object)' ) .add(otherName, '\ _.values(object)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.where`') .add(buildName, { 'fn': 'lodash.where(objects, whereObject)', 'teardown': 'function where(){}' }) .add(otherName, { 'fn': '_.where(objects, whereObject)', 'teardown': 'function where(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.without`') .add(buildName, '\ lodash.without(numbers, 9, 12, 14, 15)' ) .add(otherName, '\ _.without(numbers, 9, 12, 14, 15)' ) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.wrap` result called') .add(buildName, { 'fn': 'lodashWrapped(2, 5)', 'teardown': 'function wrap(){}' }) .add(otherName, { 'fn': '_wrapped(2, 5)', 'teardown': 'function wrap(){}' }) ); /*--------------------------------------------------------------------------*/ suites.push( Benchmark.Suite('`_.zip`') .add(buildName, { 'fn': 'lodash.zip.apply(lodash, unzipped)', 'teardown': 'function zip(){}' }) .add(otherName, { 'fn': '_.zip.apply(_, unzipped)', 'teardown': 'function zip(){}' }) ); /*--------------------------------------------------------------------------*/ if (Benchmark.platform + '') { log(Benchmark.platform); } // in the browser, expose `run` to be called later if (root.document && !root.phantom) { root.run = run; } else { run(); } }(typeof global == 'object' && global || this)); lodash-2.4.1/perf/run-perf.sh0000755000175000017500000000064212247303154014166 0ustar ovdovdcd "$(dirname "$0")" echo "Running performance suite in node..." node perf.js ../dist/lodash.js && node perf.js ../dist/lodash.min.js for cmd in rhino "rhino -require" narwhal ringo phantomjs; do echo "" echo "Running performance suite in $cmd..." $cmd perf.js ../dist/lodash.compat.js && $cmd perf.js ../dist/lodash.compat.min.js done echo "" echo "Running performance suite in a browser..." open index.html lodash-2.4.1/perf/index.html0000644000175000017500000000447112247303154014072 0ustar ovdovd Lo-Dash Performance Suite
lodash-2.4.1/lodash.js0000644000175000017500000073757112247303154012767 0ustar ovdovd/** * @license * Lo-Dash 2.4.1 * Copyright 2012-2013 The Dojo Foundation * Based on Underscore.js 1.5.2 * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license */ ;(function() { /** Used as a safe reference for `undefined` in pre ES5 environments */ var undefined; /** Used to pool arrays and objects used internally */ var arrayPool = [], objectPool = []; /** Used to generate unique IDs */ var idCounter = 0; /** Used internally to indicate various things */ var indicatorObject = {}; /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */ var keyPrefix = +new Date + ''; /** Used as the size when optimizations are enabled for large arrays */ var largeArraySize = 75; /** Used as the max size of the `arrayPool` and `objectPool` */ var maxPoolSize = 40; /** Used to detect and test whitespace */ var whitespace = ( // whitespace ' \t\x0B\f\xA0\ufeff' + // line terminators '\n\r\u2028\u2029' + // unicode category "Zs" space separators '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000' ); /** 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 ES6 template delimiters * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals */ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; /** Used to match regexp flags from their coerced string values */ var reFlags = /\w*$/; /** Used to detected named functions */ var reFuncName = /^\s*function[ \n\r\t]+\w/; /** Used to match "interpolate" template delimiters */ var reInterpolate = /<%=([\s\S]+?)%>/g; /** Used to match leading whitespace and zeros to be removed */ var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)'); /** Used to ensure capturing order of template delimiters */ var reNoMatch = /($^)/; /** Used to detect functions containing a `this` reference */ var reThis = /\bthis\b/; /** Used to match unescaped characters in compiled string literals */ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; /** Used to assign default `context` object properties */ var contextProps = [ 'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object', 'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN', 'parseInt', 'setTimeout' ]; /** Used to fix the JScript [[DontEnum]] bug */ var shadowedProps = [ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf' ]; /** Used to make template sourceURLs easier to identify */ var templateCounter = 0; /** `Object#toString` result shortcuts */ var argsClass = '[object Arguments]', arrayClass = '[object Array]', boolClass = '[object Boolean]', dateClass = '[object Date]', errorClass = '[object Error]', funcClass = '[object Function]', numberClass = '[object Number]', objectClass = '[object Object]', regexpClass = '[object RegExp]', stringClass = '[object String]'; /** Used to identify object classifications that `_.clone` supports */ var cloneableClasses = {}; cloneableClasses[funcClass] = false; cloneableClasses[argsClass] = cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] = cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; /** Used as an internal `_.debounce` options object */ var debounceOptions = { 'leading': false, 'maxWait': 0, 'trailing': false }; /** Used as the property descriptor for `__bindData__` */ var descriptor = { 'configurable': false, 'enumerable': false, 'value': null, 'writable': false }; /** Used as the data object for `iteratorTemplate` */ var iteratorData = { 'args': '', 'array': null, 'bottom': '', 'firstArg': '', 'init': '', 'keys': null, 'loop': '', 'shadowedProps': null, 'support': null, 'top': '', 'useHas': false }; /** 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 }; /** Used to escape characters for inclusion in compiled string literals */ var stringEscapes = { '\\': '\\', "'": "'", '\n': 'n', '\r': 'r', '\t': 't', '\u2028': 'u2028', '\u2029': 'u2029' }; /** Used as a reference to the global object */ var root = (objectTypes[typeof window] && window) || this; /** Detect free variable `exports` */ var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; /** Detect free variable `module` */ var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports` */ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */ var freeGlobal = objectTypes[typeof global] && global; if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { root = freeGlobal; } /*--------------------------------------------------------------------------*/ /** * The base implementation of `_.indexOf` without support for binary searches * or `fromIndex` constraints. * * @private * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {number} [fromIndex=0] The index to search from. * @returns {number} Returns the index of the matched value or `-1`. */ function baseIndexOf(array, value, fromIndex) { var index = (fromIndex || 0) - 1, length = array ? array.length : 0; while (++index < length) { if (array[index] === value) { return index; } } return -1; } /** * An implementation of `_.contains` for cache objects that mimics the return * signature of `_.indexOf` by returning `0` if the value is found, else `-1`. * * @private * @param {Object} cache The cache object to inspect. * @param {*} value The value to search for. * @returns {number} Returns `0` if `value` is found, else `-1`. */ function cacheIndexOf(cache, value) { var type = typeof value; cache = cache.cache; if (type == 'boolean' || value == null) { return cache[value] ? 0 : -1; } if (type != 'number' && type != 'string') { type = 'object'; } var key = type == 'number' ? value : keyPrefix + value; cache = (cache = cache[type]) && cache[key]; return type == 'object' ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1) : (cache ? 0 : -1); } /** * Adds a given value to the corresponding cache object. * * @private * @param {*} value The value to add to the cache. */ function cachePush(value) { var cache = this.cache, type = typeof value; if (type == 'boolean' || value == null) { cache[value] = true; } else { if (type != 'number' && type != 'string') { type = 'object'; } var key = type == 'number' ? value : keyPrefix + value, typeCache = cache[type] || (cache[type] = {}); if (type == 'object') { (typeCache[key] || (typeCache[key] = [])).push(value); } else { typeCache[key] = true; } } } /** * Used by `_.max` and `_.min` as the default callback when a given * collection is a string value. * * @private * @param {string} value The character to inspect. * @returns {number} Returns the code unit of given character. */ function charAtCallback(value) { return value.charCodeAt(0); } /** * Used by `sortBy` to compare transformed `collection` elements, 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 ac = a.criteria, bc = b.criteria, index = -1, length = ac.length; while (++index < length) { var value = ac[index], other = bc[index]; if (value !== other) { if (value > other || typeof value == 'undefined') { return 1; } if (value < other || typeof other == 'undefined') { return -1; } } } // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications // that causes it, under certain circumstances, to return the same value for // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247 // // This also ensures a stable sort in V8 and other engines. // See http://code.google.com/p/v8/issues/detail?id=90 return a.index - b.index; } /** * Creates a cache object to optimize linear searches of large arrays. * * @private * @param {Array} [array=[]] The array to search. * @returns {null|Object} Returns the cache object or `null` if caching should not be used. */ function createCache(array) { var index = -1, length = array.length, first = array[0], mid = array[(length / 2) | 0], last = array[length - 1]; if (first && typeof first == 'object' && mid && typeof mid == 'object' && last && typeof last == 'object') { return false; } var cache = getObject(); cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false; var result = getObject(); result.array = array; result.cache = cache; result.push = cachePush; while (++index < length) { result.push(array[index]); } return result; } /** * 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]; } /** * Gets an array from the array pool or creates a new one if the pool is empty. * * @private * @returns {Array} The array from the pool. */ function getArray() { return arrayPool.pop() || []; } /** * Gets an object from the object pool or creates a new one if the pool is empty. * * @private * @returns {Object} The object from the pool. */ function getObject() { return objectPool.pop() || { 'array': null, 'cache': null, 'criteria': null, 'false': false, 'index': 0, 'null': false, 'number': null, 'object': null, 'push': null, 'string': null, 'true': false, 'undefined': false, 'value': null }; } /** * Checks if `value` is a DOM node in IE < 9. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`. */ function isNode(value) { // IE < 9 presents DOM nodes as `Object` objects except they have `toString` // methods that are `typeof` "string" and still can coerce nodes to strings return typeof value.toString != 'function' && typeof (value + '') == 'string'; } /** * Releases the given array back to the array pool. * * @private * @param {Array} [array] The array to release. */ function releaseArray(array) { array.length = 0; if (arrayPool.length < maxPoolSize) { arrayPool.push(array); } } /** * Releases the given object back to the object pool. * * @private * @param {Object} [object] The object to release. */ function releaseObject(object) { var cache = object.cache; if (cache) { releaseObject(cache); } object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null; if (objectPool.length < maxPoolSize) { objectPool.push(object); } } /** * Slices the `collection` from the `start` index up to, but not including, * the `end` index. * * Note: This function is used instead of `Array#slice` to support node lists * in IE < 9 and to ensure dense arrays are returned. * * @private * @param {Array|Object|string} collection The collection to slice. * @param {number} start The start index. * @param {number} end The end index. * @returns {Array} Returns the new array. */ function slice(array, start, end) { start || (start = 0); if (typeof end == 'undefined') { end = array ? array.length : 0; } var index = -1, length = end - start || 0, result = Array(length < 0 ? 0 : length); while (++index < length) { result[index] = array[start + index]; } return result; } /*--------------------------------------------------------------------------*/ /** * Create a new `lodash` function using the given context object. * * @static * @memberOf _ * @category Utilities * @param {Object} [context=root] The context object. * @returns {Function} Returns the `lodash` function. */ function runInContext(context) { // Avoid issues with some ES3 environments that attempt to use values, named // after built-in constructors like `Object`, for the creation of literals. // ES5 clears this up by stating that literals must use built-in constructors. // See http://es5.github.io/#x11.1.5. context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; /** Native constructor references */ var Array = context.Array, Boolean = context.Boolean, Date = context.Date, Error = context.Error, Function = context.Function, Math = context.Math, Number = context.Number, Object = context.Object, RegExp = context.RegExp, String = context.String, TypeError = context.TypeError; /** * Used for `Array` method references. * * Normally `Array.prototype` would suffice, however, using an array literal * avoids issues in Narwhal. */ var arrayRef = []; /** Used for native method references */ var errorProto = Error.prototype, objectProto = Object.prototype, stringProto = String.prototype; /** Used to restore the original `_` reference in `noConflict` */ var oldDash = context._; /** Used to resolve the internal [[Class]] of values */ var toString = objectProto.toString; /** Used to detect if a method is native */ var reNative = RegExp('^' + String(toString) .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') .replace(/toString| for [^\]]+/g, '.*?') + '$' ); /** Native method shortcuts */ var ceil = Math.ceil, clearTimeout = context.clearTimeout, floor = Math.floor, fnToString = Function.prototype.toString, getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, hasOwnProperty = objectProto.hasOwnProperty, push = arrayRef.push, propertyIsEnumerable = objectProto.propertyIsEnumerable, setTimeout = context.setTimeout, splice = arrayRef.splice, unshift = arrayRef.unshift; /** Used to set meta data on functions */ var defineProperty = (function() { // IE 8 only accepts DOM elements try { var o = {}, func = isNative(func = Object.defineProperty) && func, result = func(o, o, o) && func; } catch(e) { } return result; }()); /* Native method shortcuts for methods with the same name as other `lodash` methods */ var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, nativeIsFinite = context.isFinite, nativeIsNaN = context.isNaN, nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys, nativeMax = Math.max, nativeMin = Math.min, nativeParseInt = context.parseInt, nativeRandom = Math.random; /** Used to lookup a built-in constructor by [[Class]] */ var ctorByClass = {}; ctorByClass[arrayClass] = Array; ctorByClass[boolClass] = Boolean; ctorByClass[dateClass] = Date; ctorByClass[funcClass] = Function; ctorByClass[objectClass] = Object; ctorByClass[numberClass] = Number; ctorByClass[regexpClass] = RegExp; ctorByClass[stringClass] = String; /** Used to avoid iterating non-enumerable properties in IE < 9 */ var nonEnumProps = {}; nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true }; nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true }; nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true }; nonEnumProps[objectClass] = { 'constructor': true }; (function() { var length = shadowedProps.length; while (length--) { var key = shadowedProps[length]; for (var className in nonEnumProps) { if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) { nonEnumProps[className][key] = false; } } } }()); /*--------------------------------------------------------------------------*/ /** * Creates a `lodash` object which wraps the given value to enable intuitive * method chaining. * * In addition to Lo-Dash methods, wrappers also have the following `Array` methods: * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, * and `unshift` * * Chaining is supported in custom builds as long as the `value` method is * implicitly or explicitly included in the build. * * The chainable wrapper functions are: * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`, * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, * and `zip` * * The non-chainable wrapper functions are: * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`, * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`, * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`, * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`, * `template`, `unescape`, `uniqueId`, and `value` * * The wrapper functions `first` and `last` return wrapped values when `n` is * provided, otherwise they return unwrapped values. * * Explicit chaining can be enabled by using the `_.chain` method. * * @name _ * @constructor * @category Chaining * @param {*} value The value to wrap in a `lodash` instance. * @returns {Object} Returns a `lodash` instance. * @example * * var wrapped = _([1, 2, 3]); * * // returns an unwrapped value * wrapped.reduce(function(sum, num) { * return sum + num; * }); * // => 6 * * // returns a wrapped value * var squares = wrapped.map(function(num) { * return num * num; * }); * * _.isArray(squares); * // => false * * _.isArray(squares.value()); * // => true */ function lodash(value) { // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__')) ? value : new lodashWrapper(value); } /** * A fast path for creating `lodash` wrapper objects. * * @private * @param {*} value The value to wrap in a `lodash` instance. * @param {boolean} chainAll A flag to enable chaining for all methods * @returns {Object} Returns a `lodash` instance. */ function lodashWrapper(value, chainAll) { this.__chain__ = !!chainAll; this.__wrapped__ = value; } // ensure `new lodashWrapper` is an instance of `lodash` lodashWrapper.prototype = lodash.prototype; /** * An object used to flag environments features. * * @static * @memberOf _ * @type Object */ var support = lodash.support = {}; (function() { var ctor = function() { this.x = 1; }, object = { '0': 1, 'length': 1 }, props = []; ctor.prototype = { 'valueOf': 1, 'y': 1 }; for (var key in new ctor) { props.push(key); } for (key in arguments) { } /** * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9). * * @memberOf _.support * @type boolean */ support.argsClass = toString.call(arguments) == argsClass; /** * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5). * * @memberOf _.support * @type boolean */ support.argsObject = arguments.constructor == Object && !(arguments instanceof Array); /** * Detect if `name` or `message` properties of `Error.prototype` are * enumerable by default. (IE < 9, Safari < 5.1) * * @memberOf _.support * @type boolean */ support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); /** * Detect if `prototype` properties are enumerable by default. * * 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`. * * @memberOf _.support * @type boolean */ support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype'); /** * Detect if functions can be decompiled by `Function#toString` * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps). * * @memberOf _.support * @type boolean */ support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext); /** * Detect if `Function#name` is supported (all but IE). * * @memberOf _.support * @type boolean */ support.funcNames = typeof Function.name == 'string'; /** * Detect if `arguments` object indexes are non-enumerable * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1). * * @memberOf _.support * @type boolean */ support.nonEnumArgs = key != 0; /** * Detect if properties shadowing those on `Object.prototype` are non-enumerable. * * In IE < 9 an objects own properties, shadowing non-enumerable ones, are * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug). * * @memberOf _.support * @type boolean */ support.nonEnumShadows = !/valueOf/.test(props); /** * Detect if own properties are iterated after inherited properties (all but IE < 9). * * @memberOf _.support * @type boolean */ support.ownLast = props[0] != 'x'; /** * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly. * * 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. * * @memberOf _.support * @type boolean */ support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]); /** * 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. * * @memberOf _.support * @type boolean */ support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx'; /** * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9) * and that the JS engine errors when attempting to coerce an object to * a string without a `toString` function. * * @memberOf _.support * @type boolean */ try { support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + '')); } catch(e) { support.nodeClass = true; } }(1)); /** * 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. * * @memberOf _.templateSettings * @type RegExp */ 'escape': /<%-([\s\S]+?)%>/g, /** * Used to detect code to be evaluated. * * @memberOf _.templateSettings * @type RegExp */ 'evaluate': /<%([\s\S]+?)%>/g, /** * Used to detect `data` property values to inject. * * @memberOf _.templateSettings * @type RegExp */ 'interpolate': reInterpolate, /** * Used to reference the data object in the template text. * * @memberOf _.templateSettings * @type string */ 'variable': '', /** * Used to import variables into the compiled template. * * @memberOf _.templateSettings * @type Object */ 'imports': { /** * A reference to the `lodash` function. * * @memberOf _.templateSettings.imports * @type Function */ '_': lodash } }; /*--------------------------------------------------------------------------*/ /** * The template used to create iterator functions. * * @private * @param {Object} data The data object used to populate the text. * @returns {string} Returns the interpolated text. */ var iteratorTemplate = template( // the `iterable` may be reassigned by the `top` snippet 'var index, iterable = <%= firstArg %>, ' + // assign the `result` variable an initial value 'result = <%= init %>;\n' + // exit early if the first argument is falsey 'if (!iterable) return result;\n' + // add code before the iteration branches '<%= top %>;' + // array-like iteration: '<% if (array) { %>\n' + 'var length = iterable.length; index = -1;\n' + 'if (<%= array %>) {' + // add support for accessing string characters by index if needed ' <% if (support.unindexedChars) { %>\n' + ' if (isString(iterable)) {\n' + " iterable = iterable.split('')\n" + ' }' + ' <% } %>\n' + // iterate over the array-like value ' while (++index < length) {\n' + ' <%= loop %>;\n' + ' }\n' + '}\n' + 'else {' + // object iteration: // add support for iterating over `arguments` objects if needed ' <% } else if (support.nonEnumArgs) { %>\n' + ' var length = iterable.length; index = -1;\n' + ' if (length && isArguments(iterable)) {\n' + ' while (++index < length) {\n' + " index += '';\n" + ' <%= loop %>;\n' + ' }\n' + ' } else {' + ' <% } %>' + // avoid iterating over `prototype` properties in older Firefox, Opera, and Safari ' <% if (support.enumPrototypes) { %>\n' + " var skipProto = typeof iterable == 'function';\n" + ' <% } %>' + // avoid iterating over `Error.prototype` properties in older IE and Safari ' <% if (support.enumErrorProps) { %>\n' + ' var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n' + ' <% } %>' + // define conditions used in the loop ' <%' + ' var conditions = [];' + ' if (support.enumPrototypes) { conditions.push(\'!(skipProto && index == "prototype")\'); }' + ' if (support.enumErrorProps) { conditions.push(\'!(skipErrorProps && (index == "message" || index == "name"))\'); }' + ' %>' + // iterate own properties using `Object.keys` ' <% if (useHas && keys) { %>\n' + ' var ownIndex = -1,\n' + ' ownProps = objectTypes[typeof iterable] && keys(iterable),\n' + ' length = ownProps ? ownProps.length : 0;\n\n' + ' while (++ownIndex < length) {\n' + ' index = ownProps[ownIndex];\n<%' + " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" + ' <%= loop %>;' + ' <% if (conditions.length) { %>\n }<% } %>\n' + ' }' + // else using a for-in loop ' <% } else { %>\n' + ' for (index in iterable) {\n<%' + ' if (useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); }' + " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" + ' <%= loop %>;' + ' <% if (conditions.length) { %>\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 (support.nonEnumShadows) { %>\n\n' + ' if (iterable !== objectProto) {\n' + " var ctor = iterable.constructor,\n" + ' isProto = iterable === (ctor && ctor.prototype),\n' + ' className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n' + ' nonEnum = nonEnumProps[className];\n' + ' <% for (k = 0; k < 7; k++) { %>\n' + " index = '<%= shadowedProps[k] %>';\n" + ' if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))<%' + ' if (!useHas) { %> || (!nonEnum[index] && iterable[index] !== objectProto[index])<% }' + ' %>) {\n' + ' <%= loop %>;\n' + ' }' + ' <% } %>\n' + ' }' + ' <% } %>' + ' <% } %>' + ' <% if (array || support.nonEnumArgs) { %>\n}<% } %>\n' + // add code to the bottom of the iteration function '<%= bottom %>;\n' + // finally, return the `result` 'return result' ); /*--------------------------------------------------------------------------*/ /** * The base implementation of `_.bind` that creates the bound function and * sets its meta data. * * @private * @param {Array} bindData The bind data array. * @returns {Function} Returns the new bound function. */ function baseBind(bindData) { var func = bindData[0], partialArgs = bindData[2], thisArg = bindData[4]; function bound() { // `Function#bind` spec // http://es5.github.io/#x15.3.4.5 if (partialArgs) { // avoid `arguments` object deoptimizations by using `slice` instead // of `Array.prototype.slice.call` and not assigning `arguments` to a // variable as a ternary expression var args = slice(partialArgs); push.apply(args, arguments); } // mimic the constructor's `return` behavior // http://es5.github.io/#x13.2.2 if (this instanceof bound) { // ensure `new bound` is an instance of `func` var thisBinding = baseCreate(func.prototype), result = func.apply(thisBinding, args || arguments); return isObject(result) ? result : thisBinding; } return func.apply(thisArg, args || arguments); } setBindData(bound, bindData); return bound; } /** * The base implementation of `_.clone` without argument juggling or support * for `thisArg` binding. * * @private * @param {*} value The value to clone. * @param {boolean} [isDeep=false] Specify a deep clone. * @param {Function} [callback] The function to customize cloning values. * @param {Array} [stackA=[]] Tracks traversed source objects. * @param {Array} [stackB=[]] Associates clones with source counterparts. * @returns {*} Returns the cloned value. */ function baseClone(value, isDeep, callback, stackA, stackB) { if (callback) { var result = callback(value); if (typeof result != 'undefined') { return result; } } // inspect [[Class]] var isObj = isObject(value); if (isObj) { var className = toString.call(value); if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) { return value; } var ctor = ctorByClass[className]; switch (className) { case boolClass: case dateClass: return new ctor(+value); case numberClass: case stringClass: return new ctor(value); case regexpClass: result = ctor(value.source, reFlags.exec(value)); result.lastIndex = value.lastIndex; return result; } } else { return value; } var isArr = isArray(value); if (isDeep) { // check for circular references and return corresponding clone var initedStack = !stackA; stackA || (stackA = getArray()); stackB || (stackB = getArray()); var length = stackA.length; while (length--) { if (stackA[length] == value) { return stackB[length]; } } result = isArr ? ctor(value.length) : {}; } else { result = isArr ? slice(value) : assign({}, value); } // add array properties assigned by `RegExp#exec` if (isArr) { if (hasOwnProperty.call(value, 'index')) { result.index = value.index; } if (hasOwnProperty.call(value, 'input')) { result.input = value.input; } } // exit for shallow clone if (!isDeep) { return result; } // 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) (isArr ? baseEach : forOwn)(value, function(objValue, key) { result[key] = baseClone(objValue, isDeep, callback, stackA, stackB); }); if (initedStack) { releaseArray(stackA); releaseArray(stackB); } return result; } /** * The base implementation of `_.create` without support for assigning * properties to the created object. * * @private * @param {Object} prototype The object to inherit from. * @returns {Object} Returns the new object. */ function baseCreate(prototype, properties) { return isObject(prototype) ? nativeCreate(prototype) : {}; } // fallback for browsers without `Object.create` if (!nativeCreate) { baseCreate = (function() { function Object() {} return function(prototype) { if (isObject(prototype)) { Object.prototype = prototype; var result = new Object; Object.prototype = null; } return result || context.Object(); }; }()); } /** * The base implementation of `_.createCallback` without support for creating * "_.pluck" or "_.where" style callbacks. * * @private * @param {*} [func=identity] The value to convert to a callback. * @param {*} [thisArg] The `this` binding of the created callback. * @param {number} [argCount] The number of arguments the callback accepts. * @returns {Function} Returns a callback function. */ function baseCreateCallback(func, thisArg, argCount) { if (typeof func != 'function') { return identity; } // exit early for no `thisArg` or already bound by `Function#bind` if (typeof thisArg == 'undefined' || !('prototype' in func)) { return func; } var bindData = func.__bindData__; if (typeof bindData == 'undefined') { if (support.funcNames) { bindData = !func.name; } bindData = bindData || !support.funcDecomp; if (!bindData) { var source = fnToString.call(func); if (!support.funcNames) { bindData = !reFuncName.test(source); } if (!bindData) { // checks if `func` references the `this` keyword and stores the result bindData = reThis.test(source); setBindData(func, bindData); } } } // exit early if there are no `this` references or `func` is bound if (bindData === false || (bindData !== true && bindData[1] & 1)) { return func; } switch (argCount) { case 1: return function(value) { return func.call(thisArg, value); }; case 2: return function(a, b) { return func.call(thisArg, a, b); }; case 3: return function(value, index, collection) { return func.call(thisArg, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(thisArg, accumulator, value, index, collection); }; } return bind(func, thisArg); } /** * The base implementation of `createWrapper` that creates the wrapper and * sets its meta data. * * @private * @param {Array} bindData The bind data array. * @returns {Function} Returns the new function. */ function baseCreateWrapper(bindData) { var func = bindData[0], bitmask = bindData[1], partialArgs = bindData[2], partialRightArgs = bindData[3], thisArg = bindData[4], arity = bindData[5]; var isBind = bitmask & 1, isBindKey = bitmask & 2, isCurry = bitmask & 4, isCurryBound = bitmask & 8, key = func; function bound() { var thisBinding = isBind ? thisArg : this; if (partialArgs) { var args = slice(partialArgs); push.apply(args, arguments); } if (partialRightArgs || isCurry) { args || (args = slice(arguments)); if (partialRightArgs) { push.apply(args, partialRightArgs); } if (isCurry && args.length < arity) { bitmask |= 16 & ~32; return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); } } args || (args = arguments); if (isBindKey) { func = thisBinding[key]; } if (this instanceof bound) { thisBinding = baseCreate(func.prototype); var result = func.apply(thisBinding, args); return isObject(result) ? result : thisBinding; } return func.apply(thisBinding, args); } setBindData(bound, bindData); return bound; } /** * The base implementation of `_.difference` that accepts a single array * of values to exclude. * * @private * @param {Array} array The array to process. * @param {Array} [values] The array of values to exclude. * @returns {Array} Returns a new array of filtered values. */ function baseDifference(array, values) { var index = -1, indexOf = getIndexOf(), length = array ? array.length : 0, isLarge = length >= largeArraySize && indexOf === baseIndexOf, result = []; if (isLarge) { var cache = createCache(values); if (cache) { indexOf = cacheIndexOf; values = cache; } else { isLarge = false; } } while (++index < length) { var value = array[index]; if (indexOf(values, value) < 0) { result.push(value); } } if (isLarge) { releaseObject(values); } return result; } /** * The base implementation of `_.flatten` without support for callback * shorthands or `thisArg` binding. * * @private * @param {Array} array The array to flatten. * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects. * @param {number} [fromIndex=0] The index to start from. * @returns {Array} Returns a new flattened array. */ function baseFlatten(array, isShallow, isStrict, fromIndex) { var index = (fromIndex || 0) - 1, length = array ? array.length : 0, result = []; while (++index < length) { var value = array[index]; if (value && typeof value == 'object' && typeof value.length == 'number' && (isArray(value) || isArguments(value))) { // recursively flatten arrays (susceptible to call stack limits) if (!isShallow) { value = baseFlatten(value, isShallow, isStrict); } var valIndex = -1, valLength = value.length, resIndex = result.length; result.length += valLength; while (++valIndex < valLength) { result[resIndex++] = value[valIndex]; } } else if (!isStrict) { result.push(value); } } return result; } /** * The base implementation of `_.isEqual`, without support for `thisArg` binding, * that allows partial "_.where" style comparisons. * * @private * @param {*} a The value to compare. * @param {*} b The other value to compare. * @param {Function} [callback] The function to customize comparing values. * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons. * @param {Array} [stackA=[]] Tracks traversed `a` objects. * @param {Array} [stackB=[]] Tracks traversed `b` objects. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ function baseIsEqual(a, b, callback, isWhere, stackA, stackB) { // used to indicate that when comparing objects, `a` has at least the properties of `b` if (callback) { var result = callback(a, b); if (typeof result != 'undefined') { return !!result; } } // exit early for identical values if (a === b) { // treat `+0` vs. `-0` as not equal return a !== 0 || (1 / a == 1 / b); } var type = typeof a, otherType = typeof b; // exit early for unlike primitive values if (a === a && !(a && objectTypes[type]) && !(b && objectTypes[otherType])) { return false; } // exit early for `null` and `undefined` avoiding ES3's Function#call behavior // http://es5.github.io/#x15.3.4.4 if (a == null || b == null) { return a === b; } // compare [[Class]] names var className = toString.call(a), otherClass = toString.call(b); if (className == argsClass) { className = objectClass; } if (otherClass == argsClass) { otherClass = objectClass; } if (className != otherClass) { 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.io/#x15.10.6.4) // treat string primitives and their corresponding object instances as equal return a == String(b); } var isArr = className == arrayClass; if (!isArr) { // unwrap any `lodash` wrapped values var aWrapped = hasOwnProperty.call(a, '__wrapped__'), bWrapped = hasOwnProperty.call(b, '__wrapped__'); if (aWrapped || bWrapped) { return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB); } // exit for functions and DOM nodes if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) { return false; } // in older versions of Opera, `arguments` objects have `Array` constructors var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor, ctorB = !support.argsObject && isArguments(b) ? Object : 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) && ('constructor' in a && 'constructor' in b) ) { 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.io/#x15.12.3) var initedStack = !stackA; stackA || (stackA = getArray()); stackB || (stackB = getArray()); var length = stackA.length; while (length--) { if (stackA[length] == a) { return stackB[length] == b; } } var size = 0; result = true; // 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 length = a.length; size = b.length; result = size == length; if (result || isWhere) { // deep compare the contents, ignoring non-numeric properties while (size--) { var index = length, value = b[size]; if (isWhere) { while (index--) { if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) { break; } } } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) { break; } } } } else { // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys` // which, in this case, is more costly forIn(b, function(value, key, b) { if (hasOwnProperty.call(b, key)) { // count the number of properties. size++; // deep compare each property value. return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB)); } }); if (result && !isWhere) { // ensure both objects have the same number of properties forIn(a, function(value, key, a) { if (hasOwnProperty.call(a, key)) { // `size` will be `-1` if `a` has more properties than `b` return (result = --size > -1); } }); } } stackA.pop(); stackB.pop(); if (initedStack) { releaseArray(stackA); releaseArray(stackB); } return result; } /** * The base implementation of `_.merge` without argument juggling or support * for `thisArg` binding. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {Function} [callback] The function to customize merging properties. * @param {Array} [stackA=[]] Tracks traversed source objects. * @param {Array} [stackB=[]] Associates values with source counterparts. */ function baseMerge(object, source, callback, stackA, stackB) { (isArray(source) ? forEach : forOwn)(source, function(source, key) { var found, isArr, result = source, value = object[key]; if (source && ((isArr = isArray(source)) || isPlainObject(source))) { // avoid merging previously merged cyclic sources var stackLength = stackA.length; while (stackLength--) { if ((found = stackA[stackLength] == source)) { value = stackB[stackLength]; break; } } if (!found) { var isShallow; if (callback) { result = callback(value, source); if ((isShallow = typeof result != 'undefined')) { value = result; } } if (!isShallow) { value = isArr ? (isArray(value) ? value : []) : (isPlainObject(value) ? value : {}); } // add `source` and associated `value` to the stack of traversed objects stackA.push(source); stackB.push(value); // recursively merge objects and arrays (susceptible to call stack limits) if (!isShallow) { baseMerge(value, source, callback, stackA, stackB); } } } else { if (callback) { result = callback(value, source); if (typeof result == 'undefined') { result = source; } } if (typeof result != 'undefined') { value = result; } } object[key] = value; }); } /** * The base implementation of `_.random` without argument juggling or support * for returning floating-point numbers. * * @private * @param {number} min The minimum possible value. * @param {number} max The maximum possible value. * @returns {number} Returns a random number. */ function baseRandom(min, max) { return min + floor(nativeRandom() * (max - min + 1)); } /** * The base implementation of `_.uniq` without support for callback shorthands * or `thisArg` binding. * * @private * @param {Array} array The array to process. * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. * @param {Function} [callback] The function called per iteration. * @returns {Array} Returns a duplicate-value-free array. */ function baseUniq(array, isSorted, callback) { var index = -1, indexOf = getIndexOf(), length = array ? array.length : 0, result = []; var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf, seen = (callback || isLarge) ? getArray() : result; if (isLarge) { var cache = createCache(seen); indexOf = cacheIndexOf; seen = cache; } while (++index < length) { var value = array[index], computed = callback ? callback(value, index, array) : value; if (isSorted ? !index || seen[seen.length - 1] !== computed : indexOf(seen, computed) < 0 ) { if (callback || isLarge) { seen.push(computed); } result.push(value); } } if (isLarge) { releaseArray(seen.array); releaseObject(seen); } else if (callback) { releaseArray(seen); } return result; } /** * Creates a function that aggregates a collection, creating an object composed * of keys generated from the results of running each element of the collection * through a callback. The given `setter` function sets the keys and values * of the composed object. * * @private * @param {Function} setter The setter function. * @returns {Function} Returns the new aggregator function. */ function createAggregator(setter) { return function(collection, callback, thisArg) { var result = {}; callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { var value = collection[index]; setter(result, value, callback(value, index, collection), collection); } } else { baseEach(collection, function(value, key, collection) { setter(result, value, callback(value, key, collection), collection); }); } return result; }; } /** * Creates a function that, when called, either curries or invokes `func` * with an optional `this` binding and partially applied arguments. * * @private * @param {Function|string} func The function or method name to reference. * @param {number} bitmask The bitmask of method flags to compose. * The bitmask may be composed of the following flags: * 1 - `_.bind` * 2 - `_.bindKey` * 4 - `_.curry` * 8 - `_.curry` (bound) * 16 - `_.partial` * 32 - `_.partialRight` * @param {Array} [partialArgs] An array of arguments to prepend to those * provided to the new function. * @param {Array} [partialRightArgs] An array of arguments to append to those * provided to the new function. * @param {*} [thisArg] The `this` binding of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new function. */ function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { var isBind = bitmask & 1, isBindKey = bitmask & 2, isCurry = bitmask & 4, isCurryBound = bitmask & 8, isPartial = bitmask & 16, isPartialRight = bitmask & 32; if (!isBindKey && !isFunction(func)) { throw new TypeError; } if (isPartial && !partialArgs.length) { bitmask &= ~16; isPartial = partialArgs = false; } if (isPartialRight && !partialRightArgs.length) { bitmask &= ~32; isPartialRight = partialRightArgs = false; } var bindData = func && func.__bindData__; if (bindData && bindData !== true) { // clone `bindData` bindData = slice(bindData); if (bindData[2]) { bindData[2] = slice(bindData[2]); } if (bindData[3]) { bindData[3] = slice(bindData[3]); } // set `thisBinding` is not previously bound if (isBind && !(bindData[1] & 1)) { bindData[4] = thisArg; } // set if previously bound but not currently (subsequent curried functions) if (!isBind && bindData[1] & 1) { bitmask |= 8; } // set curried arity if not yet set if (isCurry && !(bindData[1] & 4)) { bindData[5] = arity; } // append partial left arguments if (isPartial) { push.apply(bindData[2] || (bindData[2] = []), partialArgs); } // append partial right arguments if (isPartialRight) { unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); } // merge flags bindData[1] |= bitmask; return createWrapper.apply(null, bindData); } // fast path for `_.bind` var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); } /** * Creates compiled iteration functions. * * @private * @param {...Object} [options] The compile options object(s). * @param {string} [options.array] Code to determine if the iterable is an array or array-like. * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop. * @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration. * @param {string} [options.args] A comma separated string of iteration function arguments. * @param {string} [options.top] Code to execute before the iteration branches. * @param {string} [options.loop] Code to execute in the object loop. * @param {string} [options.bottom] Code to execute after the iteration branches. * @returns {Function} Returns the compiled function. */ function createIterator() { // data properties iteratorData.shadowedProps = shadowedProps; iteratorData.support = support; // iterator options iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = ''; iteratorData.init = 'iterable'; iteratorData.useHas = true; // merge options into a template data object for (var object, index = 0; object = arguments[index]; index++) { for (var key in object) { iteratorData[key] = object[key]; } } var args = iteratorData.args; iteratorData.firstArg = /^[^,]+/.exec(args)[0]; // create the function factory var factory = Function( 'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' + 'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' + 'objectTypes, nonEnumProps, stringClass, stringProto, toString', 'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}' ); // return the compiled function return factory( baseCreateCallback, errorClass, errorProto, hasOwnProperty, indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto, objectTypes, nonEnumProps, stringClass, stringProto, toString ); } /** * 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]; } /** * Gets the appropriate "indexOf" function. If the `_.indexOf` method is * customized, this method returns the custom method, otherwise it returns * the `baseIndexOf` function. * * @private * @returns {Function} Returns the "indexOf" function. */ function getIndexOf() { var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result; return result; } /** * Checks if `value` is a native function. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. */ function isNative(value) { return typeof value == 'function' && reNative.test(value); } /** * Sets `this` binding data on a given function. * * @private * @param {Function} func The function to set data on. * @param {Array} value The data array to set. */ var setBindData = !defineProperty ? noop : function(func, value) { descriptor.value = value; defineProperty(func, '__bindData__', descriptor); }; /** * A fallback implementation of `isPlainObject` which 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 {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. */ function shimIsPlainObject(value) { var ctor, result; // avoid non Object objects, `arguments` objects, and DOM elements if (!(value && toString.call(value) == objectClass) || (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor)) || (!support.argsClass && isArguments(value)) || (!support.nodeClass && isNode(value))) { return false; } // 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 (support.ownLast) { 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 typeof result == 'undefined' || hasOwnProperty.call(value, result); } /** * 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 {*} 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 value && typeof value == 'object' && typeof value.length == 'number' && toString.call(value) == argsClass || false; } // fallback for browsers that can't detect `arguments` objects by [[Class]] if (!support.argsClass) { isArguments = function(value) { return value && typeof value == 'object' && typeof value.length == 'number' && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false; }; } /** * Checks if `value` is an array. * * @static * @memberOf _ * @type Function * @category Objects * @param {*} 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 value && typeof value == 'object' && typeof value.length == 'number' && toString.call(value) == arrayClass || false; }; /** * A fallback implementation of `Object.keys` which produces an array of the * given object's own enumerable property names. * * @private * @type Function * @param {Object} object The object to inspect. * @returns {Array} Returns an array of property names. */ var shimKeys = createIterator({ 'args': 'object', 'init': '[]', 'top': 'if (!(objectTypes[typeof object])) return result', 'loop': 'result.push(index)' }); /** * Creates an array composed of the own enumerable property names of an object. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns an array of property names. * @example * * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); * // => ['one', 'two', 'three'] (property order is not guaranteed across environments) */ var keys = !nativeKeys ? shimKeys : function(object) { if (!isObject(object)) { return []; } if ((support.enumPrototypes && typeof object == 'function') || (support.nonEnumArgs && object.length && isArguments(object))) { return shimKeys(object); } return nativeKeys(object); }; /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */ var eachIteratorOptions = { 'args': 'collection, callback, thisArg', 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)", 'array': "typeof length == 'number'", 'keys': keys, 'loop': 'if (callback(iterable[index], index, collection) === false) return result' }; /** Reusable iterator options for `assign` and `defaults` */ var defaultsIteratorOptions = { 'args': 'object, source, guard', 'top': 'var args = arguments,\n' + ' argsIndex = 0,\n' + " argsLength = typeof guard == 'number' ? 2 : args.length;\n" + 'while (++argsIndex < argsLength) {\n' + ' iterable = args[argsIndex];\n' + ' if (iterable && objectTypes[typeof iterable]) {', 'keys': keys, 'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]", 'bottom': ' }\n}' }; /** Reusable iterator options for `forIn` and `forOwn` */ var forOwnIteratorOptions = { 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top, 'array': false }; /** * 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 = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; /** Used to convert HTML entities to characters */ var htmlUnescapes = invert(htmlEscapes); /** Used to match HTML entities and HTML characters */ var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'), reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g'); /** * A function compiled to iterate `arguments` objects, arrays, objects, and * strings consistenly across environments, executing the callback for each * element in the collection. The callback is bound to `thisArg` and invoked * with three arguments; (value, index|key, collection). Callbacks may exit * iteration early by explicitly returning `false`. * * @private * @type Function * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array|Object|string} Returns `collection`. */ var baseEach = createIterator(eachIteratorOptions); /*--------------------------------------------------------------------------*/ /** * Assigns own enumerable properties of source object(s) to the destination * object. Subsequent sources will overwrite property assignments of previous * sources. If a callback is provided it will be executed to produce the * assigned values. The callback is bound to `thisArg` and invoked with two * arguments; (objectValue, sourceValue). * * @static * @memberOf _ * @type Function * @alias extend * @category Objects * @param {Object} object The destination object. * @param {...Object} [source] The source objects. * @param {Function} [callback] The function to customize assigning values. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the destination object. * @example * * _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); * // => { 'name': 'fred', 'employer': 'slate' } * * var defaults = _.partialRight(_.assign, function(a, b) { * return typeof a == 'undefined' ? b : a; * }); * * var object = { 'name': 'barney' }; * defaults(object, { 'name': 'fred', 'employer': 'slate' }); * // => { 'name': 'barney', 'employer': 'slate' } */ var assign = createIterator(defaultsIteratorOptions, { 'top': defaultsIteratorOptions.top.replace(';', ';\n' + "if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" + ' var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n' + "} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" + ' callback = args[--argsLength];\n' + '}' ), 'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]' }); /** * Creates a clone of `value`. If `isDeep` is `true` nested objects will also * be cloned, otherwise they will be assigned by reference. If a callback * is provided it will be executed to produce the cloned values. If the * callback returns `undefined` cloning will be handled by the method instead. * The callback is bound to `thisArg` and invoked with one argument; (value). * * @static * @memberOf _ * @category Objects * @param {*} value The value to clone. * @param {boolean} [isDeep=false] Specify a deep clone. * @param {Function} [callback] The function to customize cloning values. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the cloned value. * @example * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * var shallow = _.clone(characters); * shallow[0] === characters[0]; * // => true * * var deep = _.clone(characters, true); * deep[0] === characters[0]; * // => false * * _.mixin({ * 'clone': _.partialRight(_.clone, function(value) { * return _.isElement(value) ? value.cloneNode(false) : undefined; * }) * }); * * var clone = _.clone(document.body); * clone.childNodes.length; * // => 0 */ function clone(value, isDeep, callback, thisArg) { // allows working with "Collections" methods without using their `index` // and `collection` arguments for `isDeep` and `callback` if (typeof isDeep != 'boolean' && isDeep != null) { thisArg = callback; callback = isDeep; isDeep = false; } return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); } /** * Creates a deep clone of `value`. If a callback is provided it will be * executed to produce the cloned values. If the callback returns `undefined` * cloning will be handled by the method instead. The callback is bound to * `thisArg` and invoked with one argument; (value). * * Note: This method is loosely based on the structured clone algorithm. Functions * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and * objects created by constructors other than `Object` are cloned to plain `Object` objects. * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm. * * @static * @memberOf _ * @category Objects * @param {*} value The value to deep clone. * @param {Function} [callback] The function to customize cloning values. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the deep cloned value. * @example * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * var deep = _.cloneDeep(characters); * deep[0] === characters[0]; * // => false * * var view = { * 'label': 'docs', * 'node': element * }; * * var clone = _.cloneDeep(view, function(value) { * return _.isElement(value) ? value.cloneNode(true) : undefined; * }); * * clone.node == view.node; * // => false */ function cloneDeep(value, callback, thisArg) { return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); } /** * Creates an object that inherits from the given `prototype` object. If a * `properties` object is provided its own enumerable properties are assigned * to the created object. * * @static * @memberOf _ * @category Objects * @param {Object} prototype The object to inherit from. * @param {Object} [properties] The properties to assign to the object. * @returns {Object} Returns the new object. * @example * * function Shape() { * this.x = 0; * this.y = 0; * } * * function Circle() { * Shape.call(this); * } * * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle }); * * var circle = new Circle; * circle instanceof Circle; * // => true * * circle instanceof Shape; * // => true */ function create(prototype, properties) { var result = baseCreate(prototype); return properties ? assign(result, properties) : result; } /** * Assigns own enumerable properties of source object(s) to the destination * object for all destination properties that resolve to `undefined`. Once a * property is set, additional defaults of the same property will be ignored. * * @static * @memberOf _ * @type Function * @category Objects * @param {Object} object The destination object. * @param {...Object} [source] The source objects. * @param- {Object} [guard] Allows working with `_.reduce` without using its * `key` and `object` arguments as sources. * @returns {Object} Returns the destination object. * @example * * var object = { 'name': 'barney' }; * _.defaults(object, { 'name': 'fred', 'employer': 'slate' }); * // => { 'name': 'barney', 'employer': 'slate' } */ var defaults = createIterator(defaultsIteratorOptions); /** * This method is like `_.findIndex` except that it returns the key of the * first element that passes the callback check, instead of the element itself. * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to search. * @param {Function|Object|string} [callback=identity] The function called per * iteration. If a property name or object is provided it will be used to * create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {string|undefined} Returns the key of the found element, else `undefined`. * @example * * var characters = { * 'barney': { 'age': 36, 'blocked': false }, * 'fred': { 'age': 40, 'blocked': true }, * 'pebbles': { 'age': 1, 'blocked': false } * }; * * _.findKey(characters, function(chr) { * return chr.age < 40; * }); * // => 'barney' (property order is not guaranteed across environments) * * // using "_.where" callback shorthand * _.findKey(characters, { 'age': 1 }); * // => 'pebbles' * * // using "_.pluck" callback shorthand * _.findKey(characters, 'blocked'); * // => 'fred' */ function findKey(object, callback, thisArg) { var result; callback = lodash.createCallback(callback, thisArg, 3); forOwn(object, function(value, key, object) { if (callback(value, key, object)) { result = key; return false; } }); return result; } /** * This method is like `_.findKey` except that it iterates over elements * of a `collection` in the opposite order. * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to search. * @param {Function|Object|string} [callback=identity] The function called per * iteration. If a property name or object is provided it will be used to * create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {string|undefined} Returns the key of the found element, else `undefined`. * @example * * var characters = { * 'barney': { 'age': 36, 'blocked': true }, * 'fred': { 'age': 40, 'blocked': false }, * 'pebbles': { 'age': 1, 'blocked': true } * }; * * _.findLastKey(characters, function(chr) { * return chr.age < 40; * }); * // => returns `pebbles`, assuming `_.findKey` returns `barney` * * // using "_.where" callback shorthand * _.findLastKey(characters, { 'age': 40 }); * // => 'fred' * * // using "_.pluck" callback shorthand * _.findLastKey(characters, 'blocked'); * // => 'pebbles' */ function findLastKey(object, callback, thisArg) { var result; callback = lodash.createCallback(callback, thisArg, 3); forOwnRight(object, function(value, key, object) { if (callback(value, key, object)) { result = key; return false; } }); return result; } /** * Iterates over own and inherited enumerable properties of an object, * 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 _ * @type Function * @category Objects * @param {Object} object The object to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns `object`. * @example * * function Shape() { * this.x = 0; * this.y = 0; * } * * Shape.prototype.move = function(x, y) { * this.x += x; * this.y += y; * }; * * _.forIn(new Shape, function(value, key) { * console.log(key); * }); * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) */ var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { 'useHas': false }); /** * This method is like `_.forIn` except that it iterates over elements * of a `collection` in the opposite order. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns `object`. * @example * * function Shape() { * this.x = 0; * this.y = 0; * } * * Shape.prototype.move = function(x, y) { * this.x += x; * this.y += y; * }; * * _.forInRight(new Shape, function(value, key) { * console.log(key); * }); * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move' */ function forInRight(object, callback, thisArg) { var pairs = []; forIn(object, function(value, key) { pairs.push(key, value); }); var length = pairs.length; callback = baseCreateCallback(callback, thisArg, 3); while (length--) { if (callback(pairs[length--], pairs[length], object) === false) { break; } } return object; } /** * Iterates over own enumerable properties of an object, 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 _ * @type Function * @category Objects * @param {Object} object The object to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns `object`. * @example * * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { * console.log(key); * }); * // => logs '0', '1', and 'length' (property order is not guaranteed across environments) */ var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); /** * This method is like `_.forOwn` except that it iterates over elements * of a `collection` in the opposite order. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns `object`. * @example * * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { * console.log(key); * }); * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length' */ function forOwnRight(object, callback, thisArg) { var props = keys(object), length = props.length; callback = baseCreateCallback(callback, thisArg, 3); while (length--) { var key = props[length]; if (callback(object[key], key, object) === false) { break; } } return object; } /** * Creates a sorted array of property names 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 an array of property names that have function values. * @example * * _.functions(_); * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] */ function functions(object) { var result = []; forIn(object, function(value, key) { if (isFunction(value)) { result.push(key); } }); return result.sort(); } /** * Checks if the specified property name exists as a direct property of `object`, * instead of an inherited property. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. * @param {string} key The name of the property to check. * @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, key) { return object ? hasOwnProperty.call(object, key) : 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': 'fred', 'second': 'barney' }); * // => { 'fred': 'first', 'barney': 'second' } */ function invert(object) { var index = -1, props = keys(object), length = props.length, result = {}; while (++index < length) { var key = props[index]; result[object[key]] = key; } return result; } /** * Checks if `value` is a boolean value. * * @static * @memberOf _ * @category Objects * @param {*} 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 || value && typeof value == 'object' && toString.call(value) == boolClass || false; } /** * Checks if `value` is a date. * * @static * @memberOf _ * @category Objects * @param {*} 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 value && typeof value == 'object' && toString.call(value) == dateClass || false; } /** * Checks if `value` is a DOM element. * * @static * @memberOf _ * @category Objects * @param {*} 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 */ function isEmpty(value) { var result = true; if (!value) { return result; } var className = toString.call(value), length = value.length; if ((className == arrayClass || className == stringClass || (support.argsClass ? className == argsClass : isArguments(value))) || (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { return !length; } forOwn(value, function() { return (result = false); }); return result; } /** * Performs a deep comparison between two values to determine if they are * equivalent to each other. If a callback is provided it will be executed * to compare values. If the callback returns `undefined` comparisons will * be handled by the method instead. The callback is bound to `thisArg` and * invoked with two arguments; (a, b). * * @static * @memberOf _ * @category Objects * @param {*} a The value to compare. * @param {*} b The other value to compare. * @param {Function} [callback] The function to customize comparing values. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'name': 'fred' }; * var copy = { 'name': 'fred' }; * * object == copy; * // => false * * _.isEqual(object, copy); * // => true * * var words = ['hello', 'goodbye']; * var otherWords = ['hi', 'goodbye']; * * _.isEqual(words, otherWords, function(a, b) { * var reGreet = /^(?:hello|hi)$/i, * aGreet = _.isString(a) && reGreet.test(a), * bGreet = _.isString(b) && reGreet.test(b); * * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined; * }); * // => true */ function isEqual(a, b, callback, thisArg) { return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2)); } /** * Checks if `value` is, or can be coerced to, a finite number. * * Note: This is not the same as native `isFinite` which will return true for * booleans and empty strings. See http://es5.github.io/#x15.1.2.5. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is finite, else `false`. * @example * * _.isFinite(-101); * // => true * * _.isFinite('10'); * // => true * * _.isFinite(true); * // => false * * _.isFinite(''); * // => false * * _.isFinite(Infinity); * // => false */ function isFinite(value) { return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value)); } /** * Checks if `value` is a function. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true */ function isFunction(value) { return typeof value == 'function'; } // fallback for older versions of Chrome and Safari if (isFunction(/x/)) { isFunction = function(value) { return typeof value == 'function' && toString.call(value) == funcClass; }; } /** * Checks if `value` is the language type of Object. * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(1); * // => false */ function isObject(value) { // check if the value is the ECMAScript language type of Object // http://es5.github.io/#x8 // and avoid a V8 bug // http://code.google.com/p/v8/issues/detail?id=2291 return !!(value && objectTypes[typeof value]); } /** * Checks if `value` is `NaN`. * * Note: This is not the same as native `isNaN` which will return `true` for * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`. * @example * * _.isNaN(NaN); * // => true * * _.isNaN(new Number(NaN)); * // => true * * isNaN(undefined); * // => true * * _.isNaN(undefined); * // => false */ function isNaN(value) { // `NaN` as a primitive is the only value that is not equal to itself // (perform the [[Class]] check first to avoid errors with some host objects in IE) return isNumber(value) && value != +value; } /** * Checks if `value` is `null`. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is `null`, else `false`. * @example * * _.isNull(null); * // => true * * _.isNull(undefined); * // => false */ function isNull(value) { return value === null; } /** * Checks if `value` is a number. * * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is a number, else `false`. * @example * * _.isNumber(8.4 * 5); * // => true */ function isNumber(value) { return typeof value == 'number' || value && typeof value == 'object' && toString.call(value) == numberClass || false; } /** * Checks if `value` is an object created by the `Object` constructor. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. * @example * * function Shape() { * this.x = 0; * this.y = 0; * } * * _.isPlainObject(new Shape); * // => false * * _.isPlainObject([1, 2, 3]); * // => false * * _.isPlainObject({ 'x': 0, 'y': 0 }); * // => true */ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) { return false; } var valueOf = value.valueOf, objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); return objProto ? (value == objProto || getPrototypeOf(value) == objProto) : shimIsPlainObject(value); }; /** * Checks if `value` is a regular expression. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`. * @example * * _.isRegExp(/fred/); * // => true */ function isRegExp(value) { return value && objectTypes[typeof value] && toString.call(value) == regexpClass || false; } /** * Checks if `value` is a string. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is a string, else `false`. * @example * * _.isString('fred'); * // => true */ function isString(value) { return typeof value == 'string' || value && typeof value == 'object' && toString.call(value) == stringClass || false; } /** * Checks if `value` is `undefined`. * * @static * @memberOf _ * @category Objects * @param {*} value The value to check. * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`. * @example * * _.isUndefined(void 0); * // => true */ function isUndefined(value) { return typeof value == 'undefined'; } /** * Creates an object with the same keys as `object` and values generated by * running each own enumerable property of `object` through the callback. * The callback is bound to `thisArg` and invoked with three arguments; * (value, key, object). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new object with values of the results of each `callback` execution. * @example * * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; }); * // => { 'a': 3, 'b': 6, 'c': 9 } * * var characters = { * 'fred': { 'name': 'fred', 'age': 40 }, * 'pebbles': { 'name': 'pebbles', 'age': 1 } * }; * * // using "_.pluck" callback shorthand * _.mapValues(characters, 'age'); * // => { 'fred': 40, 'pebbles': 1 } */ function mapValues(object, callback, thisArg) { var result = {}; callback = lodash.createCallback(callback, thisArg, 3); forOwn(object, function(value, key, object) { result[key] = callback(value, key, object); }); return result; } /** * Recursively merges own enumerable properties of the source object(s), that * don't resolve to `undefined` into the destination object. Subsequent sources * will overwrite property assignments of previous sources. If a callback is * provided it will be executed to produce the merged values of the destination * and source properties. If the callback returns `undefined` merging will * be handled by the method instead. The callback is bound to `thisArg` and * invoked with two arguments; (objectValue, sourceValue). * * @static * @memberOf _ * @category Objects * @param {Object} object The destination object. * @param {...Object} [source] The source objects. * @param {Function} [callback] The function to customize merging properties. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the destination object. * @example * * var names = { * 'characters': [ * { 'name': 'barney' }, * { 'name': 'fred' } * ] * }; * * var ages = { * 'characters': [ * { 'age': 36 }, * { 'age': 40 } * ] * }; * * _.merge(names, ages); * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] } * * var food = { * 'fruits': ['apple'], * 'vegetables': ['beet'] * }; * * var otherFood = { * 'fruits': ['banana'], * 'vegetables': ['carrot'] * }; * * _.merge(food, otherFood, function(a, b) { * return _.isArray(a) ? a.concat(b) : undefined; * }); * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] } */ function merge(object) { var args = arguments, length = 2; if (!isObject(object)) { return object; } // allows working with `_.reduce` and `_.reduceRight` without using // their `index` and `collection` arguments if (typeof args[2] != 'number') { length = args.length; } if (length > 3 && typeof args[length - 2] == 'function') { var callback = baseCreateCallback(args[--length - 1], args[length--], 2); } else if (length > 2 && typeof args[length - 1] == 'function') { callback = args[--length]; } var sources = slice(arguments, 1, length), index = -1, stackA = getArray(), stackB = getArray(); while (++index < length) { baseMerge(object, sources[index], callback, stackA, stackB); } releaseArray(stackA); releaseArray(stackB); return object; } /** * Creates a shallow clone of `object` excluding the specified properties. * Property names may be specified as individual arguments or as arrays of * property names. If a callback is provided it will be executed for each * property of `object` omitting the properties the callback returns truey * for. The callback is bound to `thisArg` and invoked with three arguments; * (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. * @param {Function|...string|string[]} [callback] The properties to omit or the * function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns an object without the omitted properties. * @example * * _.omit({ 'name': 'fred', 'age': 40 }, 'age'); * // => { 'name': 'fred' } * * _.omit({ 'name': 'fred', 'age': 40 }, function(value) { * return typeof value == 'number'; * }); * // => { 'name': 'fred' } */ function omit(object, callback, thisArg) { var result = {}; if (typeof callback != 'function') { var props = []; forIn(object, function(value, key) { props.push(key); }); props = baseDifference(props, baseFlatten(arguments, true, false, 1)); var index = -1, length = props.length; while (++index < length) { var key = props[index]; result[key] = object[key]; } } else { callback = lodash.createCallback(callback, thisArg, 3); forIn(object, function(value, key, object) { if (!callback(value, key, object)) { result[key] = value; } }); } return result; } /** * Creates a two dimensional array of an object's key-value pairs, * i.e. `[[key1, value1], [key2, value2]]`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns new array of key-value pairs. * @example * * _.pairs({ 'barney': 36, 'fred': 40 }); * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments) */ function pairs(object) { var index = -1, props = keys(object), length = props.length, result = Array(length); while (++index < length) { var key = props[index]; result[index] = [key, object[key]]; } return result; } /** * Creates a shallow clone of `object` composed of the specified properties. * Property names may be specified as individual arguments or as arrays of * property names. If a callback is provided it will be executed for each * property of `object` picking the properties the callback returns truey * for. The callback is bound to `thisArg` and invoked with three arguments; * (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. * @param {Function|...string|string[]} [callback] The function called per * iteration or property names to pick, specified as individual property * names or arrays of property names. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns an object composed of the picked properties. * @example * * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name'); * // => { 'name': 'fred' } * * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) { * return key.charAt(0) != '_'; * }); * // => { 'name': 'fred' } */ function pick(object, callback, thisArg) { var result = {}; if (typeof callback != 'function') { var index = -1, props = baseFlatten(arguments, true, false, 1), length = isObject(object) ? props.length : 0; while (++index < length) { var key = props[index]; if (key in object) { result[key] = object[key]; } } } else { callback = lodash.createCallback(callback, thisArg, 3); forIn(object, function(value, key, object) { if (callback(value, key, object)) { result[key] = value; } }); } return result; } /** * An alternative to `_.reduce` this method transforms `object` to a new * `accumulator` object which is the result of running each of its own * enumerable properties through a callback, with each callback execution * potentially mutating the `accumulator` object. The callback is bound to * `thisArg` and invoked with four arguments; (accumulator, value, key, object). * Callbacks may exit iteration early by explicitly returning `false`. * * @static * @memberOf _ * @category Objects * @param {Array|Object} object The object to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [accumulator] The custom accumulator value. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the accumulated value. * @example * * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) { * num *= num; * if (num % 2) { * return result.push(num) < 3; * } * }); * // => [1, 9, 25] * * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { * result[key] = num * 3; * }); * // => { 'a': 3, 'b': 6, 'c': 9 } */ function transform(object, callback, accumulator, thisArg) { var isArr = isArray(object); if (accumulator == null) { if (isArr) { accumulator = []; } else { var ctor = object && object.constructor, proto = ctor && ctor.prototype; accumulator = baseCreate(proto); } } if (callback) { callback = lodash.createCallback(callback, thisArg, 4); (isArr ? baseEach : forOwn)(object, function(value, index, object) { return callback(accumulator, value, index, object); }); } return accumulator; } /** * Creates an array composed of the own enumerable property values of `object`. * * @static * @memberOf _ * @category Objects * @param {Object} object The object to inspect. * @returns {Array} Returns an array of property values. * @example * * _.values({ 'one': 1, 'two': 2, 'three': 3 }); * // => [1, 2, 3] (property order is not guaranteed across environments) */ function values(object) { var index = -1, props = keys(object), length = props.length, result = Array(length); while (++index < length) { result[index] = object[props[index]]; } return result; } /*--------------------------------------------------------------------------*/ /** * Creates an array of elements from the specified indexes, or keys, of the * `collection`. Indexes may be specified as individual arguments or as arrays * of indexes. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {...(number|number[]|string|string[])} [index] The indexes of `collection` * to retrieve, specified as individual indexes or arrays of indexes. * @returns {Array} Returns a new array of elements corresponding to the * provided indexes. * @example * * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]); * // => ['a', 'c', 'e'] * * _.at(['fred', 'barney', 'pebbles'], 0, 2); * // => ['fred', 'pebbles'] */ function at(collection) { var args = arguments, index = -1, props = baseFlatten(args, true, false, 1), length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length, result = Array(length); if (support.unindexedChars && isString(collection)) { collection = collection.split(''); } while(++index < length) { result[index] = collection[props[index]]; } return result; } /** * Checks if a given value is present in a collection using strict equality * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the * offset from the end of the collection. * * @static * @memberOf _ * @alias include * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {*} target The value to check for. * @param {number} [fromIndex=0] The index to search from. * @returns {boolean} Returns `true` if the `target` element is found, else `false`. * @example * * _.contains([1, 2, 3], 1); * // => true * * _.contains([1, 2, 3], 1, 2); * // => false * * _.contains({ 'name': 'fred', 'age': 40 }, 'fred'); * // => true * * _.contains('pebbles', 'eb'); * // => true */ function contains(collection, target, fromIndex) { var index = -1, indexOf = getIndexOf(), length = collection ? collection.length : 0, result = false; fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0; if (isArray(collection)) { result = indexOf(collection, target, fromIndex) > -1; } else if (typeof length == 'number') { result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1; } else { baseEach(collection, function(value) { if (++index >= fromIndex) { return !(result = value === target); } }); } return result; } /** * Creates an object composed of keys generated from the results of running * each element of `collection` through the callback. The corresponding value * of each key is the number of times the key was returned by the callback. * The callback is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the composed aggregate object. * @example * * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); * // => { '4': 1, '6': 2 } * * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); * // => { '4': 1, '6': 2 } * * _.countBy(['one', 'two', 'three'], 'length'); * // => { '3': 2, '5': 1 } */ var countBy = createAggregator(function(result, value, key) { (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); }); /** * Checks if the given callback returns truey value for **all** elements of * a collection. The callback is bound to `thisArg` and invoked with three * arguments; (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias all * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {boolean} Returns `true` if all elements passed the callback check, * else `false`. * @example * * _.every([true, 1, null, 'yes']); * // => false * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * // using "_.pluck" callback shorthand * _.every(characters, 'age'); * // => true * * // using "_.where" callback shorthand * _.every(characters, { 'age': 36 }); * // => false */ function every(collection, callback, thisArg) { var result = true; callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { if (!(result = !!callback(collection[index], index, collection))) { break; } } } else { baseEach(collection, function(value, index, collection) { return (result = !!callback(value, index, collection)); }); } return result; } /** * Iterates over elements of a collection, returning an array of all elements * the callback returns truey for. The callback is bound to `thisArg` and * invoked with three arguments; (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias select * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new array of elements that passed the callback check. * @example * * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [2, 4, 6] * * var characters = [ * { 'name': 'barney', 'age': 36, 'blocked': false }, * { 'name': 'fred', 'age': 40, 'blocked': true } * ]; * * // using "_.pluck" callback shorthand * _.filter(characters, 'blocked'); * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] * * // using "_.where" callback shorthand * _.filter(characters, { 'age': 36 }); * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] */ function filter(collection, callback, thisArg) { var result = []; callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { var value = collection[index]; if (callback(value, index, collection)) { result.push(value); } } } else { baseEach(collection, function(value, index, collection) { if (callback(value, index, collection)) { result.push(value); } }); } return result; } /** * Iterates over elements of a collection, returning the first element that * the callback returns truey for. The callback is bound to `thisArg` and * invoked with three arguments; (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias detect, findWhere * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the found element, else `undefined`. * @example * * var characters = [ * { 'name': 'barney', 'age': 36, 'blocked': false }, * { 'name': 'fred', 'age': 40, 'blocked': true }, * { 'name': 'pebbles', 'age': 1, 'blocked': false } * ]; * * _.find(characters, function(chr) { * return chr.age < 40; * }); * // => { 'name': 'barney', 'age': 36, 'blocked': false } * * // using "_.where" callback shorthand * _.find(characters, { 'age': 1 }); * // => { 'name': 'pebbles', 'age': 1, 'blocked': false } * * // using "_.pluck" callback shorthand * _.find(characters, 'blocked'); * // => { 'name': 'fred', 'age': 40, 'blocked': true } */ function find(collection, callback, thisArg) { callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { var value = collection[index]; if (callback(value, index, collection)) { return value; } } } else { var result; baseEach(collection, function(value, index, collection) { if (callback(value, index, collection)) { result = value; return false; } }); return result; } } /** * This method is like `_.find` except that it iterates over elements * of a `collection` from right to left. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the found element, else `undefined`. * @example * * _.findLast([1, 2, 3, 4], function(num) { * return num % 2 == 1; * }); * // => 3 */ function findLast(collection, callback, thisArg) { var result; callback = lodash.createCallback(callback, thisArg, 3); forEachRight(collection, function(value, index, collection) { if (callback(value, index, collection)) { result = value; return false; } }); return result; } /** * Iterates over elements of a collection, executing the callback for each * element. The callback is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). Callbacks may exit iteration early by * explicitly returning `false`. * * Note: As with other "Collections" methods, objects with a `length` property * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` * may be used for object iteration. * * @static * @memberOf _ * @alias each * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array|Object|string} Returns `collection`. * @example * * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); * // => logs each number and returns '1,2,3' * * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); * // => logs each number and returns the object (property order is not guaranteed across environments) */ function forEach(collection, callback, thisArg) { if (callback && typeof thisArg == 'undefined' && isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { if (callback(collection[index], index, collection) === false) { break; } } } else { baseEach(collection, callback, thisArg); } return collection; } /** * This method is like `_.forEach` except that it iterates over elements * of a `collection` from right to left. * * @static * @memberOf _ * @alias eachRight * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array|Object|string} Returns `collection`. * @example * * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(','); * // => logs each number from right to left and returns '3,2,1' */ function forEachRight(collection, callback, thisArg) { var iterable = collection, length = collection ? collection.length : 0; callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3); if (isArray(collection)) { while (length--) { if (callback(collection[length], length, collection) === false) { break; } } } else { if (typeof length != 'number') { var props = keys(collection); length = props.length; } else if (support.unindexedChars && isString(collection)) { iterable = collection.split(''); } baseEach(collection, function(value, key, collection) { key = props ? props[--length] : --length; return callback(iterable[key], key, collection); }); } return collection; } /** * Creates an object composed of keys generated from the results of running * each element of a collection through the callback. The corresponding value * of each key is an array of the elements responsible for generating the key. * The callback is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false` * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the composed aggregate object. * @example * * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); * // => { '4': [4.2], '6': [6.1, 6.4] } * * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); * // => { '4': [4.2], '6': [6.1, 6.4] } * * // using "_.pluck" callback shorthand * _.groupBy(['one', 'two', 'three'], 'length'); * // => { '3': ['one', 'two'], '5': ['three'] } */ var groupBy = createAggregator(function(result, value, key) { (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); }); /** * Creates an object composed of keys generated from the results of running * each element of the collection through the given callback. The corresponding * value of each key is the last element responsible for generating the key. * The callback is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the composed aggregate object. * @example * * var keys = [ * { 'dir': 'left', 'code': 97 }, * { 'dir': 'right', 'code': 100 } * ]; * * _.indexBy(keys, 'dir'); * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } * * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); }); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } * * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } */ var indexBy = createAggregator(function(result, value, key) { result[key] = value; }); /** * Invokes the method named by `methodName` on each element in the `collection` * returning an array of the results of each invoked method. Additional arguments * will be provided to each invoked method. If `methodName` is a function it * will be invoked for, and `this` bound to, each element in the `collection`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|string} methodName The name of the method to invoke or * the function invoked per iteration. * @param {...*} [arg] Arguments to invoke the method with. * @returns {Array} Returns a new array of the results of each invoked method. * @example * * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); * // => [[1, 5, 7], [1, 2, 3]] * * _.invoke([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ function invoke(collection, methodName) { var args = slice(arguments, 2), index = -1, isFunc = typeof methodName == 'function', length = collection ? collection.length : 0, result = Array(typeof length == 'number' ? length : 0); forEach(collection, function(value) { result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args); }); return result; } /** * Creates an array of values by running each element in the collection * through the callback. The callback is bound to `thisArg` and invoked with * three arguments; (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias collect * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new array of the results of each `callback` execution. * @example * * _.map([1, 2, 3], function(num) { return num * 3; }); * // => [3, 6, 9] * * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); * // => [3, 6, 9] (property order is not guaranteed across environments) * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * // using "_.pluck" callback shorthand * _.map(characters, 'name'); * // => ['barney', 'fred'] */ function map(collection, callback, thisArg) { var index = -1, length = collection ? collection.length : 0, result = Array(typeof length == 'number' ? length : 0); callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { while (++index < length) { result[index] = callback(collection[index], index, collection); } } else { baseEach(collection, function(value, key, collection) { result[++index] = callback(value, key, collection); }); } return result; } /** * Retrieves the maximum value of a collection. If the collection is empty or * falsey `-Infinity` is returned. If a callback is provided it will be executed * for each value in the collection to generate the criterion by which the value * is ranked. The callback is bound to `thisArg` and invoked with three * arguments; (value, index, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the maximum value. * @example * * _.max([4, 2, 8, 6]); * // => 8 * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * _.max(characters, function(chr) { return chr.age; }); * // => { 'name': 'fred', 'age': 40 }; * * // using "_.pluck" callback shorthand * _.max(characters, 'age'); * // => { 'name': 'fred', 'age': 40 }; */ function max(collection, callback, thisArg) { var computed = -Infinity, result = computed; // allows working with functions like `_.map` without using // their `index` argument as a callback if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) { callback = null; } if (callback == null && isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { var value = collection[index]; if (value > result) { result = value; } } } else { callback = (callback == null && isString(collection)) ? charAtCallback : lodash.createCallback(callback, thisArg, 3); baseEach(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current > computed) { computed = current; result = value; } }); } return result; } /** * Retrieves the minimum value of a collection. If the collection is empty or * falsey `Infinity` is returned. If a callback is provided it will be executed * for each value in the collection to generate the criterion by which the value * is ranked. The callback is bound to `thisArg` and invoked with three * arguments; (value, index, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the minimum value. * @example * * _.min([4, 2, 8, 6]); * // => 2 * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * _.min(characters, function(chr) { return chr.age; }); * // => { 'name': 'barney', 'age': 36 }; * * // using "_.pluck" callback shorthand * _.min(characters, 'age'); * // => { 'name': 'barney', 'age': 36 }; */ function min(collection, callback, thisArg) { var computed = Infinity, result = computed; // allows working with functions like `_.map` without using // their `index` argument as a callback if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) { callback = null; } if (callback == null && isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { var value = collection[index]; if (value < result) { result = value; } } } else { callback = (callback == null && isString(collection)) ? charAtCallback : lodash.createCallback(callback, thisArg, 3); baseEach(collection, function(value, index, collection) { var current = callback(value, index, collection); if (current < computed) { computed = current; result = value; } }); } return result; } /** * Retrieves the value of a specified property from all elements in the collection. * * @static * @memberOf _ * @type Function * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {string} property The name of the property to pluck. * @returns {Array} Returns a new array of property values. * @example * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * _.pluck(characters, 'name'); * // => ['barney', 'fred'] */ var pluck = map; /** * Reduces a collection to a value which is the accumulated result of running * each element in the collection through the callback, where each successive * callback execution consumes the return value of the previous execution. If * `accumulator` is not provided the first element of the collection will be * used as the initial `accumulator` value. The callback is bound to `thisArg` * and invoked with four arguments; (accumulator, value, index|key, collection). * * @static * @memberOf _ * @alias foldl, inject * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [accumulator] Initial value of the accumulator. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the accumulated value. * @example * * var sum = _.reduce([1, 2, 3], function(sum, num) { * return sum + num; * }); * // => 6 * * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { * result[key] = num * 3; * return result; * }, {}); * // => { 'a': 3, 'b': 6, 'c': 9 } */ function reduce(collection, callback, accumulator, thisArg) { var noaccum = arguments.length < 3; callback = lodash.createCallback(callback, thisArg, 4); if (isArray(collection)) { var index = -1, length = collection.length; if (noaccum) { accumulator = collection[++index]; } while (++index < length) { accumulator = callback(accumulator, collection[index], index, collection); } } else { baseEach(collection, function(value, index, collection) { accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, value, index, collection) }); } return accumulator; } /** * This method is like `_.reduce` except that it iterates over elements * of a `collection` from right to left. * * @static * @memberOf _ * @alias foldr * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {*} [accumulator] Initial value of the accumulator. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the accumulated value. * @example * * var list = [[0, 1], [2, 3], [4, 5]]; * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, callback, accumulator, thisArg) { var noaccum = arguments.length < 3; callback = lodash.createCallback(callback, thisArg, 4); forEachRight(collection, function(value, index, collection) { accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, value, index, collection); }); return accumulator; } /** * The opposite of `_.filter` this method returns the elements of a * collection that the callback does **not** return truey for. * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new array of elements that failed the callback check. * @example * * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [1, 3, 5] * * var characters = [ * { 'name': 'barney', 'age': 36, 'blocked': false }, * { 'name': 'fred', 'age': 40, 'blocked': true } * ]; * * // using "_.pluck" callback shorthand * _.reject(characters, 'blocked'); * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] * * // using "_.where" callback shorthand * _.reject(characters, { 'age': 36 }); * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] */ function reject(collection, callback, thisArg) { callback = lodash.createCallback(callback, thisArg, 3); return filter(collection, function(value, index, collection) { return !callback(value, index, collection); }); } /** * Retrieves a random element or `n` random elements from a collection. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to sample. * @param {number} [n] The number of elements to sample. * @param- {Object} [guard] Allows working with functions like `_.map` * without using their `index` arguments as `n`. * @returns {Array} Returns the random sample(s) of `collection`. * @example * * _.sample([1, 2, 3, 4]); * // => 2 * * _.sample([1, 2, 3, 4], 2); * // => [3, 1] */ function sample(collection, n, guard) { if (collection && typeof collection.length != 'number') { collection = values(collection); } else if (support.unindexedChars && isString(collection)) { collection = collection.split(''); } if (n == null || guard) { return collection ? collection[baseRandom(0, collection.length - 1)] : undefined; } var result = shuffle(collection); result.length = nativeMin(nativeMax(0, n), result.length); return result; } /** * Creates an array of shuffled values, using a version of the Fisher-Yates * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to shuffle. * @returns {Array} Returns a new shuffled collection. * @example * * _.shuffle([1, 2, 3, 4, 5, 6]); * // => [4, 1, 6, 3, 5, 2] */ function shuffle(collection) { var index = -1, length = collection ? collection.length : 0, result = Array(typeof length == 'number' ? length : 0); forEach(collection, function(value) { var rand = baseRandom(0, ++index); result[index] = result[rand]; result[rand] = value; }); return result; } /** * Gets the size of the `collection` by returning `collection.length` for arrays * and array-like objects or the number of own enumerable properties for objects. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to inspect. * @returns {number} Returns `collection.length` or number of own enumerable properties. * @example * * _.size([1, 2]); * // => 2 * * _.size({ 'one': 1, 'two': 2, 'three': 3 }); * // => 3 * * _.size('pebbles'); * // => 7 */ function size(collection) { var length = collection ? collection.length : 0; return typeof length == 'number' ? length : keys(collection).length; } /** * Checks if the callback returns a truey value for **any** element of a * collection. The function returns as soon as it finds a passing value and * does not iterate over the entire collection. The callback is bound to * `thisArg` and invoked with three arguments; (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias any * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {boolean} Returns `true` if any element passed the callback check, * else `false`. * @example * * _.some([null, 0, 'yes', false], Boolean); * // => true * * var characters = [ * { 'name': 'barney', 'age': 36, 'blocked': false }, * { 'name': 'fred', 'age': 40, 'blocked': true } * ]; * * // using "_.pluck" callback shorthand * _.some(characters, 'blocked'); * // => true * * // using "_.where" callback shorthand * _.some(characters, { 'age': 1 }); * // => false */ function some(collection, callback, thisArg) { var result; callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { if ((result = callback(collection[index], index, collection))) { break; } } } else { baseEach(collection, function(value, index, collection) { return !(result = callback(value, index, collection)); }); } return !!result; } /** * Creates an array of elements, sorted in ascending order by the results of * running each element in a collection through the callback. This method * performs a stable sort, that is, it will preserve the original sort order * of equal elements. The callback is bound to `thisArg` and invoked with * three arguments; (value, index|key, collection). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an array of property names is provided for `callback` the collection * will be sorted by each property value. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Array|Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new array of sorted elements. * @example * * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); * // => [3, 1, 2] * * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); * // => [3, 1, 2] * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 }, * { 'name': 'barney', 'age': 26 }, * { 'name': 'fred', 'age': 30 } * ]; * * // using "_.pluck" callback shorthand * _.map(_.sortBy(characters, 'age'), _.values); * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]] * * // sorting by multiple properties * _.map(_.sortBy(characters, ['name', 'age']), _.values); * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]] */ function sortBy(collection, callback, thisArg) { var index = -1, isArr = isArray(callback), length = collection ? collection.length : 0, result = Array(typeof length == 'number' ? length : 0); if (!isArr) { callback = lodash.createCallback(callback, thisArg, 3); } forEach(collection, function(value, key, collection) { var object = result[++index] = getObject(); if (isArr) { object.criteria = map(callback, function(key) { return value[key]; }); } else { (object.criteria = getArray())[0] = callback(value, key, collection); } object.index = index; object.value = value; }); length = result.length; result.sort(compareAscending); while (length--) { var object = result[length]; result[length] = object.value; if (!isArr) { releaseArray(object.criteria); } releaseObject(object); } return result; } /** * Converts the `collection` to an array. * * @static * @memberOf _ * @category Collections * @param {Array|Object|string} collection The collection to convert. * @returns {Array} Returns the new converted array. * @example * * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); * // => [2, 3, 4] */ function toArray(collection) { if (collection && typeof collection.length == 'number') { return (support.unindexedChars && isString(collection)) ? collection.split('') : slice(collection); } return values(collection); } /** * Performs a deep comparison of each element in a `collection` to the given * `properties` object, returning an array of all elements that have equivalent * property values. * * @static * @memberOf _ * @type Function * @category Collections * @param {Array|Object|string} collection The collection to iterate over. * @param {Object} props The object of property values to filter by. * @returns {Array} Returns a new array of elements that have the given properties. * @example * * var characters = [ * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }, * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } * ]; * * _.where(characters, { 'age': 36 }); * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }] * * _.where(characters, { 'pets': ['dino'] }); * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }] */ var where = filter; /*--------------------------------------------------------------------------*/ /** * Creates an array with all falsey values removed. The values `false`, `null`, * `0`, `""`, `undefined`, and `NaN` are all falsey. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to compact. * @returns {Array} Returns a new array of filtered values. * @example * * _.compact([0, 1, false, 2, '', 3]); * // => [1, 2, 3] */ function compact(array) { var index = -1, length = array ? array.length : 0, result = []; while (++index < length) { var value = array[index]; if (value) { result.push(value); } } return result; } /** * Creates an array excluding all values of the provided arrays using strict * equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to process. * @param {...Array} [values] The arrays of values to exclude. * @returns {Array} Returns a new array of filtered values. * @example * * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); * // => [1, 3, 4] */ function difference(array) { return baseDifference(array, baseFlatten(arguments, true, true, 1)); } /** * This method is like `_.find` except that it returns the index of the first * element that passes the callback check, instead of the element itself. * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var characters = [ * { 'name': 'barney', 'age': 36, 'blocked': false }, * { 'name': 'fred', 'age': 40, 'blocked': true }, * { 'name': 'pebbles', 'age': 1, 'blocked': false } * ]; * * _.findIndex(characters, function(chr) { * return chr.age < 20; * }); * // => 2 * * // using "_.where" callback shorthand * _.findIndex(characters, { 'age': 36 }); * // => 0 * * // using "_.pluck" callback shorthand * _.findIndex(characters, 'blocked'); * // => 1 */ function findIndex(array, callback, thisArg) { var index = -1, length = array ? array.length : 0; callback = lodash.createCallback(callback, thisArg, 3); while (++index < length) { if (callback(array[index], index, array)) { return index; } } return -1; } /** * This method is like `_.findIndex` except that it iterates over elements * of a `collection` from right to left. * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var characters = [ * { 'name': 'barney', 'age': 36, 'blocked': true }, * { 'name': 'fred', 'age': 40, 'blocked': false }, * { 'name': 'pebbles', 'age': 1, 'blocked': true } * ]; * * _.findLastIndex(characters, function(chr) { * return chr.age > 30; * }); * // => 1 * * // using "_.where" callback shorthand * _.findLastIndex(characters, { 'age': 36 }); * // => 0 * * // using "_.pluck" callback shorthand * _.findLastIndex(characters, 'blocked'); * // => 2 */ function findLastIndex(array, callback, thisArg) { var length = array ? array.length : 0; callback = lodash.createCallback(callback, thisArg, 3); while (length--) { if (callback(array[length], length, array)) { return length; } } return -1; } /** * Gets the first element or first `n` elements of an array. If a callback * is provided elements at the beginning of the array are returned as long * as the callback returns truey. The callback is bound to `thisArg` and * invoked with three arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias head, take * @category Arrays * @param {Array} array The array to query. * @param {Function|Object|number|string} [callback] The function called * per element or the number of elements to return. If a property name or * object is provided it will be used to create a "_.pluck" or "_.where" * style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the first element(s) of `array`. * @example * * _.first([1, 2, 3]); * // => 1 * * _.first([1, 2, 3], 2); * // => [1, 2] * * _.first([1, 2, 3], function(num) { * return num < 3; * }); * // => [1, 2] * * var characters = [ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' }, * { 'name': 'fred', 'blocked': false, 'employer': 'slate' }, * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } * ]; * * // using "_.pluck" callback shorthand * _.first(characters, 'blocked'); * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }] * * // using "_.where" callback shorthand * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name'); * // => ['barney', 'fred'] */ function first(array, callback, thisArg) { var n = 0, length = array ? array.length : 0; if (typeof callback != 'number' && callback != null) { var index = -1; callback = lodash.createCallback(callback, thisArg, 3); while (++index < length && callback(array[index], index, array)) { n++; } } else { n = callback; if (n == null || thisArg) { return array ? array[0] : undefined; } } return slice(array, 0, nativeMin(nativeMax(0, n), length)); } /** * Flattens a nested array (the nesting can be to any depth). If `isShallow` * is truey, the array will only be flattened a single level. If a callback * is provided each element of the array is passed through the callback before * flattening. The callback is bound to `thisArg` and invoked with three * arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to flatten. * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new flattened array. * @example * * _.flatten([1, [2], [3, [[4]]]]); * // => [1, 2, 3, 4]; * * _.flatten([1, [2], [3, [[4]]]], true); * // => [1, 2, 3, [[4]]]; * * var characters = [ * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] }, * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } * ]; * * // using "_.pluck" callback shorthand * _.flatten(characters, 'pets'); * // => ['hoppy', 'baby puss', 'dino'] */ function flatten(array, isShallow, callback, thisArg) { // juggle arguments if (typeof isShallow != 'boolean' && isShallow != null) { thisArg = callback; callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow; isShallow = false; } if (callback != null) { array = map(array, callback, thisArg); } return baseFlatten(array, isShallow); } /** * Gets the index at which the first occurrence of `value` is found using * strict equality for comparisons, i.e. `===`. If the array is already sorted * providing `true` for `fromIndex` will run a faster binary search. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {boolean|number} [fromIndex=0] The index to search from or `true` * to perform a binary search on a sorted array. * @returns {number} Returns the index of the matched value or `-1`. * @example * * _.indexOf([1, 2, 3, 1, 2, 3], 2); * // => 1 * * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); * // => 4 * * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); * // => 2 */ function indexOf(array, value, fromIndex) { if (typeof fromIndex == 'number') { var length = array ? array.length : 0; fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0); } else if (fromIndex) { var index = sortedIndex(array, value); return array[index] === value ? index : -1; } return baseIndexOf(array, value, fromIndex); } /** * Gets all but the last element or last `n` elements of an array. If a * callback is provided elements at the end of the array are excluded from * the result as long as the callback returns truey. The callback is bound * to `thisArg` and invoked with three arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to query. * @param {Function|Object|number|string} [callback=1] The function called * per element or the number of elements to exclude. If a property name or * object is provided it will be used to create a "_.pluck" or "_.where" * style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a slice of `array`. * @example * * _.initial([1, 2, 3]); * // => [1, 2] * * _.initial([1, 2, 3], 2); * // => [1] * * _.initial([1, 2, 3], function(num) { * return num > 1; * }); * // => [1] * * var characters = [ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' }, * { 'name': 'fred', 'blocked': true, 'employer': 'slate' }, * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } * ]; * * // using "_.pluck" callback shorthand * _.initial(characters, 'blocked'); * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }] * * // using "_.where" callback shorthand * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name'); * // => ['barney', 'fred'] */ function initial(array, callback, thisArg) { var n = 0, length = array ? array.length : 0; if (typeof callback != 'number' && callback != null) { var index = length; callback = lodash.createCallback(callback, thisArg, 3); while (index-- && callback(array[index], index, array)) { n++; } } else { n = (callback == null || thisArg) ? 1 : callback || n; } return slice(array, 0, nativeMin(nativeMax(0, length - n), length)); } /** * Creates an array of unique values present in all provided arrays using * strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {...Array} [array] The arrays to inspect. * @returns {Array} Returns an array of shared values. * @example * * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]); * // => [1, 2] */ function intersection() { var args = [], argsIndex = -1, argsLength = arguments.length, caches = getArray(), indexOf = getIndexOf(), trustIndexOf = indexOf === baseIndexOf, seen = getArray(); while (++argsIndex < argsLength) { var value = arguments[argsIndex]; if (isArray(value) || isArguments(value)) { args.push(value); caches.push(trustIndexOf && value.length >= largeArraySize && createCache(argsIndex ? args[argsIndex] : seen)); } } var array = args[0], index = -1, length = array ? array.length : 0, result = []; outer: while (++index < length) { var cache = caches[0]; value = array[index]; if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) { argsIndex = argsLength; (cache || seen).push(value); while (--argsIndex) { cache = caches[argsIndex]; if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) { continue outer; } } result.push(value); } } while (argsLength--) { cache = caches[argsLength]; if (cache) { releaseObject(cache); } } releaseArray(caches); releaseArray(seen); return result; } /** * Gets the last element or last `n` elements of an array. If a callback is * provided elements at the end of the array are returned as long as the * callback returns truey. The callback is bound to `thisArg` and invoked * with three arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to query. * @param {Function|Object|number|string} [callback] The function called * per element or the number of elements to return. If a property name or * object is provided it will be used to create a "_.pluck" or "_.where" * style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {*} Returns the last element(s) of `array`. * @example * * _.last([1, 2, 3]); * // => 3 * * _.last([1, 2, 3], 2); * // => [2, 3] * * _.last([1, 2, 3], function(num) { * return num > 1; * }); * // => [2, 3] * * var characters = [ * { 'name': 'barney', 'blocked': false, 'employer': 'slate' }, * { 'name': 'fred', 'blocked': true, 'employer': 'slate' }, * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } * ]; * * // using "_.pluck" callback shorthand * _.pluck(_.last(characters, 'blocked'), 'name'); * // => ['fred', 'pebbles'] * * // using "_.where" callback shorthand * _.last(characters, { 'employer': 'na' }); * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }] */ function last(array, callback, thisArg) { var n = 0, length = array ? array.length : 0; if (typeof callback != 'number' && callback != null) { var index = length; callback = lodash.createCallback(callback, thisArg, 3); while (index-- && callback(array[index], index, array)) { n++; } } else { n = callback; if (n == null || thisArg) { return array ? array[length - 1] : undefined; } } return slice(array, nativeMax(0, length - n)); } /** * Gets the index at which the last occurrence of `value` is found using strict * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used * as the offset from the end of the collection. * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {number} [fromIndex=array.length-1] The index to search from. * @returns {number} Returns the index of the matched value or `-1`. * @example * * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); * // => 4 * * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); * // => 1 */ function lastIndexOf(array, value, fromIndex) { var index = array ? array.length : 0; if (typeof fromIndex == 'number') { index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1; } while (index--) { if (array[index] === value) { return index; } } return -1; } /** * Removes all provided values from the given array using strict equality for * comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to modify. * @param {...*} [value] The values to remove. * @returns {Array} Returns `array`. * @example * * var array = [1, 2, 3, 1, 2, 3]; * _.pull(array, 2, 3); * console.log(array); * // => [1, 1] */ function pull(array) { var args = arguments, argsIndex = 0, argsLength = args.length, length = array ? array.length : 0; while (++argsIndex < argsLength) { var index = -1, value = args[argsIndex]; while (++index < length) { if (array[index] === value) { splice.call(array, index--, 1); length--; } } } return array; } /** * Creates an array of numbers (positive and/or negative) progressing from * `start` up to but not including `end`. If `start` is less than `stop` a * zero-length range is created unless a negative `step` is specified. * * @static * @memberOf _ * @category Arrays * @param {number} [start=0] The start of the range. * @param {number} end The end of the range. * @param {number} [step=1] The value to increment or decrement by. * @returns {Array} Returns a new range array. * @example * * _.range(4); * // => [0, 1, 2, 3] * * _.range(1, 5); * // => [1, 2, 3, 4] * * _.range(0, 20, 5); * // => [0, 5, 10, 15] * * _.range(0, -4, -1); * // => [0, -1, -2, -3] * * _.range(1, 4, 0); * // => [1, 1, 1] * * _.range(0); * // => [] */ function range(start, end, step) { start = +start || 0; step = typeof step == 'number' ? step : (+step || 1); if (end == null) { end = start; start = 0; } // use `Array(length)` so engines like Chakra and V8 avoid slower modes // http://youtu.be/XAqIpGU8ZZk#t=17m25s var index = -1, length = nativeMax(0, ceil((end - start) / (step || 1))), result = Array(length); while (++index < length) { result[index] = start; start += step; } return result; } /** * Removes all elements from an array that the callback returns truey for * and returns an array of removed elements. The callback is bound to `thisArg` * and invoked with three arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to modify. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new array of removed elements. * @example * * var array = [1, 2, 3, 4, 5, 6]; * var evens = _.remove(array, function(num) { return num % 2 == 0; }); * * console.log(array); * // => [1, 3, 5] * * console.log(evens); * // => [2, 4, 6] */ function remove(array, callback, thisArg) { var index = -1, length = array ? array.length : 0, result = []; callback = lodash.createCallback(callback, thisArg, 3); while (++index < length) { var value = array[index]; if (callback(value, index, array)) { result.push(value); splice.call(array, index--, 1); length--; } } return result; } /** * The opposite of `_.initial` this method gets all but the first element or * first `n` elements of an array. If a callback function is provided elements * at the beginning of the array are excluded from the result as long as the * callback returns truey. The callback is bound to `thisArg` and invoked * with three arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias drop, tail * @category Arrays * @param {Array} array The array to query. * @param {Function|Object|number|string} [callback=1] The function called * per element or the number of elements to exclude. If a property name or * object is provided it will be used to create a "_.pluck" or "_.where" * style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a slice of `array`. * @example * * _.rest([1, 2, 3]); * // => [2, 3] * * _.rest([1, 2, 3], 2); * // => [3] * * _.rest([1, 2, 3], function(num) { * return num < 3; * }); * // => [3] * * var characters = [ * { 'name': 'barney', 'blocked': true, 'employer': 'slate' }, * { 'name': 'fred', 'blocked': false, 'employer': 'slate' }, * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } * ]; * * // using "_.pluck" callback shorthand * _.pluck(_.rest(characters, 'blocked'), 'name'); * // => ['fred', 'pebbles'] * * // using "_.where" callback shorthand * _.rest(characters, { 'employer': 'slate' }); * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }] */ function rest(array, callback, thisArg) { if (typeof callback != 'number' && callback != null) { var n = 0, index = -1, length = array ? array.length : 0; callback = lodash.createCallback(callback, thisArg, 3); while (++index < length && callback(array[index], index, array)) { n++; } } else { n = (callback == null || thisArg) ? 1 : nativeMax(0, callback); } return slice(array, n); } /** * Uses a binary search to determine the smallest index at which a value * should be inserted into a given sorted array in order to maintain the sort * order of the array. If a callback is provided it will be executed for * `value` and each element of `array` to compute their sort ranking. The * callback is bound to `thisArg` and invoked with one argument; (value). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to inspect. * @param {*} value The value to evaluate. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedIndex([20, 30, 50], 40); * // => 2 * * // using "_.pluck" callback shorthand * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); * // => 2 * * var dict = { * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 } * }; * * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { * return dict.wordToNumber[word]; * }); * // => 2 * * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { * return this.wordToNumber[word]; * }, dict); * // => 2 */ function sortedIndex(array, value, callback, thisArg) { var low = 0, high = array ? array.length : low; // explicitly reference `identity` for better inlining in Firefox callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity; value = callback(value); while (low < high) { var mid = (low + high) >>> 1; (callback(array[mid]) < value) ? low = mid + 1 : high = mid; } return low; } /** * Creates an array of unique values, in order, of the provided arrays using * strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {...Array} [array] The arrays to inspect. * @returns {Array} Returns an array of combined values. * @example * * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]); * // => [1, 2, 3, 5, 4] */ function union() { return baseUniq(baseFlatten(arguments, true, true)); } /** * Creates a duplicate-value-free version of an array using strict equality * for comparisons, i.e. `===`. If the array is sorted, providing * `true` for `isSorted` will use a faster algorithm. If a callback is provided * each element of `array` is passed through the callback before uniqueness * is computed. The callback is bound to `thisArg` and invoked with three * arguments; (value, index, array). * * If a property name is provided for `callback` the created "_.pluck" style * callback will return the property value of the given element. * * If an object is provided for `callback` the created "_.where" style callback * will return `true` for elements that have the properties of the given object, * else `false`. * * @static * @memberOf _ * @alias unique * @category Arrays * @param {Array} array The array to process. * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. * @param {Function|Object|string} [callback=identity] The function called * per iteration. If a property name or object is provided it will be used * to create a "_.pluck" or "_.where" style callback, respectively. * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a duplicate-value-free array. * @example * * _.uniq([1, 2, 1, 3, 1]); * // => [1, 2, 3] * * _.uniq([1, 1, 2, 2, 3], true); * // => [1, 2, 3] * * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); }); * // => ['A', 'b', 'C'] * * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math); * // => [1, 2.5, 3] * * // using "_.pluck" callback shorthand * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }, { 'x': 2 }] */ function uniq(array, isSorted, callback, thisArg) { // juggle arguments if (typeof isSorted != 'boolean' && isSorted != null) { thisArg = callback; callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted; isSorted = false; } if (callback != null) { callback = lodash.createCallback(callback, thisArg, 3); } return baseUniq(array, isSorted, callback); } /** * Creates an array excluding all provided values using strict equality for * comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to filter. * @param {...*} [value] The values to exclude. * @returns {Array} Returns a new array of filtered values. * @example * * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); * // => [2, 3, 4] */ function without(array) { return baseDifference(array, slice(arguments, 1)); } /** * Creates an array that is the symmetric difference of the provided arrays. * See http://en.wikipedia.org/wiki/Symmetric_difference. * * @static * @memberOf _ * @category Arrays * @param {...Array} [array] The arrays to inspect. * @returns {Array} Returns an array of values. * @example * * _.xor([1, 2, 3], [5, 2, 1, 4]); * // => [3, 5, 4] * * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]); * // => [1, 4, 5] */ function xor() { var index = -1, length = arguments.length; while (++index < length) { var array = arguments[index]; if (isArray(array) || isArguments(array)) { var result = result ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result))) : array; } } return result || []; } /** * Creates an array of grouped elements, the first of which contains the first * elements of the given arrays, the second of which contains the second * elements of the given arrays, and so on. * * @static * @memberOf _ * @alias unzip * @category Arrays * @param {...Array} [array] Arrays to process. * @returns {Array} Returns a new array of grouped elements. * @example * * _.zip(['fred', 'barney'], [30, 40], [true, false]); * // => [['fred', 30, true], ['barney', 40, false]] */ function zip() { var array = arguments.length > 1 ? arguments : arguments[0], index = -1, length = array ? max(pluck(array, 'length')) : 0, result = Array(length < 0 ? 0 : length); while (++index < length) { result[index] = pluck(array, index); } return result; } /** * Creates an object composed from arrays of `keys` and `values`. Provide * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]` * or two arrays, one of `keys` and one of corresponding `values`. * * @static * @memberOf _ * @alias object * @category Arrays * @param {Array} keys The array of keys. * @param {Array} [values=[]] The array of values. * @returns {Object} Returns an object composed of the given keys and * corresponding values. * @example * * _.zipObject(['fred', 'barney'], [30, 40]); * // => { 'fred': 30, 'barney': 40 } */ function zipObject(keys, values) { var index = -1, length = keys ? keys.length : 0, result = {}; if (!values && length && !isArray(keys[0])) { values = []; } while (++index < length) { var key = keys[index]; if (values) { result[key] = values[index]; } else if (key) { result[key[0]] = key[1]; } } return result; } /*--------------------------------------------------------------------------*/ /** * Creates a function that executes `func`, with the `this` binding and * arguments of the created function, only after being called `n` times. * * @static * @memberOf _ * @category Functions * @param {number} n The number of times the function must be called before * `func` is executed. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var saves = ['profile', 'settings']; * * var done = _.after(saves.length, function() { * console.log('Done saving!'); * }); * * _.forEach(saves, function(type) { * asyncSave({ 'type': type, 'complete': done }); * }); * // => logs 'Done saving!', after all saves have completed */ function after(n, func) { if (!isFunction(func)) { throw new TypeError; } return function() { if (--n < 1) { return func.apply(this, arguments); } }; } /** * Creates a function that, when called, invokes `func` with the `this` * binding of `thisArg` and prepends any additional `bind` arguments to those * provided to the bound function. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to bind. * @param {*} [thisArg] The `this` binding of `func`. * @param {...*} [arg] Arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * var func = function(greeting) { * return greeting + ' ' + this.name; * }; * * func = _.bind(func, { 'name': 'fred' }, 'hi'); * func(); * // => 'hi fred' */ function bind(func, thisArg) { return arguments.length > 2 ? createWrapper(func, 17, slice(arguments, 2), null, thisArg) : createWrapper(func, 1, null, null, thisArg); } /** * Binds methods of an object to the object itself, overwriting the existing * method. Method names may be specified as individual arguments or as arrays * of method names. If no method names are provided all the function properties * of `object` will be bound. * * @static * @memberOf _ * @category Functions * @param {Object} object The object to bind and assign the bound methods to. * @param {...string} [methodName] The object method names to * bind, specified as individual method names or arrays of method names. * @returns {Object} Returns `object`. * @example * * var view = { * 'label': 'docs', * 'onClick': function() { console.log('clicked ' + this.label); } * }; * * _.bindAll(view); * jQuery('#docs').on('click', view.onClick); * // => logs 'clicked docs', when the button is clicked */ function bindAll(object) { var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object), index = -1, length = funcs.length; while (++index < length) { var key = funcs[index]; object[key] = createWrapper(object[key], 1, null, null, object); } return object; } /** * Creates a function that, when called, invokes the method at `object[key]` * and prepends any additional `bindKey` arguments to those provided to the bound * function. This method differs from `_.bind` by allowing bound functions to * reference methods that will be redefined or don't yet exist. * See http://michaux.ca/articles/lazy-function-definition-pattern. * * @static * @memberOf _ * @category Functions * @param {Object} object The object the method belongs to. * @param {string} key The key of the method. * @param {...*} [arg] Arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * var object = { * 'name': 'fred', * 'greet': function(greeting) { * return greeting + ' ' + this.name; * } * }; * * var func = _.bindKey(object, 'greet', 'hi'); * func(); * // => 'hi fred' * * object.greet = function(greeting) { * return greeting + 'ya ' + this.name + '!'; * }; * * func(); * // => 'hiya fred!' */ function bindKey(object, key) { return arguments.length > 2 ? createWrapper(key, 19, slice(arguments, 2), null, object) : createWrapper(key, 3, null, null, object); } /** * Creates a function that is the composition of the provided functions, * where each function consumes the return value of the function that follows. * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. * Each function is executed with the `this` binding of the composed function. * * @static * @memberOf _ * @category Functions * @param {...Function} [func] Functions to compose. * @returns {Function} Returns the new composed function. * @example * * var realNameMap = { * 'pebbles': 'penelope' * }; * * var format = function(name) { * name = realNameMap[name.toLowerCase()] || name; * return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase(); * }; * * var greet = function(formatted) { * return 'Hiya ' + formatted + '!'; * }; * * var welcome = _.compose(greet, format); * welcome('pebbles'); * // => 'Hiya Penelope!' */ function compose() { var funcs = arguments, length = funcs.length; while (length--) { if (!isFunction(funcs[length])) { throw new TypeError; } } return function() { var args = arguments, length = funcs.length; while (length--) { args = [funcs[length].apply(this, args)]; } return args[0]; }; } /** * Creates a function which accepts one or more arguments of `func` that when * invoked either executes `func` returning its result, if all `func` arguments * have been provided, or returns a function that accepts one or more of the * remaining `func` arguments, and so on. The arity of `func` can be specified * if `func.length` is not sufficient. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to curry. * @param {number} [arity=func.length] The arity of `func`. * @returns {Function} Returns the new curried function. * @example * * var curried = _.curry(function(a, b, c) { * console.log(a + b + c); * }); * * curried(1)(2)(3); * // => 6 * * curried(1, 2)(3); * // => 6 * * curried(1, 2, 3); * // => 6 */ function curry(func, arity) { arity = typeof arity == 'number' ? arity : (+arity || func.length); return createWrapper(func, 4, null, null, null, arity); } /** * Creates a function that will delay the execution of `func` until after * `wait` milliseconds have elapsed since the last time it was invoked. * Provide an options object to indicate that `func` should be invoked on * the leading and/or trailing edge of the `wait` timeout. Subsequent calls * to the debounced function will return the result of the last `func` call. * * Note: If `leading` and `trailing` options are `true` `func` will be called * on the trailing edge of the timeout only if the the debounced function is * invoked more than once during the `wait` timeout. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to debounce. * @param {number} wait The number of milliseconds to delay. * @param {Object} [options] The options object. * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout. * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called. * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * // avoid costly calculations while the window size is in flux * var lazyLayout = _.debounce(calculateLayout, 150); * jQuery(window).on('resize', lazyLayout); * * // execute `sendMail` when the click event is fired, debouncing subsequent calls * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { * 'leading': true, * 'trailing': false * }); * * // ensure `batchLog` is executed once after 1 second of debounced calls * var source = new EventSource('/stream'); * source.addEventListener('message', _.debounce(batchLog, 250, { * 'maxWait': 1000 * }, false); */ function debounce(func, wait, options) { var args, maxTimeoutId, result, stamp, thisArg, timeoutId, trailingCall, lastCalled = 0, maxWait = false, trailing = true; if (!isFunction(func)) { throw new TypeError; } wait = nativeMax(0, wait) || 0; if (options === true) { var leading = true; trailing = false; } else if (isObject(options)) { leading = options.leading; maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0); trailing = 'trailing' in options ? options.trailing : trailing; } var delayed = function() { var remaining = wait - (now() - stamp); if (remaining <= 0) { if (maxTimeoutId) { clearTimeout(maxTimeoutId); } var isCalled = trailingCall; maxTimeoutId = timeoutId = trailingCall = undefined; if (isCalled) { lastCalled = now(); result = func.apply(thisArg, args); if (!timeoutId && !maxTimeoutId) { args = thisArg = null; } } } else { timeoutId = setTimeout(delayed, remaining); } }; var maxDelayed = function() { if (timeoutId) { clearTimeout(timeoutId); } maxTimeoutId = timeoutId = trailingCall = undefined; if (trailing || (maxWait !== wait)) { lastCalled = now(); result = func.apply(thisArg, args); if (!timeoutId && !maxTimeoutId) { args = thisArg = null; } } }; return function() { args = arguments; stamp = now(); thisArg = this; trailingCall = trailing && (timeoutId || !leading); if (maxWait === false) { var leadingCall = leading && !timeoutId; } else { if (!maxTimeoutId && !leading) { lastCalled = stamp; } var remaining = maxWait - (stamp - lastCalled), isCalled = remaining <= 0; if (isCalled) { if (maxTimeoutId) { maxTimeoutId = clearTimeout(maxTimeoutId); } lastCalled = stamp; result = func.apply(thisArg, args); } else if (!maxTimeoutId) { maxTimeoutId = setTimeout(maxDelayed, remaining); } } if (isCalled && timeoutId) { timeoutId = clearTimeout(timeoutId); } else if (!timeoutId && wait !== maxWait) { timeoutId = setTimeout(delayed, wait); } if (leadingCall) { isCalled = true; result = func.apply(thisArg, args); } if (isCalled && !timeoutId && !maxTimeoutId) { args = thisArg = null; } return result; }; } /** * Defers executing the `func` function until the current call stack has cleared. * Additional arguments will be provided to `func` when it is invoked. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to defer. * @param {...*} [arg] Arguments to invoke the function with. * @returns {number} Returns the timer id. * @example * * _.defer(function(text) { console.log(text); }, 'deferred'); * // logs 'deferred' after one or more milliseconds */ function defer(func) { if (!isFunction(func)) { throw new TypeError; } var args = slice(arguments, 1); return setTimeout(function() { func.apply(undefined, args); }, 1); } /** * Executes the `func` function after `wait` milliseconds. Additional arguments * will be provided to `func` when it is invoked. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay execution. * @param {...*} [arg] Arguments to invoke the function with. * @returns {number} Returns the timer id. * @example * * _.delay(function(text) { console.log(text); }, 1000, 'later'); * // => logs 'later' after one second */ function delay(func, wait) { if (!isFunction(func)) { throw new TypeError; } var args = slice(arguments, 2); return setTimeout(function() { func.apply(undefined, args); }, wait); } /** * Creates a function that memoizes the result of `func`. If `resolver` is * provided it will be used to determine the cache key for storing the result * based on the arguments provided to the memoized function. By default, the * first argument provided to the memoized function is used as the cache key. * The `func` is executed with the `this` binding of the memoized function. * The result cache is exposed as the `cache` property on the memoized function. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to have its output memoized. * @param {Function} [resolver] A function used to resolve the cache key. * @returns {Function} Returns the new memoizing function. * @example * * var fibonacci = _.memoize(function(n) { * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); * }); * * fibonacci(9) * // => 34 * * var data = { * 'fred': { 'name': 'fred', 'age': 40 }, * 'pebbles': { 'name': 'pebbles', 'age': 1 } * }; * * // modifying the result cache * var get = _.memoize(function(name) { return data[name]; }, _.identity); * get('pebbles'); * // => { 'name': 'pebbles', 'age': 1 } * * get.cache.pebbles.name = 'penelope'; * get('pebbles'); * // => { 'name': 'penelope', 'age': 1 } */ function memoize(func, resolver) { if (!isFunction(func)) { throw new TypeError; } var memoized = function() { var cache = memoized.cache, key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0]; return hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = func.apply(this, arguments)); } memoized.cache = {}; return memoized; } /** * Creates a function that is restricted to execute `func` once. Repeat calls to * the function will return the value of the first call. The `func` is executed * with the `this` binding of the created function. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var initialize = _.once(createApplication); * initialize(); * initialize(); * // `initialize` executes `createApplication` once */ function once(func) { var ran, result; if (!isFunction(func)) { throw new TypeError; } return function() { if (ran) { return result; } ran = true; result = func.apply(this, arguments); // clear the `func` variable so the function may be garbage collected func = null; return result; }; } /** * Creates a function that, when called, invokes `func` with any additional * `partial` arguments prepended to those provided to the new function. This * method is similar to `_.bind` except it does **not** alter the `this` binding. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to partially apply arguments to. * @param {...*} [arg] Arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * var greet = function(greeting, name) { return greeting + ' ' + name; }; * var hi = _.partial(greet, 'hi'); * hi('fred'); * // => 'hi fred' */ function partial(func) { return createWrapper(func, 16, slice(arguments, 1)); } /** * This method is like `_.partial` except that `partial` arguments are * appended to those provided to the new function. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to partially apply arguments to. * @param {...*} [arg] Arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * var defaultsDeep = _.partialRight(_.merge, _.defaults); * * var options = { * 'variable': 'data', * 'imports': { 'jq': $ } * }; * * defaultsDeep(options, _.templateSettings); * * options.variable * // => 'data' * * options.imports * // => { '_': _, 'jq': $ } */ function partialRight(func) { return createWrapper(func, 32, null, slice(arguments, 1)); } /** * Creates a function that, when executed, will only call the `func` function * at most once per every `wait` milliseconds. Provide an options object to * indicate that `func` should be invoked on the leading and/or trailing edge * of the `wait` timeout. Subsequent calls to the throttled function will * return the result of the last `func` call. * * Note: If `leading` and `trailing` options are `true` `func` will be called * on the trailing edge of the timeout only if the the throttled function is * invoked more than once during the `wait` timeout. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to throttle. * @param {number} wait The number of milliseconds to throttle executions to. * @param {Object} [options] The options object. * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout. * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * * // avoid excessively updating the position while scrolling * var throttled = _.throttle(updatePosition, 100); * jQuery(window).on('scroll', throttled); * * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { * 'trailing': false * })); */ function throttle(func, wait, options) { var leading = true, trailing = true; if (!isFunction(func)) { throw new TypeError; } if (options === false) { leading = false; } else if (isObject(options)) { leading = 'leading' in options ? options.leading : leading; trailing = 'trailing' in options ? options.trailing : trailing; } debounceOptions.leading = leading; debounceOptions.maxWait = wait; debounceOptions.trailing = trailing; return debounce(func, wait, debounceOptions); } /** * Creates a function that provides `value` to the wrapper function as its * first argument. Additional arguments provided to the function are appended * to those provided to the wrapper function. The wrapper is executed with * the `this` binding of the created function. * * @static * @memberOf _ * @category Functions * @param {*} value The value to wrap. * @param {Function} wrapper The wrapper function. * @returns {Function} Returns the new function. * @example * * var p = _.wrap(_.escape, function(func, text) { * return '

' + func(text) + '

'; * }); * * p('Fred, Wilma, & Pebbles'); * // => '

Fred, Wilma, & Pebbles

' */ function wrap(value, wrapper) { return createWrapper(wrapper, 16, [value]); } /*--------------------------------------------------------------------------*/ /** * Creates a function that returns `value`. * * @static * @memberOf _ * @category Utilities * @param {*} value The value to return from the new function. * @returns {Function} Returns the new function. * @example * * var object = { 'name': 'fred' }; * var getter = _.constant(object); * getter() === object; * // => true */ function constant(value) { return function() { return value; }; } /** * Produces a callback bound to an optional `thisArg`. If `func` is a property * name the created callback will return the property value for a given element. * If `func` is an object the created callback will return `true` for elements * that contain the equivalent object properties, otherwise it will return `false`. * * @static * @memberOf _ * @category Utilities * @param {*} [func=identity] The value to convert to a callback. * @param {*} [thisArg] The `this` binding of the created callback. * @param {number} [argCount] The number of arguments the callback accepts. * @returns {Function} Returns a callback function. * @example * * var characters = [ * { 'name': 'barney', 'age': 36 }, * { 'name': 'fred', 'age': 40 } * ]; * * // wrap to create custom callback shorthands * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) { * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback); * return !match ? func(callback, thisArg) : function(object) { * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3]; * }; * }); * * _.filter(characters, 'age__gt38'); * // => [{ 'name': 'fred', 'age': 40 }] */ function createCallback(func, thisArg, argCount) { var type = typeof func; if (func == null || type == 'function') { return baseCreateCallback(func, thisArg, argCount); } // handle "_.pluck" style callback shorthands if (type != 'object') { return property(func); } var props = keys(func), key = props[0], a = func[key]; // handle "_.where" style callback shorthands if (props.length == 1 && a === a && !isObject(a)) { // fast path the common case of providing an object with a single // property containing a primitive value return function(object) { var b = object[key]; return a === b && (a !== 0 || (1 / a == 1 / b)); }; } return function(object) { var length = props.length, result = false; while (length--) { if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) { break; } } return result; }; } /** * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their * corresponding HTML entities. * * @static * @memberOf _ * @category Utilities * @param {string} string The string to escape. * @returns {string} Returns the escaped string. * @example * * _.escape('Fred, Wilma, & Pebbles'); * // => 'Fred, Wilma, & Pebbles' */ function escape(string) { return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar); } /** * This method returns the first argument provided to it. * * @static * @memberOf _ * @category Utilities * @param {*} value Any value. * @returns {*} Returns `value`. * @example * * var object = { 'name': 'fred' }; * _.identity(object) === object; * // => true */ function identity(value) { return value; } /** * Adds function properties of a source object to the destination object. * If `object` is a function methods will be added to its prototype as well. * * @static * @memberOf _ * @category Utilities * @param {Function|Object} [object=lodash] object The destination object. * @param {Object} source The object of functions to add. * @param {Object} [options] The options object. * @param {boolean} [options.chain=true] Specify whether the functions added are chainable. * @example * * function capitalize(string) { * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); * } * * _.mixin({ 'capitalize': capitalize }); * _.capitalize('fred'); * // => 'Fred' * * _('fred').capitalize().value(); * // => 'Fred' * * _.mixin({ 'capitalize': capitalize }, { 'chain': false }); * _('fred').capitalize(); * // => 'Fred' */ function mixin(object, source, options) { var chain = true, methodNames = source && functions(source); if (!source || (!options && !methodNames.length)) { if (options == null) { options = source; } ctor = lodashWrapper; source = object; object = lodash; methodNames = functions(source); } if (options === false) { chain = false; } else if (isObject(options) && 'chain' in options) { chain = options.chain; } var ctor = object, isFunc = isFunction(ctor); forEach(methodNames, function(methodName) { var func = object[methodName] = source[methodName]; if (isFunc) { ctor.prototype[methodName] = function() { var chainAll = this.__chain__, value = this.__wrapped__, args = [value]; push.apply(args, arguments); var result = func.apply(object, args); if (chain || chainAll) { if (value === result && isObject(result)) { return this; } result = new ctor(result); result.__chain__ = chainAll; } return result; }; } }); } /** * Reverts the '_' variable to its previous value and returns a reference to * the `lodash` function. * * @static * @memberOf _ * @category Utilities * @returns {Function} Returns the `lodash` function. * @example * * var lodash = _.noConflict(); */ function noConflict() { context._ = oldDash; return this; } /** * A no-operation function. * * @static * @memberOf _ * @category Utilities * @example * * var object = { 'name': 'fred' }; * _.noop(object) === undefined; * // => true */ function noop() { // no operation performed } /** * Gets the number of milliseconds that have elapsed since the Unix epoch * (1 January 1970 00:00:00 UTC). * * @static * @memberOf _ * @category Utilities * @example * * var stamp = _.now(); * _.defer(function() { console.log(_.now() - stamp); }); * // => logs the number of milliseconds it took for the deferred function to be called */ var now = isNative(now = Date.now) && now || function() { return new Date().getTime(); }; /** * Converts the given value into an integer of the specified radix. * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the * `value` is a hexadecimal, in which case a `radix` of `16` is used. * * Note: This method avoids differences in native ES3 and ES5 `parseInt` * implementations. See http://es5.github.io/#E. * * @static * @memberOf _ * @category Utilities * @param {string} value The value to parse. * @param {number} [radix] The radix used to interpret the value to parse. * @returns {number} Returns the new integer value. * @example * * _.parseInt('08'); * // => 8 */ var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) { // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt` return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0); }; /** * Creates a "_.pluck" style function, which returns the `key` value of a * given object. * * @static * @memberOf _ * @category Utilities * @param {string} key The name of the property to retrieve. * @returns {Function} Returns the new function. * @example * * var characters = [ * { 'name': 'fred', 'age': 40 }, * { 'name': 'barney', 'age': 36 } * ]; * * var getName = _.property('name'); * * _.map(characters, getName); * // => ['barney', 'fred'] * * _.sortBy(characters, getName); * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] */ function property(key) { return function(object) { return object[key]; }; } /** * Produces a random number between `min` and `max` (inclusive). If only one * argument is provided a number between `0` and the given number will be * returned. If `floating` is truey or either `min` or `max` are floats a * floating-point number will be returned instead of an integer. * * @static * @memberOf _ * @category Utilities * @param {number} [min=0] The minimum possible value. * @param {number} [max=1] The maximum possible value. * @param {boolean} [floating=false] Specify returning a floating-point number. * @returns {number} Returns a random number. * @example * * _.random(0, 5); * // => an integer between 0 and 5 * * _.random(5); * // => also an integer between 0 and 5 * * _.random(5, true); * // => a floating-point number between 0 and 5 * * _.random(1.2, 5.2); * // => a floating-point number between 1.2 and 5.2 */ function random(min, max, floating) { var noMin = min == null, noMax = max == null; if (floating == null) { if (typeof min == 'boolean' && noMax) { floating = min; min = 1; } else if (!noMax && typeof max == 'boolean') { floating = max; noMax = true; } } if (noMin && noMax) { max = 1; } min = +min || 0; if (noMax) { max = min; min = 0; } else { max = +max || 0; } if (floating || min % 1 || max % 1) { var rand = nativeRandom(); return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max); } return baseRandom(min, max); } /** * Resolves the value of property `key` on `object`. If `key` is a function * it will be invoked with the `this` binding of `object` and its result returned, * else the property value is returned. If `object` is falsey then `undefined` * is returned. * * @static * @memberOf _ * @category Utilities * @param {Object} object The object to inspect. * @param {string} key The name of the property to resolve. * @returns {*} Returns the resolved value. * @example * * var object = { * 'cheese': 'crumpets', * 'stuff': function() { * return 'nonsense'; * } * }; * * _.result(object, 'cheese'); * // => 'crumpets' * * _.result(object, 'stuff'); * // => 'nonsense' */ function result(object, key) { if (object) { var value = object[key]; return isFunction(value) ? object[key]() : value; } } /** * A micro-templating method that handles arbitrary delimiters, preserves * whitespace, and correctly escapes quotes within interpolated code. * * Note: In the development build, `_.template` utilizes sourceURLs for easier * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl * * For more information on precompiling templates see: * http://lodash.com/custom-builds * * For more information on Chrome extension sandboxes see: * http://developer.chrome.com/stable/extensions/sandboxingEval.html * * @static * @memberOf _ * @category Utilities * @param {string} text The template text. * @param {Object} data The data object used to populate the text. * @param {Object} [options] The options object. * @param {RegExp} [options.escape] The "escape" delimiter. * @param {RegExp} [options.evaluate] The "evaluate" delimiter. * @param {Object} [options.imports] An object to import into the template as local variables. * @param {RegExp} [options.interpolate] The "interpolate" delimiter. * @param {string} [sourceURL] The sourceURL of the template's compiled source. * @param {string} [variable] The data object variable name. * @returns {Function|string} Returns a compiled function when no `data` object * is given, else it returns the interpolated text. * @example * * // using the "interpolate" delimiter to create a compiled template * var compiled = _.template('hello <%= name %>'); * compiled({ 'name': 'fred' }); * // => 'hello fred' * * // using the "escape" delimiter to escape HTML in data property values * _.template('<%- value %>', { 'value': ' lodash-2.4.1/test/underscore.html0000644000175000017500000001401012247303154015145 0ustar ovdovd Underscore Test Suite
lodash-2.4.1/test/test.js0000644000175000017500000074764212247303154013453 0ustar ovdovd;(function(root, undefined) { 'use strict'; /** Used to store Lo-Dash to test for bad shim detection */ var lodashBadShim = root.lodashBadShim; /** Method and object shortcuts */ var phantom = root.phantom, amd = root.define && define.amd, argv = root.process && process.argv, document = !phantom && root.document, body = document && document.body, create = Object.create, freeze = Object.freeze, noop = function() {}, params = root.arguments, push = Array.prototype.push, slice = Array.prototype.slice, system = root.system, toString = Object.prototype.toString, Worker = document && root.Worker; /** The file path of the Lo-Dash file to test */ var filePath = (function() { var min = 0, result = []; if (phantom) { result = params = phantom.args; } else if (system) { min = 1; result = params = system.args; } else if (argv) { min = 2; result = params = argv; } else if (params) { result = params; } var last = result[result.length - 1]; result = (result.length > min && !/test(?:\.js)?$/.test(last)) ? last : '../lodash.js'; if (!amd) { try { return require('fs').realpathSync(result); } catch(e) { } } return result; }()); /** The `ui` object */ var ui = root.ui || (root.ui = { 'buildPath': filePath, 'loaderPath': '', 'urlParams': {} }); /** The basename of the Lo-Dash file to test */ var basename = /[\w.-]+$/.exec(filePath)[0]; /** Used to indicate testing a modularized build */ var isModularize = ui.isModularize || /\b(?:commonjs|(index|main)\.js|lodash-(?:amd|node)|modularize|npm)\b/.test([ui.buildPath, ui.urlParams.build, basename]); /** Detect if testing `npm` modules */ var isNpm = isModularize && /\bnpm\b/.test([ui.buildPath, ui.urlParams.build]); /** Detect if running in Java */ var isJava = !document && !!root.java; /** Detects if running in a PhantomJS web page */ var isPhantomPage = typeof callPhantom == 'function'; /** Detect if running in Rhino */ var isRhino = isJava && typeof global == 'function' && global().Array === root.Array; /** Use a single "load" function */ var load = (typeof require == 'function' && !amd) ? require : (isJava && root.load); /** The unit testing framework */ var QUnit = (function() { return root.QUnit || ( root.addEventListener || (root.addEventListener = noop), root.setTimeout || (root.setTimeout = noop), root.QUnit = load('../vendor/qunit/qunit/qunit.js') || root.QUnit, (load('../vendor/qunit-extras/qunit-extras.js') || { 'runInContext': noop }).runInContext(root), addEventListener === noop && delete root.addEventListener, root.QUnit ); }()); /*--------------------------------------------------------------------------*/ // log params passed to `test.js` if (params) { console.log('test.js invoked with arguments: ' + JSON.stringify(slice.call(params))); } // exit early if going to run tests in a PhantomJS web page if (phantom && isModularize) { var page = require('webpage').create(); page.open(filePath, function(status) { if (status != 'success') { console.log('PhantomJS failed to load page: ' + filePath); phantom.exit(1); } }); page.onCallback = function(details) { phantom.exit(details.failed ? 1 : 0); }; page.onConsoleMessage = function(message) { console.log(message); }; page.onInitialized = function() { page.evaluate(function() { document.addEventListener('DOMContentLoaded', function() { QUnit.done(callPhantom); }); }); }; return; } /*--------------------------------------------------------------------------*/ /** The `lodash` function to test */ var _ = root._ || (root._ = ( _ = load(filePath) || root._, _ = _._ || _, (_.runInContext ? _.runInContext(root) : _) )); /** Used to pass falsey values to methods */ var falsey = [, '', 0, false, NaN, null, undefined]; /** Used to pass empty values to methods */ var empties = [[], {}].concat(falsey.slice(1)); /** Used as the size when optimizations are enabled for large arrays */ var largeArraySize = 75; /** Used to set property descriptors */ var defineProperty = (function() { try { var o = {}, func = Object.defineProperty, result = func(o, o, o) && func; } catch(e) { } return result; }()); /** Used to check problem JScript properties (a.k.a. the [[DontEnum]] bug) */ var shadowedProps = [ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf' ]; /** Used to check problem JScript properties too */ var shadowedObject = _.invert(shadowedProps); /** * Removes all own enumerable properties from a given object. * * @private * @param {Object} object The object to empty. */ function emptyObject(object) { _.forOwn(object, function(value, key, object) { delete object[key]; }); } /** * Skips a given number of tests with a passing result. * * @private * @param {number} [count=1] The number of tests to skip. */ function skipTest(count) { count || (count = 1); while (count--) { ok(true, 'test skipped'); } } /*--------------------------------------------------------------------------*/ // add values from other realms (function() { if (!amd) { try { emptyObject(require.cache); _.extend(_, require('vm').runInNewContext([ '({', "'_arguments': (function() { return arguments; }(1, 2, 3)),", "'_array': [1, 2, 3],", "'_boolean': new Boolean(false),", "'_date': new Date,", "'_function': function() {},", "'_nan': NaN,", "'_null': null,", "'_number': new Number(0),", "'_object': { 'a': 1, 'b': 2, 'c': 3 },", "'_regexp': /x/,", "'_string': new String('a'),", "'_undefined': undefined,", '})' ].join('\n'))); // set bad shims Array._isArray = Array.isArray; Array.isArray = function() {}; Date._now = Date.now; Date.now = function() {}; Function.prototype._bind = Function.prototype.bind; Function.prototype.bind = function() { return function() {}; }; Object._create = Object.create; Object.create = function() {}; Object._defineProperty = Object.defineProperty; Object.defineProperty = function() {}; Object._getPrototypeOf = Object.getPrototypeOf; Object.getPrototypeOf = function() {}; Object._keys = Object.keys; Object.keys = function() {}; // load Lo-Dash and expose it to the bad shims lodashBadShim = (lodashBadShim = require(filePath))._ || lodashBadShim; // restore native methods Array.isArray = Array._isArray; Date.now = Date._now; Function.prototype.bind = Function.prototype._bind; Object.create = Object._create; Object.defineProperty = Object._defineProperty; Object.getPrototypeOf = Object._getPrototypeOf; Object.keys = Object._keys; delete Array._isArray; delete Date._now; delete Function.prototype._bind; delete Object._create; delete Object._defineProperty; delete Object._getPrototypeOf; delete Object._keys; } catch(e) { } } if (!_._object && document) { var iframe = document.createElement('iframe'); iframe.frameBorder = iframe.height = iframe.width = 0; body.appendChild(iframe); var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc; idoc.write([ '
lodash-2.4.1/LICENSE.txt0000644000175000017500000000232612247303154012761 0ustar ovdovdCopyright 2012-2013 The Dojo Foundation Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.lodash-2.4.1/package.json0000644000175000017500000000333312247303154013423 0ustar ovdovd{ "name": "lodash", "version": "2.4.1", "description": "A utility library delivering consistency, customization, performance, & extras.", "homepage": "http://lodash.com/", "license": "MIT", "main": "dist/lodash.js", "keywords": ["amd", "browser", "client", "customize", "functional", "server", "util"], "author": "John-David Dalton (http://allyoucanleet.com/)", "contributors": [ "John-David Dalton (http://allyoucanleet.com/)", "Blaine Bublitz (http://www.iceddev.com/)", "Kit Cambridge (http://kitcambridge.be/)", "Mathias Bynens (http://mathiasbynens.be/)" ], "bugs": "https://github.com/lodash/lodash/issues", "repository": { "type": "git", "url": "https://github.com/lodash/lodash.git" }, "engines": ["node", "rhino"], "files": [ "LICENSE.txt", "lodash.js", "dist/lodash.js", "dist/lodash.min.js", "dist/lodash.compat.js", "dist/lodash.compat.min.js", "dist/lodash.underscore.js", "dist/lodash.underscore.min.js" ], "jam": { "main": "dist/lodash.compat.js", "include": [ "LICENSE.txt", "dist/lodash.js", "dist/lodash.min.js", "dist/lodash.compat.js", "dist/lodash.compat.min.js", "dist/lodash.underscore.js", "dist/lodash.underscore.min.js" ] }, "volo": { "type": "directory", "ignore": [ ".*", "*.custom.*", "*.min.*", "*.template.*", "*.map", "*.md", "lodash.js", "index.js", "bower.json", "component.json", "doc", "modularize", "node_modules", "perf", "test", "vendor" ] } } lodash-2.4.1/vendor/0000755000175000017500000000000012247303154012430 5ustar ovdovdlodash-2.4.1/vendor/firebug-lite/0000755000175000017500000000000012247303154015006 5ustar ovdovdlodash-2.4.1/vendor/firebug-lite/src/0000755000175000017500000000000012247303154015575 5ustar ovdovdlodash-2.4.1/vendor/firebug-lite/src/firebug-lite-debug.js0000755000175000017500000352307012247303154021612 0ustar ovdovd(function(){ /*!************************************************************* * * Firebug Lite 1.4.0 * * Copyright (c) 2007, Parakey Inc. * Released under BSD license. * More information: http://getfirebug.com/firebuglite * **************************************************************/ /*! * CSS selectors powered by: * * Sizzle CSS Selector Engine - v1.0 * Copyright 2009, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ /** @namespace describe lib */ // FIXME: xxxpedro if we use "var FBL = {}" the FBL won't appear in the DOM Panel in IE var FBL = {}; ( /** @scope s_lib @this FBL */ function() { // ************************************************************************************************ // ************************************************************************************************ // Constants var productionDir = "http://getfirebug.com/releases/lite/"; var bookmarkletVersion = 4; // ************************************************************************************************ var reNotWhitespace = /[^\s]/; var reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/; // Globals this.reJavascript = /\s*javascript:\s*(.*)/; this.reChrome = /chrome:\/\/([^\/]*)\//; this.reFile = /file:\/\/([^\/]*)\//; // ************************************************************************************************ // properties var userAgent = navigator.userAgent.toLowerCase(); this.isFirefox = /firefox/.test(userAgent); this.isOpera = /opera/.test(userAgent); this.isSafari = /webkit/.test(userAgent); this.isIE = /msie/.test(userAgent) && !/opera/.test(userAgent); this.isIE6 = /msie 6/i.test(navigator.appVersion); this.browserVersion = (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1]; this.isIElt8 = this.isIE && (this.browserVersion-0 < 8); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.NS = null; this.pixelsPerInch = null; // ************************************************************************************************ // Namespaces var namespaces = []; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.ns = function(fn) { var ns = {}; namespaces.push(fn, ns); return ns; }; var FBTrace = null; this.initialize = function() { // Firebug Lite is already running in persistent mode so we just quit if (window.firebug && firebug.firebuglite || window.console && console.firebuglite) return; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize environment // point the FBTrace object to the local variable if (FBL.FBTrace) FBTrace = FBL.FBTrace; else FBTrace = FBL.FBTrace = {}; // check if the actual window is a persisted chrome context var isChromeContext = window.Firebug && typeof window.Firebug.SharedEnv == "object"; // chrome context of the persistent application if (isChromeContext) { // TODO: xxxpedro persist - make a better synchronization sharedEnv = window.Firebug.SharedEnv; delete window.Firebug.SharedEnv; FBL.Env = sharedEnv; FBL.Env.isChromeContext = true; FBTrace.messageQueue = FBL.Env.traceMessageQueue; } // non-persistent application else { FBL.NS = document.documentElement.namespaceURI; FBL.Env.browser = window; FBL.Env.destroy = destroyEnvironment; if (document.documentElement.getAttribute("debug") == "true") FBL.Env.Options.startOpened = true; // find the URL location of the loaded application findLocation(); // TODO: get preferences here... // The problem is that we don't have the Firebug object yet, so we can't use // Firebug.loadPrefs. We're using the Store module directly instead. var prefs = FBL.Store.get("FirebugLite") || {}; FBL.Env.DefaultOptions = FBL.Env.Options; FBL.Env.Options = FBL.extend(FBL.Env.Options, prefs.options || {}); if (FBL.isFirefox && typeof FBL.Env.browser.console == "object" && FBL.Env.browser.console.firebug && FBL.Env.Options.disableWhenFirebugActive) return; } // exposes the FBL to the global namespace when in debug mode if (FBL.Env.isDebugMode) { FBL.Env.browser.FBL = FBL; } // check browser compatibilities this.isQuiksMode = FBL.Env.browser.document.compatMode == "BackCompat"; this.isIEQuiksMode = this.isIE && this.isQuiksMode; this.isIEStantandMode = this.isIE && !this.isQuiksMode; this.noFixedPosition = this.isIE6 || this.isIEQuiksMode; // after creating/synchronizing the environment, initialize the FBTrace module if (FBL.Env.Options.enableTrace) FBTrace.initialize(); if (FBTrace.DBG_INITIALIZE && isChromeContext) FBTrace.sysout("FBL.initialize - persistent application", "initialize chrome context"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize namespaces if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces BEGIN"); for (var i = 0; i < namespaces.length; i += 2) { var fn = namespaces[i]; var ns = namespaces[i+1]; fn.apply(ns); } if (FBTrace.DBG_INITIALIZE) { FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces END"); FBTrace.sysout("FBL waitForDocument", "waiting document load"); } FBL.Ajax.initialize(); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // finish environment initialization FBL.Firebug.loadPrefs(); if (FBL.Env.Options.enablePersistent) { // TODO: xxxpedro persist - make a better synchronization if (isChromeContext) { FBL.FirebugChrome.clone(FBL.Env.FirebugChrome); } else { FBL.Env.FirebugChrome = FBL.FirebugChrome; FBL.Env.traceMessageQueue = FBTrace.messageQueue; } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // wait document load waitForDocument(); }; var waitForDocument = function waitForDocument() { // document.body not available in XML+XSL documents in Firefox var doc = FBL.Env.browser.document; var body = doc.getElementsByTagName("body")[0]; if (body) { calculatePixelsPerInch(doc, body); onDocumentLoad(); } else setTimeout(waitForDocument, 50); }; var onDocumentLoad = function onDocumentLoad() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL onDocumentLoad", "document loaded"); // fix IE6 problem with cache of background images, causing a lot of flickering if (FBL.isIE6) fixIE6BackgroundImageCache(); // chrome context of the persistent application if (FBL.Env.Options.enablePersistent && FBL.Env.isChromeContext) { // finally, start the application in the chrome context FBL.Firebug.initialize(); // if is not development mode, remove the shared environment cache object // used to synchronize the both persistent contexts if (!FBL.Env.isDevelopmentMode) { sharedEnv.destroy(); sharedEnv = null; } } // non-persistent application else { FBL.FirebugChrome.create(); } }; // ************************************************************************************************ // Env var sharedEnv; this.Env = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Env Options (will be transported to Firebug options) Options: { saveCookies: true, saveWindowPosition: false, saveCommandLineHistory: false, startOpened: false, startInNewWindow: false, showIconWhenHidden: true, overrideConsole: true, ignoreFirebugElements: true, disableWhenFirebugActive: true, disableXHRListener: false, disableResourceFetching: false, enableTrace: false, enablePersistent: false }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Library location Location: { sourceDir: null, baseDir: null, skinDir: null, skin: null, app: null }, skin: "xp", useLocalSkin: false, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Env states isDevelopmentMode: false, isDebugMode: false, isChromeContext: false, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Env references browser: null, chrome: null }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var destroyEnvironment = function destroyEnvironment() { setTimeout(function() { FBL = null; }, 100); }; // ************************************************************************************************ // Library location var findLocation = function findLocation() { var reFirebugFile = /(firebug-lite(?:-\w+)?(?:\.js|\.jgz))(?:#(.+))?$/; var reGetFirebugSite = /(?:http|https):\/\/getfirebug.com\//; var isGetFirebugSite; var rePath = /^(.*\/)/; var reProtocol = /^\w+:\/\//; var path = null; var doc = document; // Firebug Lite 1.3.0 bookmarklet identification var script = doc.getElementById("FirebugLite"); var scriptSrc; var hasSrcAttribute = true; // If the script was loaded via bookmarklet, we already have the script tag if (script) { scriptSrc = script.src; file = reFirebugFile.exec(scriptSrc); var version = script.getAttribute("FirebugLite"); var number = version ? parseInt(version) : 0; if (!version || !number || number < bookmarkletVersion) { FBL.Env.bookmarkletOutdated = true; } } // otherwise we must search for the correct script tag else { for(var i=0, s=doc.getElementsByTagName("script"), si; si=s[i]; i++) { var file = null; if ( si.nodeName.toLowerCase() == "script" ) { if (file = reFirebugFile.exec(si.getAttribute("firebugSrc"))) { scriptSrc = si.getAttribute("firebugSrc"); hasSrcAttribute = false; } else if (file = reFirebugFile.exec(si.src)) { scriptSrc = si.src; } else continue; script = si; break; } } } // mark the script tag to be ignored by Firebug Lite if (script) script.firebugIgnore = true; if (file) { var fileName = file[1]; var fileOptions = file[2]; // absolute path if (reProtocol.test(scriptSrc)) { path = rePath.exec(scriptSrc)[1]; } // relative path else { var r = rePath.exec(scriptSrc); var src = r ? r[1] : scriptSrc; var backDir = /^((?:\.\.\/)+)(.*)/.exec(src); var reLastDir = /^(.*\/)[^\/]+\/$/; path = rePath.exec(location.href)[1]; // "../some/path" if (backDir) { var j = backDir[1].length/3; var p; while (j-- > 0) path = reLastDir.exec(path)[1]; path += backDir[2]; } else if(src.indexOf("/") != -1) { // "./some/path" if(/^\.\/./.test(src)) { path += src.substring(2); } // "/some/path" else if(/^\/./.test(src)) { var domain = /^(\w+:\/\/[^\/]+)/.exec(path); path = domain[1] + src; } // "some/path" else { path += src; } } } } FBL.Env.isChromeExtension = script && script.getAttribute("extension") == "Chrome"; if (FBL.Env.isChromeExtension) { path = productionDir; FBL.Env.bookmarkletOutdated = false; script = {innerHTML: "{showIconWhenHidden:false}"}; } isGetFirebugSite = reGetFirebugSite.test(path); if (isGetFirebugSite && path.indexOf("/releases/lite/") == -1) { // See Issue 4587 - If we are loading the script from getfirebug.com shortcut, like // https://getfirebug.com/firebug-lite.js, then we must manually add the full path, // otherwise the Env.Location will hold the wrong path, which will in turn lead to // undesirable effects like the problem in Issue 4587 path += "releases/lite/" + (fileName == "firebug-lite-beta.js" ? "beta/" : "latest/"); } var m = path && path.match(/([^\/]+)\/$/) || null; if (path && m) { var Env = FBL.Env; // Always use the local skin when running in the same domain // See Issue 3554: Firebug Lite should use local images when loaded locally Env.useLocalSkin = path.indexOf(location.protocol + "//" + location.host + "/") == 0 && // but we cannot use the locan skin when loaded from getfirebug.com, otherwise // the bookmarklet won't work when visiting getfirebug.com !isGetFirebugSite; // detecting development and debug modes via file name if (fileName == "firebug-lite-dev.js") { Env.isDevelopmentMode = true; Env.isDebugMode = true; } else if (fileName == "firebug-lite-debug.js") { Env.isDebugMode = true; } // process the if (Env.browser.document.documentElement.getAttribute("debug") == "true") { Env.Options.startOpened = true; } // process the Script URL Options if (fileOptions) { var options = fileOptions.split(","); for (var i = 0, length = options.length; i < length; i++) { var option = options[i]; var name, value; if (option.indexOf("=") != -1) { var parts = option.split("="); name = parts[0]; value = eval(unescape(parts[1])); } else { name = option; value = true; } if (name == "debug") { Env.isDebugMode = !!value; } else if (name in Env.Options) { Env.Options[name] = value; } else { Env[name] = value; } } } // process the Script JSON Options if (hasSrcAttribute) { var innerOptions = FBL.trim(script.innerHTML); if (innerOptions) { var innerOptionsObject = eval("(" + innerOptions + ")"); for (var name in innerOptionsObject) { var value = innerOptionsObject[name]; if (name == "debug") { Env.isDebugMode = !!value; } else if (name in Env.Options) { Env.Options[name] = value; } else { Env[name] = value; } } } } if (!Env.Options.saveCookies) FBL.Store.remove("FirebugLite"); // process the Debug Mode if (Env.isDebugMode) { Env.Options.startOpened = true; Env.Options.enableTrace = true; Env.Options.disableWhenFirebugActive = false; } var loc = Env.Location; var isProductionRelease = path.indexOf(productionDir) != -1; loc.sourceDir = path; loc.baseDir = path.substr(0, path.length - m[1].length - 1); loc.skinDir = (isProductionRelease ? path : loc.baseDir) + "skin/" + Env.skin + "/"; loc.skin = loc.skinDir + "firebug.html"; loc.app = path + fileName; } else { throw new Error("Firebug Error: Library path not found"); } }; // ************************************************************************************************ // Basics this.bind = function() // fn, thisObject, args => thisObject.fn(args, arguments); { var args = cloneArray(arguments), fn = args.shift(), object = args.shift(); return function() { return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments)); }; }; this.bindFixed = function() // fn, thisObject, args => thisObject.fn(args); { var args = cloneArray(arguments), fn = args.shift(), object = args.shift(); return function() { return fn.apply(object, args); }; }; this.extend = function(l, r) { var newOb = {}; for (var n in l) newOb[n] = l[n]; for (var n in r) newOb[n] = r[n]; return newOb; }; this.descend = function(prototypeParent, childProperties) { function protoSetter() {}; protoSetter.prototype = prototypeParent; var newOb = new protoSetter(); for (var n in childProperties) newOb[n] = childProperties[n]; return newOb; }; this.append = function(l, r) { for (var n in r) l[n] = r[n]; return l; }; this.keys = function(map) // At least sometimes the keys will be on user-level window objects { var keys = []; try { for (var name in map) // enumeration is safe keys.push(name); // name is string, safe } catch (exc) { // Sometimes we get exceptions trying to iterate properties } return keys; // return is safe }; this.values = function(map) { var values = []; try { for (var name in map) { try { values.push(map[name]); } catch (exc) { // Sometimes we get exceptions trying to access properties if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.values FAILED ", exc); } } } catch (exc) { // Sometimes we get exceptions trying to iterate properties if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.values FAILED ", exc); } return values; }; this.remove = function(list, item) { for (var i = 0; i < list.length; ++i) { if (list[i] == item) { list.splice(i, 1); break; } } }; this.sliceArray = function(array, index) { var slice = []; for (var i = index; i < array.length; ++i) slice.push(array[i]); return slice; }; function cloneArray(array, fn) { var newArray = []; if (fn) for (var i = 0; i < array.length; ++i) newArray.push(fn(array[i])); else for (var i = 0; i < array.length; ++i) newArray.push(array[i]); return newArray; } function extendArray(array, array2) { var newArray = []; newArray.push.apply(newArray, array); newArray.push.apply(newArray, array2); return newArray; } this.extendArray = extendArray; this.cloneArray = cloneArray; function arrayInsert(array, index, other) { for (var i = 0; i < other.length; ++i) array.splice(i+index, 0, other[i]); return array; } // ************************************************************************************************ this.createStyleSheet = function(doc, url) { //TODO: xxxpedro //var style = doc.createElementNS("http://www.w3.org/1999/xhtml", "style"); var style = this.createElement("link"); style.setAttribute("charset","utf-8"); style.firebugIgnore = true; style.setAttribute("rel", "stylesheet"); style.setAttribute("type", "text/css"); style.setAttribute("href", url); //TODO: xxxpedro //style.innerHTML = this.getResource(url); return style; }; this.addStyleSheet = function(doc, style) { var heads = doc.getElementsByTagName("head"); if (heads.length) heads[0].appendChild(style); else doc.documentElement.appendChild(style); }; this.appendStylesheet = function(doc, uri) { // Make sure the stylesheet is not appended twice. if (this.$(uri, doc)) return; var styleSheet = this.createStyleSheet(doc, uri); styleSheet.setAttribute("id", uri); this.addStyleSheet(doc, styleSheet); }; this.addScript = function(doc, id, src) { var element = doc.createElementNS("http://www.w3.org/1999/xhtml", "html:script"); element.setAttribute("type", "text/javascript"); element.setAttribute("id", id); if (!FBTrace.DBG_CONSOLE) FBL.unwrapObject(element).firebugIgnore = true; element.innerHTML = src; if (doc.documentElement) doc.documentElement.appendChild(element); else { // See issue 1079, the svg test case gives this error if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.addScript doc has no documentElement:", doc); } return element; }; // ************************************************************************************************ this.getStyle = this.isIE ? function(el, name) { return el.currentStyle[name] || el.style[name] || undefined; } : function(el, name) { return el.ownerDocument.defaultView.getComputedStyle(el,null)[name] || el.style[name] || undefined; }; // ************************************************************************************************ // Whitespace and Entity conversions var entityConversionLists = this.entityConversionLists = { normal : { whitespace : { '\t' : '\u200c\u2192', '\n' : '\u200c\u00b6', '\r' : '\u200c\u00ac', ' ' : '\u200c\u00b7' } }, reverse : { whitespace : { ' ' : '\t', ' ' : '\n', '\u200c\u2192' : '\t', '\u200c\u00b6' : '\n', '\u200c\u00ac' : '\r', '\u200c\u00b7' : ' ' } } }; var normal = entityConversionLists.normal, reverse = entityConversionLists.reverse; function addEntityMapToList(ccode, entity) { var lists = Array.prototype.slice.call(arguments, 2), len = lists.length, ch = String.fromCharCode(ccode); for (var i = 0; i < len; i++) { var list = lists[i]; normal[list]=normal[list] || {}; normal[list][ch] = '&' + entity + ';'; reverse[list]=reverse[list] || {}; reverse[list]['&' + entity + ';'] = ch; } }; var e = addEntityMapToList, white = 'whitespace', text = 'text', attr = 'attributes', css = 'css', editor = 'editor'; e(0x0022, 'quot', attr, css); e(0x0026, 'amp', attr, text, css); e(0x0027, 'apos', css); e(0x003c, 'lt', attr, text, css); e(0x003e, 'gt', attr, text, css); e(0xa9, 'copy', text, editor); e(0xae, 'reg', text, editor); e(0x2122, 'trade', text, editor); // See http://en.wikipedia.org/wiki/Dash e(0x2012, '#8210', attr, text, editor); // figure dash e(0x2013, 'ndash', attr, text, editor); // en dash e(0x2014, 'mdash', attr, text, editor); // em dash e(0x2015, '#8213', attr, text, editor); // horizontal bar e(0x00a0, 'nbsp', attr, text, white, editor); e(0x2002, 'ensp', attr, text, white, editor); e(0x2003, 'emsp', attr, text, white, editor); e(0x2009, 'thinsp', attr, text, white, editor); e(0x200c, 'zwnj', attr, text, white, editor); e(0x200d, 'zwj', attr, text, white, editor); e(0x200e, 'lrm', attr, text, white, editor); e(0x200f, 'rlm', attr, text, white, editor); e(0x200b, '#8203', attr, text, white, editor); // zero-width space (ZWSP) //************************************************************************************************ // Entity escaping var entityConversionRegexes = { normal : {}, reverse : {} }; var escapeEntitiesRegEx = { normal : function(list) { var chars = []; for ( var ch in list) { chars.push(ch); } return new RegExp('([' + chars.join('') + '])', 'gm'); }, reverse : function(list) { var chars = []; for ( var ch in list) { chars.push(ch); } return new RegExp('(' + chars.join('|') + ')', 'gm'); } }; function getEscapeRegexp(direction, lists) { var name = '', re; var groups = [].concat(lists); for (i = 0; i < groups.length; i++) { name += groups[i].group; } re = entityConversionRegexes[direction][name]; if (!re) { var list = {}; if (groups.length > 1) { for ( var i = 0; i < groups.length; i++) { var aList = entityConversionLists[direction][groups[i].group]; for ( var item in aList) list[item] = aList[item]; } } else if (groups.length==1) { list = entityConversionLists[direction][groups[0].group]; // faster for special case } else { list = {}; // perhaps should print out an error here? } re = entityConversionRegexes[direction][name] = escapeEntitiesRegEx[direction](list); } return re; }; function createSimpleEscape(name, direction) { return function(value) { var list = entityConversionLists[direction][name]; return String(value).replace( getEscapeRegexp(direction, { group : name, list : list }), function(ch) { return list[ch]; } ); }; }; function escapeGroupsForEntities(str, lists) { lists = [].concat(lists); var re = getEscapeRegexp('normal', lists), split = String(str).split(re), len = split.length, results = [], cur, r, i, ri = 0, l, list, last = ''; if (!len) return [ { str : String(str), group : '', name : '' } ]; for (i = 0; i < len; i++) { cur = split[i]; if (cur == '') continue; for (l = 0; l < lists.length; l++) { list = lists[l]; r = entityConversionLists.normal[list.group][cur]; // if (cur == ' ' && list.group == 'whitespace' && last == ' ') // only show for runs of more than one space // r = ' '; if (r) { results[ri] = { 'str' : r, 'class' : list['class'], 'extra' : list.extra[cur] ? list['class'] + list.extra[cur] : '' }; break; } } // last=cur; if (!r) results[ri] = { 'str' : cur, 'class' : '', 'extra' : '' }; ri++; } return results; }; this.escapeGroupsForEntities = escapeGroupsForEntities; function unescapeEntities(str, lists) { var re = getEscapeRegexp('reverse', lists), split = String(str).split(re), len = split.length, results = [], cur, r, i, ri = 0, l, list; if (!len) return str; lists = [].concat(lists); for (i = 0; i < len; i++) { cur = split[i]; if (cur == '') continue; for (l = 0; l < lists.length; l++) { list = lists[l]; r = entityConversionLists.reverse[list.group][cur]; if (r) { results[ri] = r; break; } } if (!r) results[ri] = cur; ri++; } return results.join('') || ''; }; // ************************************************************************************************ // String escaping var escapeForTextNode = this.escapeForTextNode = createSimpleEscape('text', 'normal'); var escapeForHtmlEditor = this.escapeForHtmlEditor = createSimpleEscape('editor', 'normal'); var escapeForElementAttribute = this.escapeForElementAttribute = createSimpleEscape('attributes', 'normal'); var escapeForCss = this.escapeForCss = createSimpleEscape('css', 'normal'); // deprecated compatibility functions //this.deprecateEscapeHTML = createSimpleEscape('text', 'normal'); //this.deprecatedUnescapeHTML = createSimpleEscape('text', 'reverse'); //this.escapeHTML = deprecated("use appropriate escapeFor... function", this.deprecateEscapeHTML); //this.unescapeHTML = deprecated("use appropriate unescapeFor... function", this.deprecatedUnescapeHTML); var escapeForSourceLine = this.escapeForSourceLine = createSimpleEscape('text', 'normal'); var unescapeWhitespace = createSimpleEscape('whitespace', 'reverse'); this.unescapeForTextNode = function(str) { if (Firebug.showTextNodesWithWhitespace) str = unescapeWhitespace(str); if (!Firebug.showTextNodesWithEntities) str = escapeForElementAttribute(str); return str; }; this.escapeNewLines = function(value) { return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n"); }; this.stripNewLines = function(value) { return typeof(value) == "string" ? value.replace(/[\r\n]/g, " ") : value; }; this.escapeJS = function(value) { return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace('"', '\\"', "g"); }; function escapeHTMLAttribute(value) { function replaceChars(ch) { switch (ch) { case "&": return "&"; case "'": return apos; case '"': return quot; } return "?"; }; var apos = "'", quot = """, around = '"'; if( value.indexOf('"') == -1 ) { quot = '"'; apos = "'"; } else if( value.indexOf("'") == -1 ) { quot = '"'; around = "'"; } return around + (String(value).replace(/[&'"]/g, replaceChars)) + around; } function escapeHTML(value) { function replaceChars(ch) { switch (ch) { case "<": return "<"; case ">": return ">"; case "&": return "&"; case "'": return "'"; case '"': return """; } return "?"; }; return String(value).replace(/[<>&"']/g, replaceChars); } this.escapeHTML = escapeHTML; this.cropString = function(text, limit) { text = text + ""; if (!limit) var halfLimit = 50; else var halfLimit = limit / 2; if (text.length > limit) return this.escapeNewLines(text.substr(0, halfLimit) + "..." + text.substr(text.length-halfLimit)); else return this.escapeNewLines(text); }; this.isWhitespace = function(text) { return !reNotWhitespace.exec(text); }; this.splitLines = function(text) { var reSplitLines2 = /.*(:?\r\n|\n|\r)?/mg; var lines; if (text.match) { lines = text.match(reSplitLines2); } else { var str = text+""; lines = str.match(reSplitLines2); } lines.pop(); return lines; }; // ************************************************************************************************ this.safeToString = function(ob) { if (this.isIE) { try { // FIXME: xxxpedro this is failing in IE for the global "external" object return ob + ""; } catch(E) { FBTrace.sysout("Lib.safeToString() failed for ", ob); return ""; } } try { if (ob && "toString" in ob && typeof ob.toString == "function") return ob.toString(); } catch (exc) { // xxxpedro it is not safe to use ob+""? return ob + ""; ///return "[an object with no toString() function]"; } }; // ************************************************************************************************ this.hasProperties = function(ob) { try { for (var name in ob) return true; } catch (exc) {} return false; }; // ************************************************************************************************ // String Util var reTrim = /^\s+|\s+$/g; this.trim = function(s) { return s.replace(reTrim, ""); }; // ************************************************************************************************ // Empty this.emptyFn = function(){}; // ************************************************************************************************ // Visibility this.isVisible = function(elt) { /* if (elt instanceof XULElement) { //FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+ elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n"); return (!elt.hidden && !elt.collapsed); } /**/ return this.getStyle(elt, "visibility") != "hidden" && ( elt.offsetWidth > 0 || elt.offsetHeight > 0 || elt.tagName in invisibleTags || elt.namespaceURI == "http://www.w3.org/2000/svg" || elt.namespaceURI == "http://www.w3.org/1998/Math/MathML" ); }; this.collapse = function(elt, collapsed) { // IE6 doesn't support the [collapsed] CSS selector. IE7 does support the selector, // but it is causing a bug (the element disappears when you set the "collapsed" // attribute, but it doesn't appear when you remove the attribute. So, for those // cases, we need to use the class attribute. if (this.isIElt8) { if (collapsed) this.setClass(elt, "collapsed"); else this.removeClass(elt, "collapsed"); } else elt.setAttribute("collapsed", collapsed ? "true" : "false"); }; this.obscure = function(elt, obscured) { if (obscured) this.setClass(elt, "obscured"); else this.removeClass(elt, "obscured"); }; this.hide = function(elt, hidden) { elt.style.visibility = hidden ? "hidden" : "visible"; }; this.clearNode = function(node) { var nodeName = " " + node.nodeName.toLowerCase() + " "; var ignoreTags = " table tbody thead tfoot th tr td "; // IE can't use innerHTML of table elements if (this.isIE && ignoreTags.indexOf(nodeName) != -1) this.eraseNode(node); else node.innerHTML = ""; }; this.eraseNode = function(node) { while (node.lastChild) node.removeChild(node.lastChild); }; // ************************************************************************************************ // Window iteration this.iterateWindows = function(win, handler) { if (!win || !win.document) return; handler(win); if (win == top || !win.frames) return; // XXXjjb hack for chromeBug for (var i = 0; i < win.frames.length; ++i) { var subWin = win.frames[i]; if (subWin != win) this.iterateWindows(subWin, handler); } }; this.getRootWindow = function(win) { for (; win; win = win.parent) { if (!win.parent || win == win.parent || !this.instanceOf(win.parent, "Window")) return win; } return null; }; // ************************************************************************************************ // Graphics this.getClientOffset = function(elt) { var addOffset = function addOffset(elt, coords, view) { var p = elt.offsetParent; ///var style = isIE ? elt.currentStyle : view.getComputedStyle(elt, ""); var chrome = Firebug.chrome; if (elt.offsetLeft) ///coords.x += elt.offsetLeft + parseInt(style.borderLeftWidth); coords.x += elt.offsetLeft + chrome.getMeasurementInPixels(elt, "borderLeft"); if (elt.offsetTop) ///coords.y += elt.offsetTop + parseInt(style.borderTopWidth); coords.y += elt.offsetTop + chrome.getMeasurementInPixels(elt, "borderTop"); if (p) { if (p.nodeType == 1) addOffset(p, coords, view); } else { var otherView = isIE ? elt.ownerDocument.parentWindow : elt.ownerDocument.defaultView; // IE will fail when reading the frameElement property of a popup window. // We don't need it anyway once it is outside the (popup) viewport, so we're // ignoring the frameElement check when the window is a popup if (!otherView.opener && otherView.frameElement) addOffset(otherView.frameElement, coords, otherView); } }; var isIE = this.isIE; var coords = {x: 0, y: 0}; if (elt) { var view = isIE ? elt.ownerDocument.parentWindow : elt.ownerDocument.defaultView; addOffset(elt, coords, view); } return coords; }; this.getViewOffset = function(elt, singleFrame) { function addOffset(elt, coords, view) { var p = elt.offsetParent; coords.x += elt.offsetLeft - (p ? p.scrollLeft : 0); coords.y += elt.offsetTop - (p ? p.scrollTop : 0); if (p) { if (p.nodeType == 1) { var parentStyle = view.getComputedStyle(p, ""); if (parentStyle.position != "static") { coords.x += parseInt(parentStyle.borderLeftWidth); coords.y += parseInt(parentStyle.borderTopWidth); if (p.localName == "TABLE") { coords.x += parseInt(parentStyle.paddingLeft); coords.y += parseInt(parentStyle.paddingTop); } else if (p.localName == "BODY") { var style = view.getComputedStyle(elt, ""); coords.x += parseInt(style.marginLeft); coords.y += parseInt(style.marginTop); } } else if (p.localName == "BODY") { coords.x += parseInt(parentStyle.borderLeftWidth); coords.y += parseInt(parentStyle.borderTopWidth); } var parent = elt.parentNode; while (p != parent) { coords.x -= parent.scrollLeft; coords.y -= parent.scrollTop; parent = parent.parentNode; } addOffset(p, coords, view); } } else { if (elt.localName == "BODY") { var style = view.getComputedStyle(elt, ""); coords.x += parseInt(style.borderLeftWidth); coords.y += parseInt(style.borderTopWidth); var htmlStyle = view.getComputedStyle(elt.parentNode, ""); coords.x -= parseInt(htmlStyle.paddingLeft); coords.y -= parseInt(htmlStyle.paddingTop); } if (elt.scrollLeft) coords.x += elt.scrollLeft; if (elt.scrollTop) coords.y += elt.scrollTop; var win = elt.ownerDocument.defaultView; if (win && (!singleFrame && win.frameElement)) addOffset(win.frameElement, coords, win); } } var coords = {x: 0, y: 0}; if (elt) addOffset(elt, coords, elt.ownerDocument.defaultView); return coords; }; this.getLTRBWH = function(elt) { var bcrect, dims = {"left": 0, "top": 0, "right": 0, "bottom": 0, "width": 0, "height": 0}; if (elt) { bcrect = elt.getBoundingClientRect(); dims.left = bcrect.left; dims.top = bcrect.top; dims.right = bcrect.right; dims.bottom = bcrect.bottom; if(bcrect.width) { dims.width = bcrect.width; dims.height = bcrect.height; } else { dims.width = dims.right - dims.left; dims.height = dims.bottom - dims.top; } } return dims; }; this.applyBodyOffsets = function(elt, clientRect) { var od = elt.ownerDocument; if (!od.body) return clientRect; var style = od.defaultView.getComputedStyle(od.body, null); var pos = style.getPropertyValue('position'); if(pos === 'absolute' || pos === 'relative') { var borderLeft = parseInt(style.getPropertyValue('border-left-width').replace('px', ''),10) || 0; var borderTop = parseInt(style.getPropertyValue('border-top-width').replace('px', ''),10) || 0; var paddingLeft = parseInt(style.getPropertyValue('padding-left').replace('px', ''),10) || 0; var paddingTop = parseInt(style.getPropertyValue('padding-top').replace('px', ''),10) || 0; var marginLeft = parseInt(style.getPropertyValue('margin-left').replace('px', ''),10) || 0; var marginTop = parseInt(style.getPropertyValue('margin-top').replace('px', ''),10) || 0; var offsetX = borderLeft + paddingLeft + marginLeft; var offsetY = borderTop + paddingTop + marginTop; clientRect.left -= offsetX; clientRect.top -= offsetY; clientRect.right -= offsetX; clientRect.bottom -= offsetY; } return clientRect; }; this.getOffsetSize = function(elt) { return {width: elt.offsetWidth, height: elt.offsetHeight}; }; this.getOverflowParent = function(element) { for (var scrollParent = element.parentNode; scrollParent; scrollParent = scrollParent.offsetParent) { if (scrollParent.scrollHeight > scrollParent.offsetHeight) return scrollParent; } }; this.isScrolledToBottom = function(element) { var onBottom = (element.scrollTop + element.offsetHeight) == element.scrollHeight; if (FBTrace.DBG_CONSOLE) FBTrace.sysout("isScrolledToBottom offsetHeight: "+element.offsetHeight +" onBottom:"+onBottom); return onBottom; }; this.scrollToBottom = function(element) { element.scrollTop = element.scrollHeight; if (FBTrace.DBG_CONSOLE) { FBTrace.sysout("scrollToBottom reset scrollTop "+element.scrollTop+" = "+element.scrollHeight); if (element.scrollHeight == element.offsetHeight) FBTrace.sysout("scrollToBottom attempt to scroll non-scrollable element "+element, element); } return (element.scrollTop == element.scrollHeight); }; this.move = function(element, x, y) { element.style.left = x + "px"; element.style.top = y + "px"; }; this.resize = function(element, w, h) { element.style.width = w + "px"; element.style.height = h + "px"; }; this.linesIntoCenterView = function(element, scrollBox) // {before: int, after: int} { if (!scrollBox) scrollBox = this.getOverflowParent(element); if (!scrollBox) return; var offset = this.getClientOffset(element); var topSpace = offset.y - scrollBox.scrollTop; var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight) - (offset.y + element.offsetHeight); if (topSpace < 0 || bottomSpace < 0) { var split = (scrollBox.clientHeight/2); var centerY = offset.y - split; scrollBox.scrollTop = centerY; topSpace = split; bottomSpace = split - element.offsetHeight; } return {before: Math.round((topSpace/element.offsetHeight) + 0.5), after: Math.round((bottomSpace/element.offsetHeight) + 0.5) }; }; this.scrollIntoCenterView = function(element, scrollBox, notX, notY) { if (!element) return; if (!scrollBox) scrollBox = this.getOverflowParent(element); if (!scrollBox) return; var offset = this.getClientOffset(element); if (!notY) { var topSpace = offset.y - scrollBox.scrollTop; var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight) - (offset.y + element.offsetHeight); if (topSpace < 0 || bottomSpace < 0) { var centerY = offset.y - (scrollBox.clientHeight/2); scrollBox.scrollTop = centerY; } } if (!notX) { var leftSpace = offset.x - scrollBox.scrollLeft; var rightSpace = (scrollBox.scrollLeft + scrollBox.clientWidth) - (offset.x + element.clientWidth); if (leftSpace < 0 || rightSpace < 0) { var centerX = offset.x - (scrollBox.clientWidth/2); scrollBox.scrollLeft = centerX; } } if (FBTrace.DBG_SOURCEFILES) FBTrace.sysout("lib.scrollIntoCenterView ","Element:"+element.innerHTML); }; // ************************************************************************************************ // CSS var cssKeywordMap = null; var cssPropNames = null; var cssColorNames = null; var imageRules = null; this.getCSSKeywordsByProperty = function(propName) { if (!cssKeywordMap) { cssKeywordMap = {}; for (var name in this.cssInfo) { var list = []; var types = this.cssInfo[name]; for (var i = 0; i < types.length; ++i) { var keywords = this.cssKeywords[types[i]]; if (keywords) list.push.apply(list, keywords); } cssKeywordMap[name] = list; } } return propName in cssKeywordMap ? cssKeywordMap[propName] : []; }; this.getCSSPropertyNames = function() { if (!cssPropNames) { cssPropNames = []; for (var name in this.cssInfo) cssPropNames.push(name); } return cssPropNames; }; this.isColorKeyword = function(keyword) { if (keyword == "transparent") return false; if (!cssColorNames) { cssColorNames = []; var colors = this.cssKeywords["color"]; for (var i = 0; i < colors.length; ++i) cssColorNames.push(colors[i].toLowerCase()); var systemColors = this.cssKeywords["systemColor"]; for (var i = 0; i < systemColors.length; ++i) cssColorNames.push(systemColors[i].toLowerCase()); } return cssColorNames.indexOf ? // Array.indexOf is not available in IE cssColorNames.indexOf(keyword.toLowerCase()) != -1 : (" " + cssColorNames.join(" ") + " ").indexOf(" " + keyword.toLowerCase() + " ") != -1; }; this.isImageRule = function(rule) { if (!imageRules) { imageRules = []; for (var i in this.cssInfo) { var r = i.toLowerCase(); var suffix = "image"; if (r.match(suffix + "$") == suffix || r == "background") imageRules.push(r); } } return imageRules.indexOf ? // Array.indexOf is not available in IE imageRules.indexOf(rule.toLowerCase()) != -1 : (" " + imageRules.join(" ") + " ").indexOf(" " + rule.toLowerCase() + " ") != -1; }; this.copyTextStyles = function(fromNode, toNode, style) { var view = this.isIE ? fromNode.ownerDocument.parentWindow : fromNode.ownerDocument.defaultView; if (view) { if (!style) style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, ""); toNode.style.fontFamily = style.fontFamily; // TODO: xxxpedro need to create a FBL.getComputedStyle() because IE // returns wrong computed styles for inherited properties (like font-*) // // Also would be good to create a FBL.getStyle() toNode.style.fontSize = style.fontSize; toNode.style.fontWeight = style.fontWeight; toNode.style.fontStyle = style.fontStyle; return style; } }; this.copyBoxStyles = function(fromNode, toNode, style) { var view = this.isIE ? fromNode.ownerDocument.parentWindow : fromNode.ownerDocument.defaultView; if (view) { if (!style) style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, ""); toNode.style.marginTop = style.marginTop; toNode.style.marginRight = style.marginRight; toNode.style.marginBottom = style.marginBottom; toNode.style.marginLeft = style.marginLeft; toNode.style.borderTopWidth = style.borderTopWidth; toNode.style.borderRightWidth = style.borderRightWidth; toNode.style.borderBottomWidth = style.borderBottomWidth; toNode.style.borderLeftWidth = style.borderLeftWidth; return style; } }; this.readBoxStyles = function(style) { var styleNames = { "margin-top": "marginTop", "margin-right": "marginRight", "margin-left": "marginLeft", "margin-bottom": "marginBottom", "border-top-width": "borderTop", "border-right-width": "borderRight", "border-left-width": "borderLeft", "border-bottom-width": "borderBottom", "padding-top": "paddingTop", "padding-right": "paddingRight", "padding-left": "paddingLeft", "padding-bottom": "paddingBottom", "z-index": "zIndex" }; var styles = {}; for (var styleName in styleNames) styles[styleNames[styleName]] = parseInt(style.getPropertyCSSValue(styleName).cssText) || 0; if (FBTrace.DBG_INSPECT) FBTrace.sysout("readBoxStyles ", styles); return styles; }; this.getBoxFromStyles = function(style, element) { var args = this.readBoxStyles(style); args.width = element.offsetWidth - (args.paddingLeft+args.paddingRight+args.borderLeft+args.borderRight); args.height = element.offsetHeight - (args.paddingTop+args.paddingBottom+args.borderTop+args.borderBottom); return args; }; this.getElementCSSSelector = function(element) { var label = element.localName.toLowerCase(); if (element.id) label += "#" + element.id; if (element.hasAttribute("class")) label += "." + element.getAttribute("class").split(" ")[0]; return label; }; this.getURLForStyleSheet= function(styleSheet) { //http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet. For inline style sheets, the value of this attribute is null. return (styleSheet.href ? styleSheet.href : styleSheet.ownerNode.ownerDocument.URL); }; this.getDocumentForStyleSheet = function(styleSheet) { while (styleSheet.parentStyleSheet && !styleSheet.ownerNode) { styleSheet = styleSheet.parentStyleSheet; } if (styleSheet.ownerNode) return styleSheet.ownerNode.ownerDocument; }; /** * Retrieves the instance number for a given style sheet. The instance number * is sheet's index within the set of all other sheets whose URL is the same. */ this.getInstanceForStyleSheet = function(styleSheet, ownerDocument) { // System URLs are always unique (or at least we are making this assumption) if (FBL.isSystemStyleSheet(styleSheet)) return 0; // ownerDocument is an optional hint for performance if (FBTrace.DBG_CSS) FBTrace.sysout("getInstanceForStyleSheet: " + styleSheet.href + " " + styleSheet.media.mediaText + " " + (styleSheet.ownerNode && FBL.getElementXPath(styleSheet.ownerNode)), ownerDocument); ownerDocument = ownerDocument || FBL.getDocumentForStyleSheet(styleSheet); var ret = 0, styleSheets = ownerDocument.styleSheets, href = styleSheet.href; for (var i = 0; i < styleSheets.length; i++) { var curSheet = styleSheets[i]; if (FBTrace.DBG_CSS) FBTrace.sysout("getInstanceForStyleSheet: compare href " + i + " " + curSheet.href + " " + curSheet.media.mediaText + " " + (curSheet.ownerNode && FBL.getElementXPath(curSheet.ownerNode))); if (curSheet == styleSheet) break; if (curSheet.href == href) ret++; } return ret; }; // ************************************************************************************************ // HTML and XML Serialization var getElementType = this.getElementType = function(node) { if (isElementXUL(node)) return 'xul'; else if (isElementSVG(node)) return 'svg'; else if (isElementMathML(node)) return 'mathml'; else if (isElementXHTML(node)) return 'xhtml'; else if (isElementHTML(node)) return 'html'; }; var getElementSimpleType = this.getElementSimpleType = function(node) { if (isElementSVG(node)) return 'svg'; else if (isElementMathML(node)) return 'mathml'; else return 'html'; }; var isElementHTML = this.isElementHTML = function(node) { return node.nodeName == node.nodeName.toUpperCase(); }; var isElementXHTML = this.isElementXHTML = function(node) { return node.nodeName == node.nodeName.toLowerCase(); }; var isElementMathML = this.isElementMathML = function(node) { return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML'; }; var isElementSVG = this.isElementSVG = function(node) { return node.namespaceURI == 'http://www.w3.org/2000/svg'; }; var isElementXUL = this.isElementXUL = function(node) { return node instanceof XULElement; }; this.isSelfClosing = function(element) { if (isElementSVG(element) || isElementMathML(element)) return true; var tag = element.localName.toLowerCase(); return (this.selfClosingTags.hasOwnProperty(tag)); }; this.getElementHTML = function(element) { var self=this; function toHTML(elt) { if (elt.nodeType == Node.ELEMENT_NODE) { if (unwrapObject(elt).firebugIgnore) return; html.push('<', elt.nodeName.toLowerCase()); for (var i = 0; i < elt.attributes.length; ++i) { var attr = elt.attributes[i]; // Hide attributes set by Firebug if (attr.localName.indexOf("firebug-") == 0) continue; // MathML if (attr.localName.indexOf("-moz-math") == 0) { // just hide for now continue; } html.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"'); } if (elt.firstChild) { html.push('>'); var pureText=true; for (var child = element.firstChild; child; child = child.nextSibling) pureText=pureText && (child.nodeType == Node.TEXT_NODE); if (pureText) html.push(escapeForHtmlEditor(elt.textContent)); else { for (var child = elt.firstChild; child; child = child.nextSibling) toHTML(child); } html.push(''); } else if (isElementSVG(elt) || isElementMathML(elt)) { html.push('/>'); } else if (self.isSelfClosing(elt)) { html.push((isElementXHTML(elt))?'/>':'>'); } else { html.push('>'); } } else if (elt.nodeType == Node.TEXT_NODE) html.push(escapeForTextNode(elt.textContent)); else if (elt.nodeType == Node.CDATA_SECTION_NODE) html.push(''); else if (elt.nodeType == Node.COMMENT_NODE) html.push(''); } var html = []; toHTML(element); return html.join(""); }; this.getElementXML = function(element) { function toXML(elt) { if (elt.nodeType == Node.ELEMENT_NODE) { if (unwrapObject(elt).firebugIgnore) return; xml.push('<', elt.nodeName.toLowerCase()); for (var i = 0; i < elt.attributes.length; ++i) { var attr = elt.attributes[i]; // Hide attributes set by Firebug if (attr.localName.indexOf("firebug-") == 0) continue; // MathML if (attr.localName.indexOf("-moz-math") == 0) { // just hide for now continue; } xml.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"'); } if (elt.firstChild) { xml.push('>'); for (var child = elt.firstChild; child; child = child.nextSibling) toXML(child); xml.push(''); } else xml.push('/>'); } else if (elt.nodeType == Node.TEXT_NODE) xml.push(elt.nodeValue); else if (elt.nodeType == Node.CDATA_SECTION_NODE) xml.push(''); else if (elt.nodeType == Node.COMMENT_NODE) xml.push(''); } var xml = []; toXML(element); return xml.join(""); }; // ************************************************************************************************ // CSS classes this.hasClass = function(node, name) // className, className, ... { // TODO: xxxpedro when lib.hasClass is called with more than 2 arguments? // this function can be optimized a lot if assumed 2 arguments only, // which seems to be what happens 99% of the time if (arguments.length == 2) return (' '+node.className+' ').indexOf(' '+name+' ') != -1; if (!node || node.nodeType != 1) return false; else { for (var i=1; i= 0) { var size = name.length; node.className = node.className.substr(0,index-1) + node.className.substr(index+size); } } }; this.toggleClass = function(elt, name) { if ((' '+elt.className+' ').indexOf(' '+name+' ') != -1) ///if (this.hasClass(elt, name)) this.removeClass(elt, name); else this.setClass(elt, name); }; this.setClassTimed = function(elt, name, context, timeout) { if (!timeout) timeout = 1300; if (elt.__setClassTimeout) context.clearTimeout(elt.__setClassTimeout); else this.setClass(elt, name); elt.__setClassTimeout = context.setTimeout(function() { delete elt.__setClassTimeout; FBL.removeClass(elt, name); }, timeout); }; this.cancelClassTimed = function(elt, name, context) { if (elt.__setClassTimeout) { FBL.removeClass(elt, name); context.clearTimeout(elt.__setClassTimeout); delete elt.__setClassTimeout; } }; // ************************************************************************************************ // DOM queries this.$ = function(id, doc) { if (doc) return doc.getElementById(id); else { return FBL.Firebug.chrome.document.getElementById(id); } }; this.$$ = function(selector, doc) { if (doc || !FBL.Firebug.chrome) return FBL.Firebug.Selector(selector, doc); else { return FBL.Firebug.Selector(selector, FBL.Firebug.chrome.document); } }; this.getChildByClass = function(node) // ,classname, classname, classname... { for (var i = 1; i < arguments.length; ++i) { var className = arguments[i]; var child = node.firstChild; node = null; for (; child; child = child.nextSibling) { if (this.hasClass(child, className)) { node = child; break; } } } return node; }; this.getAncestorByClass = function(node, className) { for (var parent = node; parent; parent = parent.parentNode) { if (this.hasClass(parent, className)) return parent; } return null; }; this.getElementsByClass = function(node, className) { var result = []; for (var child = node.firstChild; child; child = child.nextSibling) { if (this.hasClass(child, className)) result.push(child); } return result; }; this.getElementByClass = function(node, className) // className, className, ... { var args = cloneArray(arguments); args.splice(0, 1); for (var child = node.firstChild; child; child = child.nextSibling) { var args1 = cloneArray(args); args1.unshift(child); if (FBL.hasClass.apply(null, args1)) return child; else { var found = FBL.getElementByClass.apply(null, args1); if (found) return found; } } return null; }; this.isAncestor = function(node, potentialAncestor) { for (var parent = node; parent; parent = parent.parentNode) { if (parent == potentialAncestor) return true; } return false; }; this.getNextElement = function(node) { while (node && node.nodeType != 1) node = node.nextSibling; return node; }; this.getPreviousElement = function(node) { while (node && node.nodeType != 1) node = node.previousSibling; return node; }; this.getBody = function(doc) { if (doc.body) return doc.body; var body = doc.getElementsByTagName("body")[0]; if (body) return body; return doc.firstChild; // For non-HTML docs }; this.findNextDown = function(node, criteria) { if (!node) return null; for (var child = node.firstChild; child; child = child.nextSibling) { if (criteria(child)) return child; var next = this.findNextDown(child, criteria); if (next) return next; } }; this.findPreviousUp = function(node, criteria) { if (!node) return null; for (var child = node.lastChild; child; child = child.previousSibling) { var next = this.findPreviousUp(child, criteria); if (next) return next; if (criteria(child)) return child; } }; this.findNext = function(node, criteria, upOnly, maxRoot) { if (!node) return null; if (!upOnly) { var next = this.findNextDown(node, criteria); if (next) return next; } for (var sib = node.nextSibling; sib; sib = sib.nextSibling) { if (criteria(sib)) return sib; var next = this.findNextDown(sib, criteria); if (next) return next; } if (node.parentNode && node.parentNode != maxRoot) return this.findNext(node.parentNode, criteria, true); }; this.findPrevious = function(node, criteria, downOnly, maxRoot) { if (!node) return null; for (var sib = node.previousSibling; sib; sib = sib.previousSibling) { var prev = this.findPreviousUp(sib, criteria); if (prev) return prev; if (criteria(sib)) return sib; } if (!downOnly) { var next = this.findPreviousUp(node, criteria); if (next) return next; } if (node.parentNode && node.parentNode != maxRoot) { if (criteria(node.parentNode)) return node.parentNode; return this.findPrevious(node.parentNode, criteria, true); } }; this.getNextByClass = function(root, state) { var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }; return this.findNext(root, iter); }; this.getPreviousByClass = function(root, state) { var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }; return this.findPrevious(root, iter); }; this.isElement = function(o) { try { return o && this.instanceOf(o, "Element"); } catch (ex) { return false; } }; // ************************************************************************************************ // DOM Modification // TODO: xxxpedro use doc fragments in Context API var appendFragment = null; this.appendInnerHTML = function(element, html, referenceElement) { // if undefined, we must convert it to null otherwise it will throw an error in IE // when executing element.insertBefore(firstChild, referenceElement) referenceElement = referenceElement || null; var doc = element.ownerDocument; // doc.createRange not available in IE if (doc.createRange) { var range = doc.createRange(); // a helper object range.selectNodeContents(element); // the environment to interpret the html var fragment = range.createContextualFragment(html); // parse var firstChild = fragment.firstChild; element.insertBefore(fragment, referenceElement); } else { if (!appendFragment || appendFragment.ownerDocument != doc) appendFragment = doc.createDocumentFragment(); var div = doc.createElement("div"); div.innerHTML = html; var firstChild = div.firstChild; while (div.firstChild) appendFragment.appendChild(div.firstChild); element.insertBefore(appendFragment, referenceElement); div = null; } return firstChild; }; // ************************************************************************************************ // DOM creation this.createElement = function(tagName, properties) { properties = properties || {}; var doc = properties.document || FBL.Firebug.chrome.document; var element = doc.createElement(tagName); for(var name in properties) { if (name != "document") { element[name] = properties[name]; } } return element; }; this.createGlobalElement = function(tagName, properties) { properties = properties || {}; var doc = FBL.Env.browser.document; var element = this.NS && doc.createElementNS ? doc.createElementNS(FBL.NS, tagName) : doc.createElement(tagName); for(var name in properties) { var propname = name; if (FBL.isIE && name == "class") propname = "className"; if (name != "document") { element.setAttribute(propname, properties[name]); } } return element; }; //************************************************************************************************ this.safeGetWindowLocation = function(window) { try { if (window) { if (window.closed) return "(window.closed)"; if ("location" in window) return window.location+""; else return "(no window.location)"; } else return "(no context.window)"; } catch(exc) { if (FBTrace.DBG_WINDOWS || FBTrace.DBG_ERRORS) FBTrace.sysout("TabContext.getWindowLocation failed "+exc, exc); FBTrace.sysout("TabContext.getWindowLocation failed window:", window); return "(getWindowLocation: "+exc+")"; } }; // ************************************************************************************************ // Events this.isLeftClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && // others this.noKeyModifiers(event); }; this.isMiddleClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 4 : // IE "click" and "dblclick" button model event.button == 1) && this.noKeyModifiers(event); }; this.isRightClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 2 : // IE "click" and "dblclick" button model event.button == 2) && this.noKeyModifiers(event); }; this.noKeyModifiers = function(event) { return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey; }; this.isControlClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && this.isControl(event); }; this.isShiftClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && this.isShift(event); }; this.isControl = function(event) { return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey; }; this.isAlt = function(event) { return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey; }; this.isAltClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && this.isAlt(event); }; this.isControlShift = function(event) { return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey; }; this.isShift = function(event) { return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey; }; this.addEvent = function(object, name, handler, useCapture) { if (object.addEventListener) object.addEventListener(name, handler, useCapture); else object.attachEvent("on"+name, handler); }; this.removeEvent = function(object, name, handler, useCapture) { try { if (object.removeEventListener) object.removeEventListener(name, handler, useCapture); else object.detachEvent("on"+name, handler); } catch(e) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("FBL.removeEvent error: ", object, name); } }; this.cancelEvent = function(e, preventDefault) { if (!e) return; if (preventDefault) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; } if (e.stopPropagation) e.stopPropagation(); else e.cancelBubble = true; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.addGlobalEvent = function(name, handler) { var doc = this.Firebug.browser.document; var frames = this.Firebug.browser.window.frames; this.addEvent(doc, name, handler); if (this.Firebug.chrome.type == "popup") this.addEvent(this.Firebug.chrome.document, name, handler); for (var i = 0, frame; frame = frames[i]; i++) { try { this.addEvent(frame.document, name, handler); } catch(E) { // Avoid acess denied } } }; this.removeGlobalEvent = function(name, handler) { var doc = this.Firebug.browser.document; var frames = this.Firebug.browser.window.frames; this.removeEvent(doc, name, handler); if (this.Firebug.chrome.type == "popup") this.removeEvent(this.Firebug.chrome.document, name, handler); for (var i = 0, frame; frame = frames[i]; i++) { try { this.removeEvent(frame.document, name, handler); } catch(E) { // Avoid acess denied } } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.dispatch = function(listeners, name, args) { if (!listeners) return; try {/**/ if (typeof listeners.length != "undefined") { if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to "+listeners.length+" listeners"); for (var i = 0; i < listeners.length; ++i) { var listener = listeners[i]; if ( listener[name] ) listener[name].apply(listener, args); } } else { if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to listeners of an object"); for (var prop in listeners) { var listener = listeners[prop]; if ( listener[name] ) listener[name].apply(listener, args); } } } catch (exc) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout(" Exception in lib.dispatch "+ name, exc); //FBTrace.dumpProperties(" Exception in lib.dispatch listener", listener); } } /**/ }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var disableTextSelectionHandler = function(event) { FBL.cancelEvent(event, true); return false; }; this.disableTextSelection = function(e) { if (typeof e.onselectstart != "undefined") // IE this.addEvent(e, "selectstart", disableTextSelectionHandler); else // others { e.style.cssText = "user-select: none; -khtml-user-select: none; -moz-user-select: none;"; // canceling the event in FF will prevent the menu popups to close when clicking over // text-disabled elements if (!this.isFirefox) this.addEvent(e, "mousedown", disableTextSelectionHandler); } e.style.cursor = "default"; }; this.restoreTextSelection = function(e) { if (typeof e.onselectstart != "undefined") // IE this.removeEvent(e, "selectstart", disableTextSelectionHandler); else // others { e.style.cssText = "cursor: default;"; // canceling the event in FF will prevent the menu popups to close when clicking over // text-disabled elements if (!this.isFirefox) this.removeEvent(e, "mousedown", disableTextSelectionHandler); } }; // ************************************************************************************************ // DOM Events var eventTypes = { composition: [ "composition", "compositionstart", "compositionend" ], contextmenu: [ "contextmenu" ], drag: [ "dragenter", "dragover", "dragexit", "dragdrop", "draggesture" ], focus: [ "focus", "blur" ], form: [ "submit", "reset", "change", "select", "input" ], key: [ "keydown", "keyup", "keypress" ], load: [ "load", "beforeunload", "unload", "abort", "error" ], mouse: [ "mousedown", "mouseup", "click", "dblclick", "mouseover", "mouseout", "mousemove" ], mutation: [ "DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved", "DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument", "DOMAttrModified", "DOMCharacterDataModified" ], paint: [ "paint", "resize", "scroll" ], scroll: [ "overflow", "underflow", "overflowchanged" ], text: [ "text" ], ui: [ "DOMActivate", "DOMFocusIn", "DOMFocusOut" ], xul: [ "popupshowing", "popupshown", "popuphiding", "popuphidden", "close", "command", "broadcast", "commandupdate" ] }; this.getEventFamily = function(eventType) { if (!this.families) { this.families = {}; for (var family in eventTypes) { var types = eventTypes[family]; for (var i = 0; i < types.length; ++i) this.families[types[i]] = family; } } return this.families[eventType]; }; // ************************************************************************************************ // URLs this.getFileName = function(url) { var split = this.splitURLBase(url); return split.name; }; this.splitURLBase = function(url) { if (this.isDataURL(url)) return this.splitDataURL(url); return this.splitURLTrue(url); }; this.splitDataURL = function(url) { var mark = url.indexOf(':', 3); if (mark != 4) return false; // the first 5 chars must be 'data:' var point = url.indexOf(',', mark+1); if (point < mark) return false; // syntax error var props = { encodedContent: url.substr(point+1) }; var metadataBuffer = url.substr(mark+1, point); var metadata = metadataBuffer.split(';'); for (var i = 0; i < metadata.length; i++) { var nv = metadata[i].split('='); if (nv.length == 2) props[nv[0]] = nv[1]; } // Additional Firebug-specific properties if (props.hasOwnProperty('fileName')) { var caller_URL = decodeURIComponent(props['fileName']); var caller_split = this.splitURLTrue(caller_URL); if (props.hasOwnProperty('baseLineNumber')) // this means it's probably an eval() { props['path'] = caller_split.path; props['line'] = props['baseLineNumber']; var hint = decodeURIComponent(props['encodedContent'].substr(0,200)).replace(/\s*$/, ""); props['name'] = 'eval->'+hint; } else { props['name'] = caller_split.name; props['path'] = caller_split.path; } } else { if (!props.hasOwnProperty('path')) props['path'] = "data:"; if (!props.hasOwnProperty('name')) props['name'] = decodeURIComponent(props['encodedContent'].substr(0,200)).replace(/\s*$/, ""); } return props; }; this.splitURLTrue = function(url) { var m = reSplitFile.exec(url); if (!m) return {name: url, path: url}; else if (!m[2]) return {path: m[1], name: m[1]}; else return {path: m[1], name: m[2]+m[3]}; }; this.getFileExtension = function(url) { if (!url) return null; // Remove query string from the URL if any. var queryString = url.indexOf("?"); if (queryString != -1) url = url.substr(0, queryString); // Now get the file extension. var lastDot = url.lastIndexOf("."); return url.substr(lastDot+1); }; this.isSystemURL = function(url) { if (!url) return true; if (url.length == 0) return true; if (url[0] == 'h') return false; if (url.substr(0, 9) == "resource:") return true; else if (url.substr(0, 16) == "chrome://firebug") return true; else if (url == "XPCSafeJSObjectWrapper.cpp") return true; else if (url.substr(0, 6) == "about:") return true; else if (url.indexOf("firebug-service.js") != -1) return true; else return false; }; this.isSystemPage = function(win) { try { var doc = win.document; if (!doc) return false; // Detect pages for pretty printed XML if ((doc.styleSheets.length && doc.styleSheets[0].href == "chrome://global/content/xml/XMLPrettyPrint.css") || (doc.styleSheets.length > 1 && doc.styleSheets[1].href == "chrome://browser/skin/feeds/subscribe.css")) return true; return FBL.isSystemURL(win.location.href); } catch (exc) { // Sometimes documents just aren't ready to be manipulated here, but don't let that // gum up the works ERROR("tabWatcher.isSystemPage document not ready:"+ exc); return false; } }; this.isSystemStyleSheet = function(sheet) { var href = sheet && sheet.href; return href && FBL.isSystemURL(href); }; this.getURIHost = function(uri) { try { if (uri) return uri.host; else return ""; } catch (exc) { return ""; } }; this.isLocalURL = function(url) { if (url.substr(0, 5) == "file:") return true; else if (url.substr(0, 8) == "wyciwyg:") return true; else return false; }; this.isDataURL = function(url) { return (url && url.substr(0,5) == "data:"); }; this.getLocalPath = function(url) { if (this.isLocalURL(url)) { var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler); var file = fileHandler.getFileFromURLSpec(url); return file.path; } }; this.getURLFromLocalFile = function(file) { var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler); var URL = fileHandler.getURLSpecFromFile(file); return URL; }; this.getDataURLForContent = function(content, url) { // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10, var uri = "data:text/html;"; uri += "fileName="+encodeURIComponent(url)+ ","; uri += encodeURIComponent(content); return uri; }, this.getDomain = function(url) { var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url); return m ? m[1] : ""; }; this.getURLPath = function(url) { var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url); return m ? m[1] : ""; }; this.getPrettyDomain = function(url) { var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url); return m ? m[2] : ""; }; this.absoluteURL = function(url, baseURL) { return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g"); }; this.absoluteURLWithDots = function(url, baseURL) { if (url[0] == "?") return baseURL + url; var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/; var m = reURL.exec(url); if (m) return url; var m = reURL.exec(baseURL); if (!m) return ""; var head = m[1]; var tail = m[3]; if (url.substr(0, 2) == "//") return m[2] + url; else if (url[0] == "/") { return head + url; } else if (tail[tail.length-1] == "/") return baseURL + url; else { var parts = tail.split("/"); return head + parts.slice(0, parts.length-1).join("/") + "/" + url; } }; this.normalizeURL = function(url) // this gets called a lot, any performance improvement welcome { if (!url) return ""; // Replace one or more characters that are not forward-slash followed by /.., by space. if (url.length < 255) // guard against monsters. { // Replace one or more characters that are not forward-slash followed by /.., by space. url = url.replace(/[^\/]+\/\.\.\//, "", "g"); // Issue 1496, avoid # url = url.replace(/#.*/,""); // For some reason, JSDS reports file URLs like "file:/" instead of "file:///", so they // don't match up with the URLs we get back from the DOM url = url.replace(/file:\/([^\/])/g, "file:///$1"); if (url.indexOf('chrome:')==0) { var m = reChromeCase.exec(url); // 1 is package name, 2 is path if (m) { url = "chrome://"+m[1].toLowerCase()+"/"+m[2]; } } } return url; }; this.denormalizeURL = function(url) { return url.replace(/file:\/\/\//g, "file:/"); }; this.parseURLParams = function(url) { var q = url ? url.indexOf("?") : -1; if (q == -1) return []; var search = url.substr(q+1); var h = search.lastIndexOf("#"); if (h != -1) search = search.substr(0, h); if (!search) return []; return this.parseURLEncodedText(search); }; this.parseURLEncodedText = function(text) { var maxValueLength = 25000; var params = []; // Unescape '+' characters that are used to encode a space. // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt text = text.replace(/\+/g, " "); var args = text.split("&"); for (var i = 0; i < args.length; ++i) { try { var parts = args[i].split("="); if (parts.length == 2) { if (parts[1].length > maxValueLength) parts[1] = this.$STR("LargeData"); params.push({name: decodeURIComponent(parts[0]), value: decodeURIComponent(parts[1])}); } else params.push({name: decodeURIComponent(parts[0]), value: ""}); } catch (e) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("parseURLEncodedText EXCEPTION ", e); FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]); } } } params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; }); return params; }; // TODO: xxxpedro lib. why loops in domplate are requiring array in parameters // as in response/request headers and get/post parameters in Net module? this.parseURLParamsArray = function(url) { var q = url ? url.indexOf("?") : -1; if (q == -1) return []; var search = url.substr(q+1); var h = search.lastIndexOf("#"); if (h != -1) search = search.substr(0, h); if (!search) return []; return this.parseURLEncodedTextArray(search); }; this.parseURLEncodedTextArray = function(text) { var maxValueLength = 25000; var params = []; // Unescape '+' characters that are used to encode a space. // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt text = text.replace(/\+/g, " "); var args = text.split("&"); for (var i = 0; i < args.length; ++i) { try { var parts = args[i].split("="); if (parts.length == 2) { if (parts[1].length > maxValueLength) parts[1] = this.$STR("LargeData"); params.push({name: decodeURIComponent(parts[0]), value: [decodeURIComponent(parts[1])]}); } else params.push({name: decodeURIComponent(parts[0]), value: [""]}); } catch (e) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("parseURLEncodedText EXCEPTION ", e); FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]); } } } params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; }); return params; }; this.reEncodeURL = function(file, text) { var lines = text.split("\n"); var params = this.parseURLEncodedText(lines[lines.length-1]); var args = []; for (var i = 0; i < params.length; ++i) args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value)); var url = file.href; url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&"); return url; }; this.getResource = function(aURL) { try { var channel=ioService.newChannel(aURL,null,null); var input=channel.open(); return FBL.readFromStream(input); } catch (e) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.getResource FAILS for "+aURL, e); } }; this.parseJSONString = function(jsonString, originURL) { // See if this is a Prototype style *-secure request. var regex = new RegExp(/^\/\*-secure-([\s\S]*)\*\/\s*$/); var matches = regex.exec(jsonString); if (matches) { jsonString = matches[1]; if (jsonString[0] == "\\" && jsonString[1] == "n") jsonString = jsonString.substr(2); if (jsonString[jsonString.length-2] == "\\" && jsonString[jsonString.length-1] == "n") jsonString = jsonString.substr(0, jsonString.length-2); } if (jsonString.indexOf("&&&START&&&")) { regex = new RegExp(/&&&START&&& (.+) &&&END&&&/); matches = regex.exec(jsonString); if (matches) jsonString = matches[1]; } // throw on the extra parentheses jsonString = "(" + jsonString + ")"; ///var s = Components.utils.Sandbox(originURL); var jsonObject = null; try { ///jsonObject = Components.utils.evalInSandbox(jsonString, s); //jsonObject = Firebug.context.eval(jsonString); jsonObject = Firebug.context.evaluate(jsonString, null, null, function(){return null;}); } catch(e) { /*** if (e.message.indexOf("is not defined")) { var parts = e.message.split(" "); s[parts[0]] = function(str){ return str; }; try { jsonObject = Components.utils.evalInSandbox(jsonString, s); } catch(ex) { if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER) FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e); return null; } } else {/**/ if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER) FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e); return null; ///} } return jsonObject; }; // ************************************************************************************************ this.objectToString = function(object) { try { return object+""; } catch (exc) { return null; } }; // ************************************************************************************************ // Input Caret Position this.setSelectionRange = function(input, start, length) { if (input.createTextRange) { var range = input.createTextRange(); range.moveStart("character", start); range.moveEnd("character", length - input.value.length); range.select(); } else if (input.setSelectionRange) { input.setSelectionRange(start, length); input.focus(); } }; // ************************************************************************************************ // Input Selection Start / Caret Position this.getInputSelectionStart = function(input) { if (document.selection) { var range = input.ownerDocument.selection.createRange(); var text = range.text; //console.log("range", range.text); // if there is a selection, find the start position if (text) { return input.value.indexOf(text); } // if there is no selection, find the caret position else { range.moveStart("character", -input.value.length); return range.text.length; } } else if (typeof input.selectionStart != "undefined") return input.selectionStart; return 0; }; // ************************************************************************************************ // Opera Tab Fix function onOperaTabBlur(e) { if (this.lastKey == 9) this.focus(); }; function onOperaTabKeyDown(e) { this.lastKey = e.keyCode; }; function onOperaTabFocus(e) { this.lastKey = null; }; this.fixOperaTabKey = function(el) { el.onfocus = onOperaTabFocus; el.onblur = onOperaTabBlur; el.onkeydown = onOperaTabKeyDown; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.Property = function(object, name) { this.object = object; this.name = name; this.getObject = function() { return object[name]; }; }; this.ErrorCopy = function(message) { this.message = message; }; function EventCopy(event) { // Because event objects are destroyed arbitrarily by Gecko, we must make a copy of them to // represent them long term in the inspector. for (var name in event) { try { this[name] = event[name]; } catch (exc) { } } } this.EventCopy = EventCopy; // ************************************************************************************************ // Type Checking var toString = Object.prototype.toString; var reFunction = /^\s*function(\s+[\w_$][\w\d_$]*)?\s*\(/; this.isArray = function(object) { return toString.call(object) === '[object Array]'; }; this.isFunction = function(object) { if (!object) return false; try { // FIXME: xxxpedro this is failing in IE for the global "external" object return toString.call(object) === "[object Function]" || this.isIE && typeof object != "string" && reFunction.test(""+object); } catch (E) { FBTrace.sysout("Lib.isFunction() failed for ", object); return false; } }; // ************************************************************************************************ // Instance Checking this.instanceOf = function(object, className) { if (!object || typeof object != "object") return false; // Try to use the native instanceof operator. We can only use it when we know // exactly the window where the object is located at if (object.ownerDocument) { // find the correct window of the object var win = object.ownerDocument.defaultView || object.ownerDocument.parentWindow; // if the class is accessible in the window, uses the native instanceof operator // if the instanceof evaluates to "true" we can assume it is a instance, but if it // evaluates to "false" we must continue with the duck type detection below because // the native object may be extended, thus breaking the instanceof result // See Issue 3524: Firebug Lite Style Panel doesn't work if the native Element is extended if (className in win && object instanceof win[className]) return true; } // If the object doesn't have the ownerDocument property, we'll try to look at // the current context's window else { // TODO: xxxpedro context // Since we're not using yet a Firebug.context, we'll just use the top window // (browser) as a reference var win = Firebug.browser.window; if (className in win) return object instanceof win[className]; } // get the duck type model from the cache var cache = instanceCheckMap[className]; if (!cache) return false; // starts the hacky duck type detection for(var n in cache) { var obj = cache[n]; var type = typeof obj; obj = type == "object" ? obj : [obj]; for(var name in obj) { // avoid problems with extended native objects // See Issue 3524: Firebug Lite Style Panel doesn't work if the native Element is extended if (!obj.hasOwnProperty(name)) continue; var value = obj[name]; if( n == "property" && !(value in object) || n == "method" && !this.isFunction(object[value]) || n == "value" && (""+object[name]).toLowerCase() != (""+value).toLowerCase() ) return false; } } return true; }; var instanceCheckMap = { // DuckTypeCheck: // { // property: ["window", "document"], // method: "setTimeout", // value: {nodeType: 1} // }, Window: { property: ["window", "document"], method: "setTimeout" }, Document: { property: ["body", "cookie"], method: "getElementById" }, Node: { property: "ownerDocument", method: "appendChild" }, Element: { property: "tagName", value: {nodeType: 1} }, Location: { property: ["hostname", "protocol"], method: "assign" }, HTMLImageElement: { property: "useMap", value: { nodeType: 1, tagName: "img" } }, HTMLAnchorElement: { property: "hreflang", value: { nodeType: 1, tagName: "a" } }, HTMLInputElement: { property: "form", value: { nodeType: 1, tagName: "input" } }, HTMLButtonElement: { // ? }, HTMLFormElement: { method: "submit", value: { nodeType: 1, tagName: "form" } }, HTMLBodyElement: { }, HTMLHtmlElement: { }, CSSStyleRule: { property: ["selectorText", "style"] } }; // ************************************************************************************************ // DOM Constants /* Problems: - IE does not have window.Node, window.Element, etc - for (var name in Node.prototype) return nothing on FF */ var domMemberMap2 = {}; var domMemberMap2Sandbox = null; var getDomMemberMap2 = function(name) { if (!domMemberMap2Sandbox) { var doc = Firebug.chrome.document; var frame = doc.createElement("iframe"); frame.id = "FirebugSandbox"; frame.style.display = "none"; frame.src = "about:blank"; doc.body.appendChild(frame); domMemberMap2Sandbox = frame.window || frame.contentWindow; } var props = []; //var object = domMemberMap2Sandbox[name]; //object = object.prototype || object; var object = null; if (name == "Window") object = domMemberMap2Sandbox.window; else if (name == "Document") object = domMemberMap2Sandbox.document; else if (name == "HTMLScriptElement") object = domMemberMap2Sandbox.document.createElement("script"); else if (name == "HTMLAnchorElement") object = domMemberMap2Sandbox.document.createElement("a"); else if (name.indexOf("Element") != -1) { object = domMemberMap2Sandbox.document.createElement("div"); } if (object) { //object = object.prototype || object; //props = 'addEventListener,document,location,navigator,window'.split(','); for (var n in object) props.push(n); } /**/ return props; return extendArray(props, domMemberMap[name]); }; // xxxpedro experimental get DOM members this.getDOMMembers = function(object) { if (!domMemberCache) { FBL.domMemberCache = domMemberCache = {}; for (var name in domMemberMap) { var builtins = getDomMemberMap2(name); var cache = domMemberCache[name] = {}; /* if (name.indexOf("Element") != -1) { this.append(cache, this.getDOMMembers("Node")); this.append(cache, this.getDOMMembers("Element")); } /**/ for (var i = 0; i < builtins.length; ++i) cache[builtins[i]] = i; } } try { if (this.instanceOf(object, "Window")) { return domMemberCache.Window; } else if (this.instanceOf(object, "Document") || this.instanceOf(object, "XMLDocument")) { return domMemberCache.Document; } else if (this.instanceOf(object, "Location")) { return domMemberCache.Location; } else if (this.instanceOf(object, "HTMLImageElement")) { return domMemberCache.HTMLImageElement; } else if (this.instanceOf(object, "HTMLAnchorElement")) { return domMemberCache.HTMLAnchorElement; } else if (this.instanceOf(object, "HTMLInputElement")) { return domMemberCache.HTMLInputElement; } else if (this.instanceOf(object, "HTMLButtonElement")) { return domMemberCache.HTMLButtonElement; } else if (this.instanceOf(object, "HTMLFormElement")) { return domMemberCache.HTMLFormElement; } else if (this.instanceOf(object, "HTMLBodyElement")) { return domMemberCache.HTMLBodyElement; } else if (this.instanceOf(object, "HTMLHtmlElement")) { return domMemberCache.HTMLHtmlElement; } else if (this.instanceOf(object, "HTMLScriptElement")) { return domMemberCache.HTMLScriptElement; } else if (this.instanceOf(object, "HTMLTableElement")) { return domMemberCache.HTMLTableElement; } else if (this.instanceOf(object, "HTMLTableRowElement")) { return domMemberCache.HTMLTableRowElement; } else if (this.instanceOf(object, "HTMLTableCellElement")) { return domMemberCache.HTMLTableCellElement; } else if (this.instanceOf(object, "HTMLIFrameElement")) { return domMemberCache.HTMLIFrameElement; } else if (this.instanceOf(object, "SVGSVGElement")) { return domMemberCache.SVGSVGElement; } else if (this.instanceOf(object, "SVGElement")) { return domMemberCache.SVGElement; } else if (this.instanceOf(object, "Element")) { return domMemberCache.Element; } else if (this.instanceOf(object, "Text") || this.instanceOf(object, "CDATASection")) { return domMemberCache.Text; } else if (this.instanceOf(object, "Attr")) { return domMemberCache.Attr; } else if (this.instanceOf(object, "Node")) { return domMemberCache.Node; } else if (this.instanceOf(object, "Event") || this.instanceOf(object, "EventCopy")) { return domMemberCache.Event; } else return {}; } catch(E) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.getDOMMembers FAILED ", E); return {}; } }; /* this.getDOMMembers = function(object) { if (!domMemberCache) { domMemberCache = {}; for (var name in domMemberMap) { var builtins = domMemberMap[name]; var cache = domMemberCache[name] = {}; for (var i = 0; i < builtins.length; ++i) cache[builtins[i]] = i; } } try { if (this.instanceOf(object, "Window")) { return domMemberCache.Window; } else if (object instanceof Document || object instanceof XMLDocument) { return domMemberCache.Document; } else if (object instanceof Location) { return domMemberCache.Location; } else if (object instanceof HTMLImageElement) { return domMemberCache.HTMLImageElement; } else if (object instanceof HTMLAnchorElement) { return domMemberCache.HTMLAnchorElement; } else if (object instanceof HTMLInputElement) { return domMemberCache.HTMLInputElement; } else if (object instanceof HTMLButtonElement) { return domMemberCache.HTMLButtonElement; } else if (object instanceof HTMLFormElement) { return domMemberCache.HTMLFormElement; } else if (object instanceof HTMLBodyElement) { return domMemberCache.HTMLBodyElement; } else if (object instanceof HTMLHtmlElement) { return domMemberCache.HTMLHtmlElement; } else if (object instanceof HTMLScriptElement) { return domMemberCache.HTMLScriptElement; } else if (object instanceof HTMLTableElement) { return domMemberCache.HTMLTableElement; } else if (object instanceof HTMLTableRowElement) { return domMemberCache.HTMLTableRowElement; } else if (object instanceof HTMLTableCellElement) { return domMemberCache.HTMLTableCellElement; } else if (object instanceof HTMLIFrameElement) { return domMemberCache.HTMLIFrameElement; } else if (object instanceof SVGSVGElement) { return domMemberCache.SVGSVGElement; } else if (object instanceof SVGElement) { return domMemberCache.SVGElement; } else if (object instanceof Element) { return domMemberCache.Element; } else if (object instanceof Text || object instanceof CDATASection) { return domMemberCache.Text; } else if (object instanceof Attr) { return domMemberCache.Attr; } else if (object instanceof Node) { return domMemberCache.Node; } else if (object instanceof Event || object instanceof EventCopy) { return domMemberCache.Event; } else return {}; } catch(E) { return {}; } }; /**/ this.isDOMMember = function(object, propName) { var members = this.getDOMMembers(object); return members && propName in members; }; var domMemberCache = null; var domMemberMap = {}; domMemberMap.Window = [ "document", "frameElement", "innerWidth", "innerHeight", "outerWidth", "outerHeight", "screenX", "screenY", "pageXOffset", "pageYOffset", "scrollX", "scrollY", "scrollMaxX", "scrollMaxY", "status", "defaultStatus", "parent", "opener", "top", "window", "content", "self", "location", "history", "frames", "navigator", "screen", "menubar", "toolbar", "locationbar", "personalbar", "statusbar", "directories", "scrollbars", "fullScreen", "netscape", "java", "console", "Components", "controllers", "closed", "crypto", "pkcs11", "name", "property", "length", "sessionStorage", "globalStorage", "setTimeout", "setInterval", "clearTimeout", "clearInterval", "addEventListener", "removeEventListener", "dispatchEvent", "getComputedStyle", "captureEvents", "releaseEvents", "routeEvent", "enableExternalCapture", "disableExternalCapture", "moveTo", "moveBy", "resizeTo", "resizeBy", "scroll", "scrollTo", "scrollBy", "scrollByLines", "scrollByPages", "sizeToContent", "setResizable", "getSelection", "open", "openDialog", "close", "alert", "confirm", "prompt", "dump", "focus", "blur", "find", "back", "forward", "home", "stop", "print", "atob", "btoa", "updateCommands", "XPCNativeWrapper", "GeckoActiveXObject", "applicationCache" // FF3 ]; domMemberMap.Location = [ "href", "protocol", "host", "hostname", "port", "pathname", "search", "hash", "assign", "reload", "replace" ]; domMemberMap.Node = [ "id", "className", "nodeType", "tagName", "nodeName", "localName", "prefix", "namespaceURI", "nodeValue", "ownerDocument", "parentNode", "offsetParent", "nextSibling", "previousSibling", "firstChild", "lastChild", "childNodes", "attributes", "dir", "baseURI", "textContent", "innerHTML", "addEventListener", "removeEventListener", "dispatchEvent", "cloneNode", "appendChild", "insertBefore", "replaceChild", "removeChild", "compareDocumentPosition", "hasAttributes", "hasChildNodes", "lookupNamespaceURI", "lookupPrefix", "normalize", "isDefaultNamespace", "isEqualNode", "isSameNode", "isSupported", "getFeature", "getUserData", "setUserData" ]; domMemberMap.Document = extendArray(domMemberMap.Node, [ "documentElement", "body", "title", "location", "referrer", "cookie", "contentType", "lastModified", "characterSet", "inputEncoding", "xmlEncoding", "xmlStandalone", "xmlVersion", "strictErrorChecking", "documentURI", "URL", "defaultView", "doctype", "implementation", "styleSheets", "images", "links", "forms", "anchors", "embeds", "plugins", "applets", "width", "height", "designMode", "compatMode", "async", "preferredStylesheetSet", "alinkColor", "linkColor", "vlinkColor", "bgColor", "fgColor", "domain", "addEventListener", "removeEventListener", "dispatchEvent", "captureEvents", "releaseEvents", "routeEvent", "clear", "open", "close", "execCommand", "execCommandShowHelp", "getElementsByName", "getSelection", "queryCommandEnabled", "queryCommandIndeterm", "queryCommandState", "queryCommandSupported", "queryCommandText", "queryCommandValue", "write", "writeln", "adoptNode", "appendChild", "removeChild", "renameNode", "cloneNode", "compareDocumentPosition", "createAttribute", "createAttributeNS", "createCDATASection", "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEntityReference", "createEvent", "createExpression", "createNSResolver", "createNodeIterator", "createProcessingInstruction", "createRange", "createTextNode", "createTreeWalker", "domConfig", "evaluate", "evaluateFIXptr", "evaluateXPointer", "getAnonymousElementByAttribute", "getAnonymousNodes", "addBinding", "removeBinding", "getBindingParent", "getBoxObjectFor", "setBoxObjectFor", "getElementById", "getElementsByTagName", "getElementsByTagNameNS", "hasAttributes", "hasChildNodes", "importNode", "insertBefore", "isDefaultNamespace", "isEqualNode", "isSameNode", "isSupported", "load", "loadBindingDocument", "lookupNamespaceURI", "lookupPrefix", "normalize", "normalizeDocument", "getFeature", "getUserData", "setUserData" ]); domMemberMap.Element = extendArray(domMemberMap.Node, [ "clientWidth", "clientHeight", "offsetLeft", "offsetTop", "offsetWidth", "offsetHeight", "scrollLeft", "scrollTop", "scrollWidth", "scrollHeight", "style", "tabIndex", "title", "lang", "align", "spellcheck", "addEventListener", "removeEventListener", "dispatchEvent", "focus", "blur", "cloneNode", "appendChild", "insertBefore", "replaceChild", "removeChild", "compareDocumentPosition", "getElementsByTagName", "getElementsByTagNameNS", "getAttribute", "getAttributeNS", "getAttributeNode", "getAttributeNodeNS", "setAttribute", "setAttributeNS", "setAttributeNode", "setAttributeNodeNS", "removeAttribute", "removeAttributeNS", "removeAttributeNode", "hasAttribute", "hasAttributeNS", "hasAttributes", "hasChildNodes", "lookupNamespaceURI", "lookupPrefix", "normalize", "isDefaultNamespace", "isEqualNode", "isSameNode", "isSupported", "getFeature", "getUserData", "setUserData" ]); domMemberMap.SVGElement = extendArray(domMemberMap.Element, [ "x", "y", "width", "height", "rx", "ry", "transform", "href", "ownerSVGElement", "viewportElement", "farthestViewportElement", "nearestViewportElement", "getBBox", "getCTM", "getScreenCTM", "getTransformToElement", "getPresentationAttribute", "preserveAspectRatio" ]); domMemberMap.SVGSVGElement = extendArray(domMemberMap.Element, [ "x", "y", "width", "height", "rx", "ry", "transform", "viewBox", "viewport", "currentView", "useCurrentView", "pixelUnitToMillimeterX", "pixelUnitToMillimeterY", "screenPixelToMillimeterX", "screenPixelToMillimeterY", "currentScale", "currentTranslate", "zoomAndPan", "ownerSVGElement", "viewportElement", "farthestViewportElement", "nearestViewportElement", "contentScriptType", "contentStyleType", "getBBox", "getCTM", "getScreenCTM", "getTransformToElement", "getEnclosureList", "getIntersectionList", "getViewboxToViewportTransform", "getPresentationAttribute", "getElementById", "checkEnclosure", "checkIntersection", "createSVGAngle", "createSVGLength", "createSVGMatrix", "createSVGNumber", "createSVGPoint", "createSVGRect", "createSVGString", "createSVGTransform", "createSVGTransformFromMatrix", "deSelectAll", "preserveAspectRatio", "forceRedraw", "suspendRedraw", "unsuspendRedraw", "unsuspendRedrawAll", "getCurrentTime", "setCurrentTime", "animationsPaused", "pauseAnimations", "unpauseAnimations" ]); domMemberMap.HTMLImageElement = extendArray(domMemberMap.Element, [ "src", "naturalWidth", "naturalHeight", "width", "height", "x", "y", "name", "alt", "longDesc", "lowsrc", "border", "complete", "hspace", "vspace", "isMap", "useMap" ]); domMemberMap.HTMLAnchorElement = extendArray(domMemberMap.Element, [ "name", "target", "accessKey", "href", "protocol", "host", "hostname", "port", "pathname", "search", "hash", "hreflang", "coords", "shape", "text", "type", "rel", "rev", "charset" ]); domMemberMap.HTMLIFrameElement = extendArray(domMemberMap.Element, [ "contentDocument", "contentWindow", "frameBorder", "height", "longDesc", "marginHeight", "marginWidth", "name", "scrolling", "src", "width" ]); domMemberMap.HTMLTableElement = extendArray(domMemberMap.Element, [ "bgColor", "border", "caption", "cellPadding", "cellSpacing", "frame", "rows", "rules", "summary", "tBodies", "tFoot", "tHead", "width", "createCaption", "createTFoot", "createTHead", "deleteCaption", "deleteRow", "deleteTFoot", "deleteTHead", "insertRow" ]); domMemberMap.HTMLTableRowElement = extendArray(domMemberMap.Element, [ "bgColor", "cells", "ch", "chOff", "rowIndex", "sectionRowIndex", "vAlign", "deleteCell", "insertCell" ]); domMemberMap.HTMLTableCellElement = extendArray(domMemberMap.Element, [ "abbr", "axis", "bgColor", "cellIndex", "ch", "chOff", "colSpan", "headers", "height", "noWrap", "rowSpan", "scope", "vAlign", "width" ]); domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element, [ "src" ]); domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element, [ "accessKey", "disabled", "form", "name", "type", "value", "click" ]); domMemberMap.HTMLInputElement = extendArray(domMemberMap.Element, [ "type", "value", "checked", "accept", "accessKey", "alt", "controllers", "defaultChecked", "defaultValue", "disabled", "form", "maxLength", "name", "readOnly", "selectionEnd", "selectionStart", "size", "src", "textLength", "useMap", "click", "select", "setSelectionRange" ]); domMemberMap.HTMLFormElement = extendArray(domMemberMap.Element, [ "acceptCharset", "action", "author", "elements", "encoding", "enctype", "entry_id", "length", "method", "name", "post", "target", "text", "url", "reset", "submit" ]); domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element, [ "aLink", "background", "bgColor", "link", "text", "vLink" ]); domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element, [ "version" ]); domMemberMap.Text = extendArray(domMemberMap.Node, [ "data", "length", "appendData", "deleteData", "insertData", "replaceData", "splitText", "substringData" ]); domMemberMap.Attr = extendArray(domMemberMap.Node, [ "name", "value", "specified", "ownerElement" ]); domMemberMap.Event = [ "type", "target", "currentTarget", "originalTarget", "explicitOriginalTarget", "relatedTarget", "rangeParent", "rangeOffset", "view", "keyCode", "charCode", "screenX", "screenY", "clientX", "clientY", "layerX", "layerY", "pageX", "pageY", "detail", "button", "which", "ctrlKey", "shiftKey", "altKey", "metaKey", "eventPhase", "timeStamp", "bubbles", "cancelable", "cancelBubble", "isTrusted", "isChar", "getPreventDefault", "initEvent", "initMouseEvent", "initKeyEvent", "initUIEvent", "preventBubble", "preventCapture", "preventDefault", "stopPropagation" ]; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.domConstantMap = { "ELEMENT_NODE": 1, "ATTRIBUTE_NODE": 1, "TEXT_NODE": 1, "CDATA_SECTION_NODE": 1, "ENTITY_REFERENCE_NODE": 1, "ENTITY_NODE": 1, "PROCESSING_INSTRUCTION_NODE": 1, "COMMENT_NODE": 1, "DOCUMENT_NODE": 1, "DOCUMENT_TYPE_NODE": 1, "DOCUMENT_FRAGMENT_NODE": 1, "NOTATION_NODE": 1, "DOCUMENT_POSITION_DISCONNECTED": 1, "DOCUMENT_POSITION_PRECEDING": 1, "DOCUMENT_POSITION_FOLLOWING": 1, "DOCUMENT_POSITION_CONTAINS": 1, "DOCUMENT_POSITION_CONTAINED_BY": 1, "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": 1, "UNKNOWN_RULE": 1, "STYLE_RULE": 1, "CHARSET_RULE": 1, "IMPORT_RULE": 1, "MEDIA_RULE": 1, "FONT_FACE_RULE": 1, "PAGE_RULE": 1, "CAPTURING_PHASE": 1, "AT_TARGET": 1, "BUBBLING_PHASE": 1, "SCROLL_PAGE_UP": 1, "SCROLL_PAGE_DOWN": 1, "MOUSEUP": 1, "MOUSEDOWN": 1, "MOUSEOVER": 1, "MOUSEOUT": 1, "MOUSEMOVE": 1, "MOUSEDRAG": 1, "CLICK": 1, "DBLCLICK": 1, "KEYDOWN": 1, "KEYUP": 1, "KEYPRESS": 1, "DRAGDROP": 1, "FOCUS": 1, "BLUR": 1, "SELECT": 1, "CHANGE": 1, "RESET": 1, "SUBMIT": 1, "SCROLL": 1, "LOAD": 1, "UNLOAD": 1, "XFER_DONE": 1, "ABORT": 1, "ERROR": 1, "LOCATE": 1, "MOVE": 1, "RESIZE": 1, "FORWARD": 1, "HELP": 1, "BACK": 1, "TEXT": 1, "ALT_MASK": 1, "CONTROL_MASK": 1, "SHIFT_MASK": 1, "META_MASK": 1, "DOM_VK_TAB": 1, "DOM_VK_PAGE_UP": 1, "DOM_VK_PAGE_DOWN": 1, "DOM_VK_UP": 1, "DOM_VK_DOWN": 1, "DOM_VK_LEFT": 1, "DOM_VK_RIGHT": 1, "DOM_VK_CANCEL": 1, "DOM_VK_HELP": 1, "DOM_VK_BACK_SPACE": 1, "DOM_VK_CLEAR": 1, "DOM_VK_RETURN": 1, "DOM_VK_ENTER": 1, "DOM_VK_SHIFT": 1, "DOM_VK_CONTROL": 1, "DOM_VK_ALT": 1, "DOM_VK_PAUSE": 1, "DOM_VK_CAPS_LOCK": 1, "DOM_VK_ESCAPE": 1, "DOM_VK_SPACE": 1, "DOM_VK_END": 1, "DOM_VK_HOME": 1, "DOM_VK_PRINTSCREEN": 1, "DOM_VK_INSERT": 1, "DOM_VK_DELETE": 1, "DOM_VK_0": 1, "DOM_VK_1": 1, "DOM_VK_2": 1, "DOM_VK_3": 1, "DOM_VK_4": 1, "DOM_VK_5": 1, "DOM_VK_6": 1, "DOM_VK_7": 1, "DOM_VK_8": 1, "DOM_VK_9": 1, "DOM_VK_SEMICOLON": 1, "DOM_VK_EQUALS": 1, "DOM_VK_A": 1, "DOM_VK_B": 1, "DOM_VK_C": 1, "DOM_VK_D": 1, "DOM_VK_E": 1, "DOM_VK_F": 1, "DOM_VK_G": 1, "DOM_VK_H": 1, "DOM_VK_I": 1, "DOM_VK_J": 1, "DOM_VK_K": 1, "DOM_VK_L": 1, "DOM_VK_M": 1, "DOM_VK_N": 1, "DOM_VK_O": 1, "DOM_VK_P": 1, "DOM_VK_Q": 1, "DOM_VK_R": 1, "DOM_VK_S": 1, "DOM_VK_T": 1, "DOM_VK_U": 1, "DOM_VK_V": 1, "DOM_VK_W": 1, "DOM_VK_X": 1, "DOM_VK_Y": 1, "DOM_VK_Z": 1, "DOM_VK_CONTEXT_MENU": 1, "DOM_VK_NUMPAD0": 1, "DOM_VK_NUMPAD1": 1, "DOM_VK_NUMPAD2": 1, "DOM_VK_NUMPAD3": 1, "DOM_VK_NUMPAD4": 1, "DOM_VK_NUMPAD5": 1, "DOM_VK_NUMPAD6": 1, "DOM_VK_NUMPAD7": 1, "DOM_VK_NUMPAD8": 1, "DOM_VK_NUMPAD9": 1, "DOM_VK_MULTIPLY": 1, "DOM_VK_ADD": 1, "DOM_VK_SEPARATOR": 1, "DOM_VK_SUBTRACT": 1, "DOM_VK_DECIMAL": 1, "DOM_VK_DIVIDE": 1, "DOM_VK_F1": 1, "DOM_VK_F2": 1, "DOM_VK_F3": 1, "DOM_VK_F4": 1, "DOM_VK_F5": 1, "DOM_VK_F6": 1, "DOM_VK_F7": 1, "DOM_VK_F8": 1, "DOM_VK_F9": 1, "DOM_VK_F10": 1, "DOM_VK_F11": 1, "DOM_VK_F12": 1, "DOM_VK_F13": 1, "DOM_VK_F14": 1, "DOM_VK_F15": 1, "DOM_VK_F16": 1, "DOM_VK_F17": 1, "DOM_VK_F18": 1, "DOM_VK_F19": 1, "DOM_VK_F20": 1, "DOM_VK_F21": 1, "DOM_VK_F22": 1, "DOM_VK_F23": 1, "DOM_VK_F24": 1, "DOM_VK_NUM_LOCK": 1, "DOM_VK_SCROLL_LOCK": 1, "DOM_VK_COMMA": 1, "DOM_VK_PERIOD": 1, "DOM_VK_SLASH": 1, "DOM_VK_BACK_QUOTE": 1, "DOM_VK_OPEN_BRACKET": 1, "DOM_VK_BACK_SLASH": 1, "DOM_VK_CLOSE_BRACKET": 1, "DOM_VK_QUOTE": 1, "DOM_VK_META": 1, "SVG_ZOOMANDPAN_DISABLE": 1, "SVG_ZOOMANDPAN_MAGNIFY": 1, "SVG_ZOOMANDPAN_UNKNOWN": 1 }; this.cssInfo = { "background": ["bgRepeat", "bgAttachment", "bgPosition", "color", "systemColor", "none"], "background-attachment": ["bgAttachment"], "background-color": ["color", "systemColor"], "background-image": ["none"], "background-position": ["bgPosition"], "background-repeat": ["bgRepeat"], "border": ["borderStyle", "thickness", "color", "systemColor", "none"], "border-top": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-right": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-bottom": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-left": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-collapse": ["borderCollapse"], "border-color": ["color", "systemColor"], "border-top-color": ["color", "systemColor"], "border-right-color": ["color", "systemColor"], "border-bottom-color": ["color", "systemColor"], "border-left-color": ["color", "systemColor"], "border-spacing": [], "border-style": ["borderStyle"], "border-top-style": ["borderStyle"], "border-right-style": ["borderStyle"], "border-bottom-style": ["borderStyle"], "border-left-style": ["borderStyle"], "border-width": ["thickness"], "border-top-width": ["thickness"], "border-right-width": ["thickness"], "border-bottom-width": ["thickness"], "border-left-width": ["thickness"], "bottom": ["auto"], "caption-side": ["captionSide"], "clear": ["clear", "none"], "clip": ["auto"], "color": ["color", "systemColor"], "content": ["content"], "counter-increment": ["none"], "counter-reset": ["none"], "cursor": ["cursor", "none"], "direction": ["direction"], "display": ["display", "none"], "empty-cells": [], "float": ["float", "none"], "font": ["fontStyle", "fontVariant", "fontWeight", "fontFamily"], "font-family": ["fontFamily"], "font-size": ["fontSize"], "font-size-adjust": [], "font-stretch": [], "font-style": ["fontStyle"], "font-variant": ["fontVariant"], "font-weight": ["fontWeight"], "height": ["auto"], "left": ["auto"], "letter-spacing": [], "line-height": [], "list-style": ["listStyleType", "listStylePosition", "none"], "list-style-image": ["none"], "list-style-position": ["listStylePosition"], "list-style-type": ["listStyleType", "none"], "margin": [], "margin-top": [], "margin-right": [], "margin-bottom": [], "margin-left": [], "marker-offset": ["auto"], "min-height": ["none"], "max-height": ["none"], "min-width": ["none"], "max-width": ["none"], "outline": ["borderStyle", "color", "systemColor", "none"], "outline-color": ["color", "systemColor"], "outline-style": ["borderStyle"], "outline-width": [], "overflow": ["overflow", "auto"], "overflow-x": ["overflow", "auto"], "overflow-y": ["overflow", "auto"], "padding": [], "padding-top": [], "padding-right": [], "padding-bottom": [], "padding-left": [], "position": ["position"], "quotes": ["none"], "right": ["auto"], "table-layout": ["tableLayout", "auto"], "text-align": ["textAlign"], "text-decoration": ["textDecoration", "none"], "text-indent": [], "text-shadow": [], "text-transform": ["textTransform", "none"], "top": ["auto"], "unicode-bidi": [], "vertical-align": ["verticalAlign"], "white-space": ["whiteSpace"], "width": ["auto"], "word-spacing": [], "z-index": [], "-moz-appearance": ["mozAppearance"], "-moz-border-radius": [], "-moz-border-radius-bottomleft": [], "-moz-border-radius-bottomright": [], "-moz-border-radius-topleft": [], "-moz-border-radius-topright": [], "-moz-border-top-colors": ["color", "systemColor"], "-moz-border-right-colors": ["color", "systemColor"], "-moz-border-bottom-colors": ["color", "systemColor"], "-moz-border-left-colors": ["color", "systemColor"], "-moz-box-align": ["mozBoxAlign"], "-moz-box-direction": ["mozBoxDirection"], "-moz-box-flex": [], "-moz-box-ordinal-group": [], "-moz-box-orient": ["mozBoxOrient"], "-moz-box-pack": ["mozBoxPack"], "-moz-box-sizing": ["mozBoxSizing"], "-moz-opacity": [], "-moz-user-focus": ["userFocus", "none"], "-moz-user-input": ["userInput"], "-moz-user-modify": [], "-moz-user-select": ["userSelect", "none"], "-moz-background-clip": [], "-moz-background-inline-policy": [], "-moz-background-origin": [], "-moz-binding": [], "-moz-column-count": [], "-moz-column-gap": [], "-moz-column-width": [], "-moz-image-region": [] }; this.inheritedStyleNames = { "border-collapse": 1, "border-spacing": 1, "border-style": 1, "caption-side": 1, "color": 1, "cursor": 1, "direction": 1, "empty-cells": 1, "font": 1, "font-family": 1, "font-size-adjust": 1, "font-size": 1, "font-style": 1, "font-variant": 1, "font-weight": 1, "letter-spacing": 1, "line-height": 1, "list-style": 1, "list-style-image": 1, "list-style-position": 1, "list-style-type": 1, "quotes": 1, "text-align": 1, "text-decoration": 1, "text-indent": 1, "text-shadow": 1, "text-transform": 1, "white-space": 1, "word-spacing": 1 }; this.cssKeywords = { "appearance": [ "button", "button-small", "checkbox", "checkbox-container", "checkbox-small", "dialog", "listbox", "menuitem", "menulist", "menulist-button", "menulist-textfield", "menupopup", "progressbar", "radio", "radio-container", "radio-small", "resizer", "scrollbar", "scrollbarbutton-down", "scrollbarbutton-left", "scrollbarbutton-right", "scrollbarbutton-up", "scrollbartrack-horizontal", "scrollbartrack-vertical", "separator", "statusbar", "tab", "tab-left-edge", "tabpanels", "textfield", "toolbar", "toolbarbutton", "toolbox", "tooltip", "treeheadercell", "treeheadersortarrow", "treeitem", "treetwisty", "treetwistyopen", "treeview", "window" ], "systemColor": [ "ActiveBorder", "ActiveCaption", "AppWorkspace", "Background", "ButtonFace", "ButtonHighlight", "ButtonShadow", "ButtonText", "CaptionText", "GrayText", "Highlight", "HighlightText", "InactiveBorder", "InactiveCaption", "InactiveCaptionText", "InfoBackground", "InfoText", "Menu", "MenuText", "Scrollbar", "ThreeDDarkShadow", "ThreeDFace", "ThreeDHighlight", "ThreeDLightShadow", "ThreeDShadow", "Window", "WindowFrame", "WindowText", "-moz-field", "-moz-fieldtext", "-moz-workspace", "-moz-visitedhyperlinktext", "-moz-use-text-color" ], "color": [ "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkTurquoise", "DarkViolet", "DeepPink", "DarkSkyBlue", "DimGray", "DodgerBlue", "Feldspar", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", "Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGrey", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateBlue", "LightSlateGray", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "VioletRed", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen", "transparent", "invert" ], "auto": [ "auto" ], "none": [ "none" ], "captionSide": [ "top", "bottom", "left", "right" ], "clear": [ "left", "right", "both" ], "cursor": [ "auto", "cell", "context-menu", "crosshair", "default", "help", "pointer", "progress", "move", "e-resize", "all-scroll", "ne-resize", "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize", "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "col-resize", "row-resize", "text", "vertical-text", "wait", "alias", "copy", "move", "no-drop", "not-allowed", "-moz-alias", "-moz-cell", "-moz-copy", "-moz-grab", "-moz-grabbing", "-moz-contextmenu", "-moz-zoom-in", "-moz-zoom-out", "-moz-spinning" ], "direction": [ "ltr", "rtl" ], "bgAttachment": [ "scroll", "fixed" ], "bgPosition": [ "top", "center", "bottom", "left", "right" ], "bgRepeat": [ "repeat", "repeat-x", "repeat-y", "no-repeat" ], "borderStyle": [ "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "-moz-bg-inset", "-moz-bg-outset", "-moz-bg-solid" ], "borderCollapse": [ "collapse", "separate" ], "overflow": [ "visible", "hidden", "scroll", "-moz-scrollbars-horizontal", "-moz-scrollbars-none", "-moz-scrollbars-vertical" ], "listStyleType": [ "disc", "circle", "square", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian", "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha", "katakana-iroha", "inherit" ], "listStylePosition": [ "inside", "outside" ], "content": [ "open-quote", "close-quote", "no-open-quote", "no-close-quote", "inherit" ], "fontStyle": [ "normal", "italic", "oblique", "inherit" ], "fontVariant": [ "normal", "small-caps", "inherit" ], "fontWeight": [ "normal", "bold", "bolder", "lighter", "inherit" ], "fontSize": [ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "smaller", "larger" ], "fontFamily": [ "Arial", "Comic Sans MS", "Georgia", "Tahoma", "Verdana", "Times New Roman", "Trebuchet MS", "Lucida Grande", "Helvetica", "serif", "sans-serif", "cursive", "fantasy", "monospace", "caption", "icon", "menu", "message-box", "small-caption", "status-bar", "inherit" ], "display": [ "block", "inline", "inline-block", "list-item", "marker", "run-in", "compact", "table", "inline-table", "table-row-group", "table-column", "table-column-group", "table-header-group", "table-footer-group", "table-row", "table-cell", "table-caption", "-moz-box", "-moz-compact", "-moz-deck", "-moz-grid", "-moz-grid-group", "-moz-grid-line", "-moz-groupbox", "-moz-inline-block", "-moz-inline-box", "-moz-inline-grid", "-moz-inline-stack", "-moz-inline-table", "-moz-marker", "-moz-popup", "-moz-runin", "-moz-stack" ], "position": [ "static", "relative", "absolute", "fixed", "inherit" ], "float": [ "left", "right" ], "textAlign": [ "left", "right", "center", "justify" ], "tableLayout": [ "fixed" ], "textDecoration": [ "underline", "overline", "line-through", "blink" ], "textTransform": [ "capitalize", "lowercase", "uppercase", "inherit" ], "unicodeBidi": [ "normal", "embed", "bidi-override" ], "whiteSpace": [ "normal", "pre", "nowrap" ], "verticalAlign": [ "baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "inherit" ], "thickness": [ "thin", "medium", "thick" ], "userFocus": [ "ignore", "normal" ], "userInput": [ "disabled", "enabled" ], "userSelect": [ "normal" ], "mozBoxSizing": [ "content-box", "padding-box", "border-box" ], "mozBoxAlign": [ "start", "center", "end", "baseline", "stretch" ], "mozBoxDirection": [ "normal", "reverse" ], "mozBoxOrient": [ "horizontal", "vertical" ], "mozBoxPack": [ "start", "center", "end" ] }; this.nonEditableTags = { "HTML": 1, "HEAD": 1, "html": 1, "head": 1 }; this.innerEditableTags = { "BODY": 1, "body": 1 }; this.selfClosingTags = { // End tags for void elements are forbidden http://wiki.whatwg.org/wiki/HTML_vs._XHTML "meta": 1, "link": 1, "area": 1, "base": 1, "col": 1, "input": 1, "img": 1, "br": 1, "hr": 1, "param":1, "embed":1 }; var invisibleTags = this.invisibleTags = { "HTML": 1, "HEAD": 1, "TITLE": 1, "META": 1, "LINK": 1, "STYLE": 1, "SCRIPT": 1, "NOSCRIPT": 1, "BR": 1, "PARAM": 1, "COL": 1, "html": 1, "head": 1, "title": 1, "meta": 1, "link": 1, "style": 1, "script": 1, "noscript": 1, "br": 1, "param": 1, "col": 1 /* "window": 1, "browser": 1, "frame": 1, "tabbrowser": 1, "WINDOW": 1, "BROWSER": 1, "FRAME": 1, "TABBROWSER": 1, */ }; if (typeof KeyEvent == "undefined") { this.KeyEvent = { DOM_VK_CANCEL: 3, DOM_VK_HELP: 6, DOM_VK_BACK_SPACE: 8, DOM_VK_TAB: 9, DOM_VK_CLEAR: 12, DOM_VK_RETURN: 13, DOM_VK_ENTER: 14, DOM_VK_SHIFT: 16, DOM_VK_CONTROL: 17, DOM_VK_ALT: 18, DOM_VK_PAUSE: 19, DOM_VK_CAPS_LOCK: 20, DOM_VK_ESCAPE: 27, DOM_VK_SPACE: 32, DOM_VK_PAGE_UP: 33, DOM_VK_PAGE_DOWN: 34, DOM_VK_END: 35, DOM_VK_HOME: 36, DOM_VK_LEFT: 37, DOM_VK_UP: 38, DOM_VK_RIGHT: 39, DOM_VK_DOWN: 40, DOM_VK_PRINTSCREEN: 44, DOM_VK_INSERT: 45, DOM_VK_DELETE: 46, DOM_VK_0: 48, DOM_VK_1: 49, DOM_VK_2: 50, DOM_VK_3: 51, DOM_VK_4: 52, DOM_VK_5: 53, DOM_VK_6: 54, DOM_VK_7: 55, DOM_VK_8: 56, DOM_VK_9: 57, DOM_VK_SEMICOLON: 59, DOM_VK_EQUALS: 61, DOM_VK_A: 65, DOM_VK_B: 66, DOM_VK_C: 67, DOM_VK_D: 68, DOM_VK_E: 69, DOM_VK_F: 70, DOM_VK_G: 71, DOM_VK_H: 72, DOM_VK_I: 73, DOM_VK_J: 74, DOM_VK_K: 75, DOM_VK_L: 76, DOM_VK_M: 77, DOM_VK_N: 78, DOM_VK_O: 79, DOM_VK_P: 80, DOM_VK_Q: 81, DOM_VK_R: 82, DOM_VK_S: 83, DOM_VK_T: 84, DOM_VK_U: 85, DOM_VK_V: 86, DOM_VK_W: 87, DOM_VK_X: 88, DOM_VK_Y: 89, DOM_VK_Z: 90, DOM_VK_CONTEXT_MENU: 93, DOM_VK_NUMPAD0: 96, DOM_VK_NUMPAD1: 97, DOM_VK_NUMPAD2: 98, DOM_VK_NUMPAD3: 99, DOM_VK_NUMPAD4: 100, DOM_VK_NUMPAD5: 101, DOM_VK_NUMPAD6: 102, DOM_VK_NUMPAD7: 103, DOM_VK_NUMPAD8: 104, DOM_VK_NUMPAD9: 105, DOM_VK_MULTIPLY: 106, DOM_VK_ADD: 107, DOM_VK_SEPARATOR: 108, DOM_VK_SUBTRACT: 109, DOM_VK_DECIMAL: 110, DOM_VK_DIVIDE: 111, DOM_VK_F1: 112, DOM_VK_F2: 113, DOM_VK_F3: 114, DOM_VK_F4: 115, DOM_VK_F5: 116, DOM_VK_F6: 117, DOM_VK_F7: 118, DOM_VK_F8: 119, DOM_VK_F9: 120, DOM_VK_F10: 121, DOM_VK_F11: 122, DOM_VK_F12: 123, DOM_VK_F13: 124, DOM_VK_F14: 125, DOM_VK_F15: 126, DOM_VK_F16: 127, DOM_VK_F17: 128, DOM_VK_F18: 129, DOM_VK_F19: 130, DOM_VK_F20: 131, DOM_VK_F21: 132, DOM_VK_F22: 133, DOM_VK_F23: 134, DOM_VK_F24: 135, DOM_VK_NUM_LOCK: 144, DOM_VK_SCROLL_LOCK: 145, DOM_VK_COMMA: 188, DOM_VK_PERIOD: 190, DOM_VK_SLASH: 191, DOM_VK_BACK_QUOTE: 192, DOM_VK_OPEN_BRACKET: 219, DOM_VK_BACK_SLASH: 220, DOM_VK_CLOSE_BRACKET: 221, DOM_VK_QUOTE: 222, DOM_VK_META: 224 }; } // ************************************************************************************************ // Ajax /** * @namespace */ this.Ajax = { requests: [], transport: null, states: ["Uninitialized","Loading","Loaded","Interactive","Complete"], initialize: function() { this.transport = FBL.getNativeXHRObject(); }, getXHRObject: function() { var xhrObj = false; try { xhrObj = new XMLHttpRequest(); } catch(e) { var progid = [ "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP" ]; for ( var i=0; i < progid.length; ++i ) { try { xhrObj = new ActiveXObject(progid[i]); } catch(e) { continue; } break; } } finally { return xhrObj; } }, /** * Create a AJAX request. * * @name request * @param {Object} options request options * @param {String} options.url URL to be requested * @param {String} options.type Request type ("get" ou "post"). Default is "get". * @param {Boolean} options.async Asynchronous flag. Default is "true". * @param {String} options.dataType Data type ("text", "html", "xml" or "json"). Default is "text". * @param {String} options.contentType Content-type of the data being sent. Default is "application/x-www-form-urlencoded". * @param {Function} options.onLoading onLoading callback * @param {Function} options.onLoaded onLoaded callback * @param {Function} options.onInteractive onInteractive callback * @param {Function} options.onComplete onComplete callback * @param {Function} options.onUpdate onUpdate callback * @param {Function} options.onSuccess onSuccess callback * @param {Function} options.onFailure onFailure callback */ request: function(options) { // process options var o = FBL.extend( { // default values type: "get", async: true, dataType: "text", contentType: "application/x-www-form-urlencoded" }, options || {} ); this.requests.push(o); var s = this.getState(); if (s == "Uninitialized" || s == "Complete" || s == "Loaded") this.sendRequest(); }, serialize: function(data) { var r = [""], rl = 0; if (data) { if (typeof data == "string") r[rl++] = data; else if (data.innerHTML && data.elements) { for (var i=0,el,l=(el=data.elements).length; i < l; i++) if (el[i].name) { r[rl++] = encodeURIComponent(el[i].name); r[rl++] = "="; r[rl++] = encodeURIComponent(el[i].value); r[rl++] = "&"; } } else for(var param in data) { r[rl++] = encodeURIComponent(param); r[rl++] = "="; r[rl++] = encodeURIComponent(data[param]); r[rl++] = "&"; } } return r.join("").replace(/&$/, ""); }, sendRequest: function() { var t = FBL.Ajax.transport, r = FBL.Ajax.requests.shift(), data; // open XHR object t.open(r.type, r.url, r.async); //setRequestHeaders(); // indicates that it is a XHR request to the server t.setRequestHeader("X-Requested-With", "XMLHttpRequest"); // if data is being sent, sets the appropriate content-type if (data = FBL.Ajax.serialize(r.data)) t.setRequestHeader("Content-Type", r.contentType); /** @ignore */ // onreadystatechange handler t.onreadystatechange = function() { FBL.Ajax.onStateChange(r); }; // send the request t.send(data); }, /** * Handles the state change */ onStateChange: function(options) { var fn, o = options, t = this.transport; var state = this.getState(t); if (fn = o["on" + state]) fn(this.getResponse(o), o); if (state == "Complete") { var success = t.status == 200, response = this.getResponse(o); if (fn = o["onUpdate"]) fn(response, o); if (fn = o["on" + (success ? "Success" : "Failure")]) fn(response, o); t.onreadystatechange = FBL.emptyFn; if (this.requests.length > 0) setTimeout(this.sendRequest, 10); } }, /** * gets the appropriate response value according the type */ getResponse: function(options) { var t = this.transport, type = options.dataType; if (t.status != 200) return t.statusText; else if (type == "text") return t.responseText; else if (type == "html") return t.responseText; else if (type == "xml") return t.responseXML; else if (type == "json") return eval("(" + t.responseText + ")"); }, /** * returns the current state of the XHR object */ getState: function() { return this.states[this.transport.readyState]; } }; // ************************************************************************************************ // Cookie, from http://www.quirksmode.org/js/cookies.html this.createCookie = function(name,value,days) { if ('cookie' in document) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else var expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } }; this.readCookie = function (name) { if ('cookie' in document) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } } return null; }; this.removeCookie = function(name) { this.createCookie(name, "", -1); }; // ************************************************************************************************ // http://www.mister-pixel.com/#Content__state=is_that_simple var fixIE6BackgroundImageCache = function(doc) { doc = doc || document; try { doc.execCommand("BackgroundImageCache", false, true); } catch(E) { } }; // ************************************************************************************************ // calculatePixelsPerInch var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;"; var calculatePixelsPerInch = function calculatePixelsPerInch(doc, body) { var inch = FBL.createGlobalElement("div"); inch.style.cssText = resetStyle + "width:1in; height:1in; position:absolute; top:-1234px; left:-1234px;"; body.appendChild(inch); FBL.pixelsPerInch = { x: inch.offsetWidth, y: inch.offsetHeight }; body.removeChild(inch); }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.SourceLink = function(url, line, type, object, instance) { this.href = url; this.instance = instance; this.line = line; this.type = type; this.object = object; }; this.SourceLink.prototype = { toString: function() { return this.href; }, toJSON: function() // until 3.1... { return "{\"href\":\""+this.href+"\", "+ (this.line?("\"line\":"+this.line+","):"")+ (this.type?(" \"type\":\""+this.type+"\","):"")+ "}"; } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.SourceText = function(lines, owner) { this.lines = lines; this.owner = owner; }; this.SourceText.getLineAsHTML = function(lineNo) { return escapeForSourceLine(this.lines[lineNo-1]); }; // ************************************************************************************************ }).apply(FBL); /* See license.txt for terms of usage */ FBL.ns( /** @scope s_i18n */ function() { with (FBL) { // ************************************************************************************************ // TODO: xxxpedro localization var oSTR = { "NoMembersWarning": "There are no properties to show for this object.", "EmptyStyleSheet": "There are no rules in this stylesheet.", "EmptyElementCSS": "This element has no style rules.", "AccessRestricted": "Access to restricted URI denied.", "net.label.Parameters": "Parameters", "net.label.Source": "Source", "URLParameters": "Params", "EditStyle": "Edit Element Style...", "NewRule": "New Rule...", "NewProp": "New Property...", "EditProp": 'Edit "%s"', "DeleteProp": 'Delete "%s"', "DisableProp": 'Disable "%s"' }; // ************************************************************************************************ FBL.$STR = function(name) { return oSTR.hasOwnProperty(name) ? oSTR[name] : name; }; FBL.$STRF = function(name, args) { if (!oSTR.hasOwnProperty(name)) return name; var format = oSTR[name]; var objIndex = 0; var parts = parseFormat(format); var trialIndex = objIndex; var objects = args; for (var i= 0; i < parts.length; i++) { var part = parts[i]; if (part && typeof(part) == "object") { if (++trialIndex > objects.length) // then too few parameters for format, assume unformatted. { format = ""; objIndex = -1; parts.length = 0; break; } } } var result = []; for (var i = 0; i < parts.length; ++i) { var part = parts[i]; if (part && typeof(part) == "object") { result.push(""+args.shift()); } else result.push(part); } return result.join(""); }; // ************************************************************************************************ var parseFormat = function parseFormat(format) { var parts = []; if (format.length <= 0) return parts; var reg = /((^%|.%)(\d+)?(\.)([a-zA-Z]))|((^%|.%)([a-zA-Z]))/; for (var m = reg.exec(format); m; m = reg.exec(format)) { if (m[0].substr(0, 2) == "%%") { parts.push(format.substr(0, m.index)); parts.push(m[0].substr(1)); } else { var type = m[8] ? m[8] : m[5]; var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); var rep = null; switch (type) { case "s": rep = FirebugReps.Text; break; case "f": case "i": case "d": rep = FirebugReps.Number; break; case "o": rep = null; break; } parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); parts.push({rep: rep, precision: precision, type: ("%" + type)}); } format = format.substr(m.index+m[0].length); } parts.push(format); return parts; }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns( /** @scope s_firebug */ function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Globals // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Internals var modules = []; var panelTypes = []; var panelTypeMap = {}; var reps = []; var parentPanelMap = {}; // ************************************************************************************************ // Firebug /** * @namespace describe Firebug * @exports FBL.Firebug as Firebug */ FBL.Firebug = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * version: "Firebug Lite 1.4.0", revision: "$Revision: 11967 $", // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * modules: modules, panelTypes: panelTypes, panelTypeMap: panelTypeMap, reps: reps, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Initialization initialize: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.initialize", "initializing application"); Firebug.browser = new Context(Env.browser); Firebug.context = Firebug.browser; Firebug.loadPrefs(); Firebug.context.persistedState.isOpen = false; // Document must be cached before chrome initialization cacheDocument(); if (Firebug.Inspector && Firebug.Inspector.create) Firebug.Inspector.create(); if (FBL.CssAnalyzer && FBL.CssAnalyzer.processAllStyleSheets) FBL.CssAnalyzer.processAllStyleSheets(Firebug.browser.document); FirebugChrome.initialize(); dispatch(modules, "initialize", []); if (Firebug.disableResourceFetching) Firebug.Console.logFormatted(["Some Firebug Lite features are not working because " + "resource fetching is disabled. To enabled it set the Firebug Lite option " + "\"disableResourceFetching\" to \"false\". More info at " + "http://getfirebug.com/firebuglite#Options"], Firebug.context, "warn"); if (Env.onLoad) { var onLoad = Env.onLoad; delete Env.onLoad; setTimeout(onLoad, 200); } }, shutdown: function() { if (Firebug.saveCookies) Firebug.savePrefs(); if (Firebug.Inspector) Firebug.Inspector.destroy(); dispatch(modules, "shutdown", []); var chromeMap = FirebugChrome.chromeMap; for (var name in chromeMap) { if (chromeMap.hasOwnProperty(name)) { try { chromeMap[name].destroy(); } catch(E) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("chrome.destroy() failed to: " + name); } } } Firebug.Lite.Cache.Element.clear(); Firebug.Lite.Cache.StyleSheet.clear(); Firebug.browser = null; Firebug.context = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Registration registerModule: function() { modules.push.apply(modules, arguments); if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.registerModule"); }, registerPanel: function() { panelTypes.push.apply(panelTypes, arguments); for (var i = 0, panelType; panelType = arguments[i]; ++i) { panelTypeMap[panelType.prototype.name] = arguments[i]; if (panelType.prototype.parentPanel) parentPanelMap[panelType.prototype.parentPanel] = 1; } if (FBTrace.DBG_INITIALIZE) for (var i = 0; i < arguments.length; ++i) FBTrace.sysout("Firebug.registerPanel", arguments[i].prototype.name); }, registerRep: function() { reps.push.apply(reps, arguments); }, unregisterRep: function() { for (var i = 0; i < arguments.length; ++i) remove(reps, arguments[i]); }, setDefaultReps: function(funcRep, rep) { FBL.defaultRep = rep; FBL.defaultFuncRep = funcRep; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Reps getRep: function(object) { var type = typeof object; if (isIE && isFunction(object)) type = "function"; for (var i = 0; i < reps.length; ++i) { var rep = reps[i]; try { if (rep.supportsObject(object, type)) { if (FBTrace.DBG_DOM) FBTrace.sysout("getRep type: "+type+" object: "+object, rep); return rep; } } catch (exc) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("firebug.getRep FAILS: ", exc.message || exc); FBTrace.sysout("firebug.getRep reps["+i+"/"+reps.length+"]: Rep="+reps[i].className); // TODO: xxxpedro add trace to FBTrace logs like in Firebug //firebug.trace(); } } } return (type == 'function') ? defaultFuncRep : defaultRep; }, getRepObject: function(node) { var target = null; for (var child = node; child; child = child.parentNode) { if (hasClass(child, "repTarget")) target = child; if (child.repObject) { if (!target && hasClass(child, "repIgnore")) break; else return child.repObject; } } }, getRepNode: function(node) { for (var child = node; child; child = child.parentNode) { if (child.repObject) return child; } }, getElementByRepObject: function(element, object) { for (var child = element.firstChild; child; child = child.nextSibling) { if (child.repObject == object) return child; } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Preferences getPref: function(name) { return Firebug[name]; }, setPref: function(name, value) { Firebug[name] = value; Firebug.savePrefs(); }, setPrefs: function(prefs) { for (var name in prefs) { if (prefs.hasOwnProperty(name)) Firebug[name] = prefs[name]; } Firebug.savePrefs(); }, restorePrefs: function() { var Options = Env.DefaultOptions; for (var name in Options) { Firebug[name] = Options[name]; } }, loadPrefs: function() { this.restorePrefs(); var prefs = Store.get("FirebugLite") || {}; var options = prefs.options; var persistedState = prefs.persistedState || FBL.defaultPersistedState; for (var name in options) { if (options.hasOwnProperty(name)) Firebug[name] = options[name]; } if (Firebug.context && persistedState) Firebug.context.persistedState = persistedState; }, savePrefs: function() { var prefs = { options: {} }; var EnvOptions = Env.Options; var options = prefs.options; for (var name in EnvOptions) { if (EnvOptions.hasOwnProperty(name)) { options[name] = Firebug[name]; } } var persistedState = Firebug.context.persistedState; if (!persistedState) { persistedState = Firebug.context.persistedState = FBL.defaultPersistedState; } prefs.persistedState = persistedState; Store.set("FirebugLite", prefs); }, erasePrefs: function() { Store.remove("FirebugLite"); this.restorePrefs(); } }; Firebug.restorePrefs(); // xxxpedro should we remove this? window.Firebug = FBL.Firebug; if (!Env.Options.enablePersistent || Env.Options.enablePersistent && Env.isChromeContext || Env.isDebugMode) Env.browser.window.Firebug = FBL.Firebug; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Other methods FBL.cacheDocument = function cacheDocument() { var ElementCache = Firebug.Lite.Cache.Element; var els = Firebug.browser.document.getElementsByTagName("*"); for (var i=0, l=els.length, el; iFirebug.registerModule method. There is always one instance of a module object * per browser window. * @extends Firebug.Listener */ Firebug.Module = extend(new Firebug.Listener(), /** @extend Firebug.Module */ { /** * Called when the window is opened. */ initialize: function() { }, /** * Called when the window is closed. */ shutdown: function() { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Called when a new context is created but before the page is loaded. */ initContext: function(context) { }, /** * Called after a context is detached to a separate window; */ reattachContext: function(browser, context) { }, /** * Called when a context is destroyed. Module may store info on persistedState for reloaded pages. */ destroyContext: function(context, persistedState) { }, // Called when a FF tab is create or activated (user changes FF tab) // Called after context is created or with context == null (to abort?) showContext: function(browser, context) { }, /** * Called after a context's page gets DOMContentLoaded */ loadedContext: function(context) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * showPanel: function(browser, panel) { }, showSidePanel: function(browser, panel) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * updateOption: function(name, value) { }, getObjectByURL: function(context, url) { } }); // ************************************************************************************************ // Panel /** * @panel Base class for all panels. Every derived panel must define a constructor and * register with "Firebug.registerPanel" method. An instance of the panel * object is created by the framework for each browser tab where Firebug is activated. */ Firebug.Panel = { name: "HelloWorld", title: "Hello World!", parentPanel: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * options: { hasCommandLine: false, hasStatusBar: false, hasToolButtons: false, // Pre-rendered panels are those included in the skin file (firebug.html) isPreRendered: false, innerHTMLSync: false /* // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // To be used by external extensions panelHTML: "", panelCSS: "", toolButtonsHTML: "" /**/ }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * tabNode: null, panelNode: null, sidePanelNode: null, statusBarNode: null, toolButtonsNode: null, panelBarNode: null, sidePanelBarBoxNode: null, sidePanelBarNode: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * sidePanelBar: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * searchable: false, editable: true, order: 2147483647, statusSeparator: "<", create: function(context, doc) { this.hasSidePanel = parentPanelMap.hasOwnProperty(this.name); this.panelBarNode = $("fbPanelBar1"); this.sidePanelBarBoxNode = $("fbPanelBar2"); if (this.hasSidePanel) { this.sidePanelBar = extend({}, PanelBar); this.sidePanelBar.create(this); } var options = this.options = extend(Firebug.Panel.options, this.options); var panelId = "fb" + this.name; if (options.isPreRendered) { this.panelNode = $(panelId); this.tabNode = $(panelId + "Tab"); this.tabNode.style.display = "block"; if (options.hasToolButtons) { this.toolButtonsNode = $(panelId + "Buttons"); } if (options.hasStatusBar) { this.statusBarBox = $("fbStatusBarBox"); this.statusBarNode = $(panelId + "StatusBar"); } } else { var containerSufix = this.parentPanel ? "2" : "1"; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Create Panel var panelNode = this.panelNode = createElement("div", { id: panelId, className: "fbPanel" }); $("fbPanel" + containerSufix).appendChild(panelNode); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Create Panel Tab var tabHTML = '' + this.title + ''; var tabNode = this.tabNode = createElement("a", { id: panelId + "Tab", className: "fbTab fbHover", innerHTML: tabHTML }); if (isIE6) { tabNode.href = "javascript:void(0)"; } var panelBarNode = this.parentPanel ? Firebug.chrome.getPanel(this.parentPanel).sidePanelBarNode : this.panelBarNode; panelBarNode.appendChild(tabNode); tabNode.style.display = "block"; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create ToolButtons if (options.hasToolButtons) { this.toolButtonsNode = createElement("span", { id: panelId + "Buttons", className: "fbToolbarButtons" }); $("fbToolbarButtons").appendChild(this.toolButtonsNode); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create StatusBar if (options.hasStatusBar) { this.statusBarBox = $("fbStatusBarBox"); this.statusBarNode = createElement("span", { id: panelId + "StatusBar", className: "fbToolbarButtons fbStatusBar" }); this.statusBarBox.appendChild(this.statusBarNode); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create SidePanel } this.containerNode = this.panelNode.parentNode; if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.create", this.name); // xxxpedro contextMenu this.onContextMenu = bind(this.onContextMenu, this); /* this.context = context; this.document = doc; this.panelNode = doc.createElement("div"); this.panelNode.ownerPanel = this; setClass(this.panelNode, "panelNode panelNode-"+this.name+" contextUID="+context.uid); doc.body.appendChild(this.panelNode); if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("firebug.initialize panelNode for "+this.name+"\n"); this.initializeNode(this.panelNode); /**/ }, destroy: function(state) // Panel may store info on state { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.destroy", this.name); if (this.hasSidePanel) { this.sidePanelBar.destroy(); this.sidePanelBar = null; } this.options = null; this.name = null; this.parentPanel = null; this.tabNode = null; this.panelNode = null; this.containerNode = null; this.toolButtonsNode = null; this.statusBarBox = null; this.statusBarNode = null; //if (this.panelNode) // delete this.panelNode.ownerPanel; //this.destroyNode(); }, initialize: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.initialize", this.name); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (this.hasSidePanel) { this.sidePanelBar.initialize(); } var options = this.options = extend(Firebug.Panel.options, this.options); var panelId = "fb" + this.name; this.panelNode = $(panelId); this.tabNode = $(panelId + "Tab"); this.tabNode.style.display = "block"; if (options.hasStatusBar) { this.statusBarBox = $("fbStatusBarBox"); this.statusBarNode = $(panelId + "StatusBar"); } if (options.hasToolButtons) { this.toolButtonsNode = $(panelId + "Buttons"); } this.containerNode = this.panelNode.parentNode; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // restore persistent state this.containerNode.scrollTop = this.lastScrollTop; // xxxpedro contextMenu addEvent(this.containerNode, "contextmenu", this.onContextMenu); /// TODO: xxxpedro infoTip Hack Firebug.chrome.currentPanel = Firebug.chrome.selectedPanel && Firebug.chrome.selectedPanel.sidePanelBar ? Firebug.chrome.selectedPanel.sidePanelBar.selectedPanel : Firebug.chrome.selectedPanel; Firebug.showInfoTips = true; if (Firebug.InfoTip) Firebug.InfoTip.initializeBrowser(Firebug.chrome); }, shutdown: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.shutdown", this.name); /// TODO: xxxpedro infoTip Hack if (Firebug.InfoTip) Firebug.InfoTip.uninitializeBrowser(Firebug.chrome); if (Firebug.chrome.largeCommandLineVisible) Firebug.chrome.hideLargeCommandLine(); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (this.hasSidePanel) { // TODO: xxxpedro firebug1.3a6 // new PanelBar mechanism will need to call shutdown to hide the panels (so it // doesn't appears in other panel's sidePanelBar. Therefore, we need to implement // a "remember selected panel" feature in the sidePanelBar //this.sidePanelBar.shutdown(); } // store persistent state this.lastScrollTop = this.containerNode.scrollTop; // xxxpedro contextMenu removeEvent(this.containerNode, "contextmenu", this.onContextMenu); }, detach: function(oldChrome, newChrome) { if (oldChrome && oldChrome.selectedPanel && oldChrome.selectedPanel.name == this.name) this.lastScrollTop = oldChrome.selectedPanel.containerNode.scrollTop; }, reattach: function(doc) { if (this.options.innerHTMLSync) this.synchronizeUI(); }, synchronizeUI: function() { this.containerNode.scrollTop = this.lastScrollTop || 0; }, show: function(state) { var options = this.options; if (options.hasStatusBar) { this.statusBarBox.style.display = "inline"; this.statusBarNode.style.display = "inline"; } if (options.hasToolButtons) { this.toolButtonsNode.style.display = "inline"; } this.panelNode.style.display = "block"; this.visible = true; if (!this.parentPanel) Firebug.chrome.layout(this); }, hide: function(state) { var options = this.options; if (options.hasStatusBar) { this.statusBarBox.style.display = "none"; this.statusBarNode.style.display = "none"; } if (options.hasToolButtons) { this.toolButtonsNode.style.display = "none"; } this.panelNode.style.display = "none"; this.visible = false; }, watchWindow: function(win) { }, unwatchWindow: function(win) { }, updateOption: function(name, value) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Toolbar helpers */ showToolbarButtons: function(buttonsId, show) { try { if (!this.context.browser) // XXXjjb this is bug. Somehow the panel context is not FirebugContext. { if (FBTrace.DBG_ERRORS) FBTrace.sysout("firebug.Panel showToolbarButtons this.context has no browser, this:", this); return; } var buttons = this.context.browser.chrome.$(buttonsId); if (buttons) collapse(buttons, show ? "false" : "true"); } catch (exc) { if (FBTrace.DBG_ERRORS) { FBTrace.dumpProperties("firebug.Panel showToolbarButtons FAILS", exc); if (!this.context.browser)FBTrace.dumpStack("firebug.Panel showToolbarButtons no browser"); } } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Returns a number indicating the view's ability to inspect the object. * * Zero means not supported, and higher numbers indicate specificity. */ supportsObject: function(object) { return 0; }, hasObject: function(object) // beyond type testing, is this object selectable? { return false; }, select: function(object, forceUpdate) { if (!object) object = this.getDefaultSelection(this.context); if(FBTrace.DBG_PANELS) FBTrace.sysout("firebug.select "+this.name+" forceUpdate: "+forceUpdate+" "+object+((object==this.selection)?"==":"!=")+this.selection); if (forceUpdate || object != this.selection) { this.selection = object; this.updateSelection(object); // TODO: xxxpedro // XXXjoe This is kind of cheating, but, feh. //Firebug.chrome.onPanelSelect(object, this); //if (uiListeners.length > 0) // dispatch(uiListeners, "onPanelSelect", [object, this]); // TODO: make Firebug.chrome a uiListener } }, updateSelection: function(object) { }, markChange: function(skipSelf) { if (this.dependents) { if (skipSelf) { for (var i = 0; i < this.dependents.length; ++i) { var panelName = this.dependents[i]; if (panelName != this.name) this.context.invalidatePanels(panelName); } } else this.context.invalidatePanels.apply(this.context, this.dependents); } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * startInspecting: function() { }, stopInspecting: function(object, cancelled) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * search: function(text, reverse) { }, /** * Retrieves the search options that this modules supports. * This is used by the search UI to present the proper options. */ getSearchOptionsMenuItems: function() { return [ Firebug.Search.searchOptionMenu("search.Case Sensitive", "searchCaseSensitive") ]; }, /** * Navigates to the next document whose match parameter returns true. */ navigateToNextDocument: function(match, reverse) { // This is an approximation of the UI that is displayed by the location // selector. This should be close enough, although it may be better // to simply generate the sorted list within the module, rather than // sorting within the UI. var self = this; function compare(a, b) { var locA = self.getObjectDescription(a); var locB = self.getObjectDescription(b); if(locA.path > locB.path) return 1; if(locA.path < locB.path) return -1; if(locA.name > locB.name) return 1; if(locA.name < locB.name) return -1; return 0; } var allLocs = this.getLocationList().sort(compare); for (var curPos = 0; curPos < allLocs.length && allLocs[curPos] != this.location; curPos++); function transformIndex(index) { if (reverse) { // For the reverse case we need to implement wrap around. var intermediate = curPos - index - 1; return (intermediate < 0 ? allLocs.length : 0) + intermediate; } else { return (curPos + index + 1) % allLocs.length; } }; for (var next = 0; next < allLocs.length - 1; next++) { var object = allLocs[transformIndex(next)]; if (match(object)) { this.navigate(object); return object; } } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Called when "Options" clicked. Return array of // {label: 'name', nol10n: true, type: "checkbox", checked: , command:function to set } getOptionsMenuItems: function() { return null; }, /* * Called by chrome.onContextMenu to build the context menu when this panel has focus. * See also FirebugRep for a similar function also called by onContextMenu * Extensions may monkey patch and chain off this call * @param object: the 'realObject', a model value, eg a DOM property * @param target: the HTML element clicked on. * @return an array of menu items. */ getContextMenuItems: function(object, target) { return []; }, getBreakOnMenuItems: function() { return []; }, getEditor: function(target, value) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * getDefaultSelection: function() { return null; }, browseObject: function(object) { }, getPopupObject: function(target) { return Firebug.getRepObject(target); }, getTooltipObject: function(target) { return Firebug.getRepObject(target); }, showInfoTip: function(infoTip, x, y) { }, getObjectPath: function(object) { return null; }, // An array of objects that can be passed to getObjectLocation. // The list of things a panel can show, eg sourceFiles. // Only shown if panel.location defined and supportsObject true getLocationList: function() { return null; }, getDefaultLocation: function() { return null; }, getObjectLocation: function(object) { return ""; }, // Text for the location list menu eg script panel source file list // return.path: group/category label, return.name: item label getObjectDescription: function(object) { var url = this.getObjectLocation(object); return FBL.splitURLBase(url); }, /* * UI signal that a tab needs attention, eg Script panel is currently stopped on a breakpoint * @param: show boolean, true turns on. */ highlight: function(show) { var tab = this.getTab(); if (!tab) return; if (show) tab.setAttribute("highlight", "true"); else tab.removeAttribute("highlight"); }, getTab: function() { var chrome = Firebug.chrome; var tab = chrome.$("fbPanelBar2").getTab(this.name); if (!tab) tab = chrome.$("fbPanelBar1").getTab(this.name); return tab; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Support for Break On Next /** * Called by the framework when the user clicks on the Break On Next button. * @param {Boolean} armed Set to true if the Break On Next feature is * to be armed for action and set to false if the Break On Next should be disarmed. * If 'armed' is true, then the next call to shouldBreakOnNext should be |true|. */ breakOnNext: function(armed) { }, /** * Called when a panel is selected/displayed. The method should return true * if the Break On Next feature is currently armed for this panel. */ shouldBreakOnNext: function() { return false; }, /** * Returns labels for Break On Next tooltip (one for enabled and one for disabled state). * @param {Boolean} enabled Set to true if the Break On Next feature is * currently activated for this panel. */ getBreakOnNextTooltip: function(enabled) { return null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // xxxpedro contextMenu onContextMenu: function(event) { if (!this.getContextMenuItems) return; cancelEvent(event, true); var target = event.target || event.srcElement; var menu = this.getContextMenuItems(this.selection, target); if (!menu) return; var contextMenu = new Menu( { id: "fbPanelContextMenu", items: menu }); contextMenu.show(event.clientX, event.clientY); return true; /* // TODO: xxxpedro move code to somewhere. code to get cross-browser // window to screen coordinates var box = Firebug.browser.getElementPosition(Firebug.chrome.node); var screenY = 0; // Firefox if (typeof window.mozInnerScreenY != "undefined") { screenY = window.mozInnerScreenY; } // Chrome else if (typeof window.innerHeight != "undefined") { screenY = window.outerHeight - window.innerHeight; } // IE else if (typeof window.screenTop != "undefined") { screenY = window.screenTop; } contextMenu.show(event.screenX-box.left, event.screenY-screenY-box.top); /**/ } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * MeasureBox * To get pixels size.width and size.height: *
  • this.startMeasuring(view);
  • *
  • var size = this.measureText(lineNoCharsSpacer);
  • *
  • this.stopMeasuring();
  • *
* * @namespace */ Firebug.MeasureBox = { startMeasuring: function(target) { if (!this.measureBox) { this.measureBox = target.ownerDocument.createElement("span"); this.measureBox.className = "measureBox"; } copyTextStyles(target, this.measureBox); target.ownerDocument.body.appendChild(this.measureBox); }, getMeasuringElement: function() { return this.measureBox; }, measureText: function(value) { this.measureBox.innerHTML = value ? escapeForSourceLine(value) : "m"; return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1}; }, measureInputText: function(value) { value = value ? escapeForTextNode(value) : "m"; if (!Firebug.showTextNodesWithWhitespace) value = value.replace(/\t/g,'mmmmmm').replace(/\ /g,'m'); this.measureBox.innerHTML = value; return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1}; }, getBox: function(target) { var style = this.measureBox.ownerDocument.defaultView.getComputedStyle(this.measureBox, ""); var box = getBoxFromStyles(style, this.measureBox); return box; }, stopMeasuring: function() { this.measureBox.parentNode.removeChild(this.measureBox); } }; // ************************************************************************************************ if (FBL.domplate) Firebug.Rep = domplate( { className: "", inspectable: true, supportsObject: function(object, type) { return false; }, inspectObject: function(object, context) { Firebug.chrome.select(object); }, browseObject: function(object, context) { }, persistObject: function(object, context) { }, getRealObject: function(object, context) { return object; }, getTitle: function(object) { var label = safeToString(object); var re = /\[object (.*?)\]/; var m = re.exec(label); ///return m ? m[1] : label; // if the label is in the "[object TYPE]" format return its type if (m) { return m[1]; } // if it is IE we need to handle some special cases else if ( // safeToString() fails to recognize some objects in IE isIE && // safeToString() returns "[object]" for some objects like window.Image (label == "[object]" || // safeToString() returns undefined for some objects like window.clientInformation typeof object == "object" && typeof label == "undefined") ) { return "Object"; } else { return label; } }, getTooltip: function(object) { return null; }, getContextMenuItems: function(object, target, context) { return []; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Convenience for domplates STR: function(name) { return $STR(name); }, cropString: function(text) { return cropString(text); }, cropMultipleLines: function(text, limit) { return cropMultipleLines(text, limit); }, toLowerCase: function(text) { return text ? text.toLowerCase() : text; }, plural: function(n) { return n == 1 ? "" : "s"; } }); // ************************************************************************************************ // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns( /** @scope s_gui */ function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Controller /**@namespace*/ FBL.Controller = { controllers: null, controllerContext: null, initialize: function(context) { this.controllers = []; this.controllerContext = context || Firebug.chrome; }, shutdown: function() { this.removeControllers(); //this.controllers = null; //this.controllerContext = null; }, addController: function() { for (var i=0, arg; arg=arguments[i]; i++) { // If the first argument is a string, make a selector query // within the controller node context if (typeof arg[0] == "string") { arg[0] = $$(arg[0], this.controllerContext); } // bind the handler to the proper context var handler = arg[2]; arg[2] = bind(handler, this); // save the original handler as an extra-argument, so we can // look for it later, when removing a particular controller arg[3] = handler; this.controllers.push(arg); addEvent.apply(this, arg); } }, removeController: function() { for (var i=0, arg; arg=arguments[i]; i++) { for (var j=0, c; c=this.controllers[j]; j++) { if (arg[0] == c[0] && arg[1] == c[1] && arg[2] == c[3]) removeEvent.apply(this, c); } } }, removeControllers: function() { for (var i=0, c; c=this.controllers[i]; i++) { removeEvent.apply(this, c); } } }; // ************************************************************************************************ // PanelBar /**@namespace*/ FBL.PanelBar = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * panelMap: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * selectedPanel: null, parentPanelName: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * create: function(ownerPanel) { this.panelMap = {}; this.ownerPanel = ownerPanel; if (ownerPanel) { ownerPanel.sidePanelBarNode = createElement("span"); ownerPanel.sidePanelBarNode.style.display = "none"; ownerPanel.sidePanelBarBoxNode.appendChild(ownerPanel.sidePanelBarNode); } var panels = Firebug.panelTypes; for (var i=0, p; p=panels[i]; i++) { if ( // normal Panel of the Chrome's PanelBar !ownerPanel && !p.prototype.parentPanel || // Child Panel of the current Panel's SidePanelBar ownerPanel && p.prototype.parentPanel && ownerPanel.name == p.prototype.parentPanel) { this.addPanel(p.prototype.name); } } }, destroy: function() { PanelBar.shutdown.call(this); for (var name in this.panelMap) { this.removePanel(name); var panel = this.panelMap[name]; panel.destroy(); this.panelMap[name] = null; delete this.panelMap[name]; } this.panelMap = null; this.ownerPanel = null; }, initialize: function() { if (this.ownerPanel) this.ownerPanel.sidePanelBarNode.style.display = "inline"; for(var name in this.panelMap) { (function(self, name){ // tab click handler var onTabClick = function onTabClick() { self.selectPanel(name); return false; }; Firebug.chrome.addController([self.panelMap[name].tabNode, "mousedown", onTabClick]); })(this, name); } }, shutdown: function() { var selectedPanel = this.selectedPanel; if (selectedPanel) { removeClass(selectedPanel.tabNode, "fbSelectedTab"); selectedPanel.hide(); selectedPanel.shutdown(); } if (this.ownerPanel) this.ownerPanel.sidePanelBarNode.style.display = "none"; this.selectedPanel = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * addPanel: function(panelName, parentPanel) { var PanelType = Firebug.panelTypeMap[panelName]; var panel = this.panelMap[panelName] = new PanelType(); panel.create(); }, removePanel: function(panelName) { var panel = this.panelMap[panelName]; if (panel.hasOwnProperty(panelName)) panel.destroy(); }, selectPanel: function(panelName) { var selectedPanel = this.selectedPanel; var panel = this.panelMap[panelName]; if (panel && selectedPanel != panel) { if (selectedPanel) { removeClass(selectedPanel.tabNode, "fbSelectedTab"); selectedPanel.shutdown(); selectedPanel.hide(); } if (!panel.parentPanel) Firebug.context.persistedState.selectedPanelName = panelName; this.selectedPanel = panel; setClass(panel.tabNode, "fbSelectedTab"); panel.show(); panel.initialize(); } }, getPanel: function(panelName) { var panel = this.panelMap[panelName]; return panel; } }; //************************************************************************************************ // Button /** * options.element * options.caption * options.title * * options.owner * options.className * options.pressedClassName * * options.onPress * options.onUnpress * options.onClick * * @class * @extends FBL.Controller * */ FBL.Button = function(options) { options = options || {}; append(this, options); this.state = "unpressed"; this.display = "unpressed"; if (this.element) { this.container = this.element.parentNode; } else { this.shouldDestroy = true; this.container = this.owner.getPanel().toolButtonsNode; this.element = createElement("a", { className: this.baseClassName + " " + this.className + " fbHover", innerHTML: this.caption }); if (this.title) this.element.title = this.title; this.container.appendChild(this.element); } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Button.prototype = extend(Controller, /**@extend FBL.Button.prototype*/ { type: "normal", caption: "caption", title: null, className: "", // custom class baseClassName: "fbButton", // control class pressedClassName: "fbBtnPressed", // control pressed class element: null, container: null, owner: null, state: null, display: null, destroy: function() { this.shutdown(); // only remove if it is a dynamically generated button (not pre-rendered) if (this.shouldDestroy) this.container.removeChild(this.element); this.element = null; this.container = null; this.owner = null; }, initialize: function() { Controller.initialize.apply(this); var element = this.element; this.addController([element, "mousedown", this.handlePress]); if (this.type == "normal") this.addController( [element, "mouseup", this.handleUnpress], [element, "mouseout", this.handleUnpress], [element, "click", this.handleClick] ); }, shutdown: function() { Controller.shutdown.apply(this); }, restore: function() { this.changeState("unpressed"); }, changeState: function(state) { this.state = state; this.changeDisplay(state); }, changeDisplay: function(display) { if (display != this.display) { if (display == "pressed") { setClass(this.element, this.pressedClassName); } else if (display == "unpressed") { removeClass(this.element, this.pressedClassName); } this.display = display; } }, handlePress: function(event) { cancelEvent(event, true); if (this.type == "normal") { this.changeDisplay("pressed"); this.beforeClick = true; } else if (this.type == "toggle") { if (this.state == "pressed") { this.changeState("unpressed"); if (this.onUnpress) this.onUnpress.apply(this.owner, arguments); } else { this.changeState("pressed"); if (this.onPress) this.onPress.apply(this.owner, arguments); } if (this.onClick) this.onClick.apply(this.owner, arguments); } return false; }, handleUnpress: function(event) { cancelEvent(event, true); if (this.beforeClick) this.changeDisplay("unpressed"); return false; }, handleClick: function(event) { cancelEvent(event, true); if (this.type == "normal") { if (this.onClick) this.onClick.apply(this.owner); this.changeState("unpressed"); } this.beforeClick = false; return false; } }); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * @class * @extends FBL.Button */ FBL.IconButton = function() { Button.apply(this, arguments); }; IconButton.prototype = extend(Button.prototype, /**@extend FBL.IconButton.prototype*/ { baseClassName: "fbIconButton", pressedClassName: "fbIconPressed" }); //************************************************************************************************ // Menu var menuItemProps = {"class": "$item.className", type: "$item.type", value: "$item.value", _command: "$item.command"}; if (isIE6) menuItemProps.href = "javascript:void(0)"; // Allow GUI to be loaded even when Domplate module is not installed. if (FBL.domplate) var MenuPlate = domplate(Firebug.Rep, { tag: DIV({"class": "fbMenu fbShadow"}, DIV({"class": "fbMenuContent fbShadowContent"}, FOR("item", "$object.items|memberIterator", TAG("$item.tag", {item: "$item"}) ) ) ), itemTag: A(menuItemProps, "$item.label" ), checkBoxTag: A(extend(menuItemProps, {checked : "$item.checked"}), "$item.label" ), radioButtonTag: A(extend(menuItemProps, {selected : "$item.selected"}), "$item.label" ), groupTag: A(extend(menuItemProps, {child: "$item.child"}), "$item.label" ), shortcutTag: A(menuItemProps, "$item.label", SPAN({"class": "fbMenuShortcutKey"}, "$item.key" ) ), separatorTag: SPAN({"class": "fbMenuSeparator"}), memberIterator: function(items) { var result = []; for (var i=0, length=items.length; i width || el.scrollHeight > height)) { width = el.scrollWidth; height = el.scrollHeight; } return {width: width, height: height}; }, getWindowScrollPosition: function() { var top=0, left=0, el; if(typeof this.window.pageYOffset == "number") { top = this.window.pageYOffset; left = this.window.pageXOffset; } else if((el=this.document.body) && (el.scrollTop || el.scrollLeft)) { top = el.scrollTop; left = el.scrollLeft; } else if((el=this.document.documentElement) && (el.scrollTop || el.scrollLeft)) { top = el.scrollTop; left = el.scrollLeft; } return {top:top, left:left}; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Element Methods getElementFromPoint: function(x, y) { if (shouldFixElementFromPoint) { var scroll = this.getWindowScrollPosition(); return this.document.elementFromPoint(x + scroll.left, y + scroll.top); } else return this.document.elementFromPoint(x, y); }, getElementPosition: function(el) { var left = 0; var top = 0; do { left += el.offsetLeft; top += el.offsetTop; } while (el = el.offsetParent); return {left:left, top:top}; }, getElementBox: function(el) { var result = {}; if (el.getBoundingClientRect) { var rect = el.getBoundingClientRect(); // fix IE problem with offset when not in fullscreen mode var offset = isIE ? this.document.body.clientTop || this.document.documentElement.clientTop: 0; var scroll = this.getWindowScrollPosition(); result.top = Math.round(rect.top - offset + scroll.top); result.left = Math.round(rect.left - offset + scroll.left); result.height = Math.round(rect.bottom - rect.top); result.width = Math.round(rect.right - rect.left); } else { var position = this.getElementPosition(el); result.top = position.top; result.left = position.left; result.height = el.offsetHeight; result.width = el.offsetWidth; } return result; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Measurement Methods getMeasurement: function(el, name) { var result = {value: 0, unit: "px"}; var cssValue = this.getStyle(el, name); if (!cssValue) return result; if (cssValue.toLowerCase() == "auto") return result; var reMeasure = /(\d+\.?\d*)(.*)/; var m = cssValue.match(reMeasure); if (m) { result.value = m[1]-0; result.unit = m[2].toLowerCase(); } return result; }, getMeasurementInPixels: function(el, name) { if (!el) return null; var m = this.getMeasurement(el, name); var value = m.value; var unit = m.unit; if (unit == "px") return value; else if (unit == "pt") return this.pointsToPixels(name, value); else if (unit == "em") return this.emToPixels(el, value); else if (unit == "%") return this.percentToPixels(el, value); else if (unit == "ex") return this.exToPixels(el, value); // TODO: add other units. Maybe create a better general way // to calculate measurements in different units. }, getMeasurementBox1: function(el, name) { var sufixes = ["Top", "Left", "Bottom", "Right"]; var result = []; for(var i=0, sufix; sufix=sufixes[i]; i++) result[i] = Math.round(this.getMeasurementInPixels(el, name + sufix)); return {top:result[0], left:result[1], bottom:result[2], right:result[3]}; }, getMeasurementBox: function(el, name) { var result = []; var sufixes = name == "border" ? ["TopWidth", "LeftWidth", "BottomWidth", "RightWidth"] : ["Top", "Left", "Bottom", "Right"]; if (isIE) { var propName, cssValue; var autoMargin = null; for(var i=0, sufix; sufix=sufixes[i]; i++) { propName = name + sufix; cssValue = el.currentStyle[propName] || el.style[propName]; if (cssValue == "auto") { if (!autoMargin) autoMargin = this.getCSSAutoMarginBox(el); result[i] = autoMargin[sufix.toLowerCase()]; } else result[i] = this.getMeasurementInPixels(el, propName); } } else { for(var i=0, sufix; sufix=sufixes[i]; i++) result[i] = this.getMeasurementInPixels(el, name + sufix); } return {top:result[0], left:result[1], bottom:result[2], right:result[3]}; }, getCSSAutoMarginBox: function(el) { if (isIE && " meta title input script link a ".indexOf(" "+el.nodeName.toLowerCase()+" ") != -1) return {top:0, left:0, bottom:0, right:0}; /**/ if (isIE && " h1 h2 h3 h4 h5 h6 h7 ul p ".indexOf(" "+el.nodeName.toLowerCase()+" ") == -1) return {top:0, left:0, bottom:0, right:0}; /**/ var offsetTop = 0; if (false && isIEStantandMode) { var scrollSize = Firebug.browser.getWindowScrollSize(); offsetTop = scrollSize.height; } var box = this.document.createElement("div"); //box.style.cssText = "margin:0; padding:1px; border: 0; position:static; overflow:hidden; visibility: hidden;"; box.style.cssText = "margin:0; padding:1px; border: 0; visibility: hidden;"; var clone = el.cloneNode(false); var text = this.document.createTextNode(" "); clone.appendChild(text); box.appendChild(clone); this.document.body.appendChild(box); var marginTop = clone.offsetTop - box.offsetTop - 1; var marginBottom = box.offsetHeight - clone.offsetHeight - 2 - marginTop; var marginLeft = clone.offsetLeft - box.offsetLeft - 1; var marginRight = box.offsetWidth - clone.offsetWidth - 2 - marginLeft; this.document.body.removeChild(box); return {top:marginTop+offsetTop, left:marginLeft, bottom:marginBottom-offsetTop, right:marginRight}; }, getFontSizeInPixels: function(el) { var size = this.getMeasurement(el, "fontSize"); if (size.unit == "px") return size.value; // get font size, the dirty way var computeDirtyFontSize = function(el, calibration) { var div = this.document.createElement("div"); var divStyle = offscreenStyle; if (calibration) divStyle += " font-size:"+calibration+"px;"; div.style.cssText = divStyle; div.innerHTML = "A"; el.appendChild(div); var value = div.offsetHeight; el.removeChild(div); return value; }; /* var calibrationBase = 200; var calibrationValue = computeDirtyFontSize(el, calibrationBase); var rate = calibrationBase / calibrationValue; /**/ // the "dirty technique" fails in some environments, so we're using a static value // based in some tests. var rate = 200 / 225; var value = computeDirtyFontSize(el); return value * rate; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Unit Funtions pointsToPixels: function(name, value, returnFloat) { var axis = /Top$|Bottom$/.test(name) ? "y" : "x"; var result = value * pixelsPerInch[axis] / 72; return returnFloat ? result : Math.round(result); }, emToPixels: function(el, value) { if (!el) return null; var fontSize = this.getFontSizeInPixels(el); return Math.round(value * fontSize); }, exToPixels: function(el, value) { if (!el) return null; // get ex value, the dirty way var div = this.document.createElement("div"); div.style.cssText = offscreenStyle + "width:"+value + "ex;"; el.appendChild(div); var value = div.offsetWidth; el.removeChild(div); return value; }, percentToPixels: function(el, value) { if (!el) return null; // get % value, the dirty way var div = this.document.createElement("div"); div.style.cssText = offscreenStyle + "width:"+value + "%;"; el.appendChild(div); var value = div.offsetWidth; el.removeChild(div); return value; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * getStyle: isIE ? function(el, name) { return el.currentStyle[name] || el.style[name] || undefined; } : function(el, name) { return this.document.defaultView.getComputedStyle(el,null)[name] || el.style[name] || undefined; } }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns( /**@scope ns-chrome*/ function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Globals // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Window Options var WindowDefaultOptions = { type: "frame", id: "FirebugUI" //height: 350 // obsolete }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Instantiated objects commandLine, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Interface Elements Cache fbTop, fbContent, fbContentStyle, fbBottom, fbBtnInspect, fbToolbar, fbPanelBox1, fbPanelBox1Style, fbPanelBox2, fbPanelBox2Style, fbPanelBar2Box, fbPanelBar2BoxStyle, fbHSplitter, fbVSplitter, fbVSplitterStyle, fbPanel1, fbPanel1Style, fbPanel2, fbPanel2Style, fbConsole, fbConsoleStyle, fbHTML, fbCommandLine, fbLargeCommandLine, fbLargeCommandButtons, //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Cached size values topHeight, topPartialHeight, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * chromeRedrawSkipRate = isIE ? 75 : isOpera ? 80 : 75, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * lastSelectedPanelName, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * focusCommandLineState = 0, lastFocusedPanelName, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * lastHSplitterMouseMove = 0, onHSplitterMouseMoveBuffer = null, onHSplitterMouseMoveTimer = null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * lastVSplitterMouseMove = 0; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ************************************************************************************************ // FirebugChrome FBL.defaultPersistedState = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * isOpen: false, height: 300, sidePanelWidth: 350, selectedPanelName: "Console", selectedHTMLElementId: null, htmlSelectionStack: [] // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }; /**@namespace*/ FBL.FirebugChrome = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //isOpen: false, //height: 300, //sidePanelWidth: 350, //selectedPanelName: "Console", //selectedHTMLElementId: null, chromeMap: {}, htmlSelectionStack: [], // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * create: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.create", "creating chrome window"); createChromeWindow(); }, initialize: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.initialize", "initializing chrome window"); if (Env.chrome.type == "frame" || Env.chrome.type == "div") ChromeMini.create(Env.chrome); var chrome = Firebug.chrome = new Chrome(Env.chrome); FirebugChrome.chromeMap[chrome.type] = chrome; addGlobalEvent("keydown", onGlobalKeyDown); if (Env.Options.enablePersistent && chrome.type == "popup") { // TODO: xxxpedro persist - revise chrome synchronization when in persistent mode var frame = FirebugChrome.chromeMap.frame; if (frame) frame.close(); //chrome.reattach(frame, chrome); //TODO: xxxpedro persist synchronize? chrome.initialize(); } }, clone: function(FBChrome) { for (var name in FBChrome) { var prop = FBChrome[name]; if (FBChrome.hasOwnProperty(name) && !isFunction(prop)) { this[name] = prop; } } } }; // ************************************************************************************************ // Chrome Window Creation var createChromeWindow = function(options) { options = extend(WindowDefaultOptions, options || {}); //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Locals var browserWin = Env.browser.window; var browserContext = new Context(browserWin); var prefs = Store.get("FirebugLite"); var persistedState = prefs && prefs.persistedState || defaultPersistedState; var chrome = {}, context = options.context || Env.browser, type = chrome.type = Env.Options.enablePersistent ? "popup" : options.type, isChromeFrame = type == "frame", useLocalSkin = Env.useLocalSkin, url = useLocalSkin ? Env.Location.skin : "about:blank", // document.body not available in XML+XSL documents in Firefox body = context.document.getElementsByTagName("body")[0], formatNode = function(node) { if (!Env.isDebugMode) { node.firebugIgnore = true; } var browserWinSize = browserContext.getWindowSize(); var height = persistedState.height || 300; height = Math.min(browserWinSize.height, height); height = Math.max(200, height); node.style.border = "0"; node.style.visibility = "hidden"; node.style.zIndex = "2147483647"; // MAX z-index = 2147483647 node.style.position = noFixedPosition ? "absolute" : "fixed"; node.style.width = "100%"; // "102%"; IE auto margin bug node.style.left = "0"; node.style.bottom = noFixedPosition ? "-1px" : "0"; node.style.height = height + "px"; // avoid flickering during chrome rendering //if (isFirefox) // node.style.display = "none"; }, createChromeDiv = function() { //Firebug.Console.warn("Firebug Lite GUI is working in 'windowless mode'. It may behave slower and receive interferences from the page in which it is installed."); var node = chrome.node = createGlobalElement("div"), style = createGlobalElement("style"), css = FirebugChrome.Skin.CSS /* .replace(/;/g, " !important;") .replace(/!important\s!important/g, "!important") .replace(/display\s*:\s*(\w+)\s*!important;/g, "display:$1;")*/, // reset some styles to minimize interference from the main page's style rules = ".fbBody *{margin:0;padding:0;font-size:11px;line-height:13px;color:inherit;}" + // load the chrome styles css + // adjust some remaining styles ".fbBody #fbHSplitter{position:absolute !important;} .fbBody #fbHTML span{line-height:14px;} .fbBody .lineNo div{line-height:inherit !important;}"; /* if (isIE) { // IE7 CSS bug (FbChrome table bigger than its parent div) rules += ".fbBody table.fbChrome{position: static !important;}"; }/**/ style.type = "text/css"; if (style.styleSheet) style.styleSheet.cssText = rules; else style.appendChild(context.document.createTextNode(rules)); document.getElementsByTagName("head")[0].appendChild(style); node.className = "fbBody"; node.style.overflow = "hidden"; node.innerHTML = getChromeDivTemplate(); if (isIE) { // IE7 CSS bug (FbChrome table bigger than its parent div) setTimeout(function(){ node.firstChild.style.height = "1px"; node.firstChild.style.position = "static"; },0); /**/ } formatNode(node); body.appendChild(node); chrome.window = window; chrome.document = document; onChromeLoad(chrome); }; //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * try { //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create the Chrome as a "div" (windowless mode) if (type == "div") { createChromeDiv(); return; } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // cretate the Chrome as an "iframe" else if (isChromeFrame) { // Create the Chrome Frame var node = chrome.node = createGlobalElement("iframe"); node.setAttribute("src", url); node.setAttribute("frameBorder", "0"); formatNode(node); body.appendChild(node); // must set the id after appending to the document, otherwise will cause an // strange error in IE, making the iframe load the page in which the bookmarklet // was created (like getfirebug.com), before loading the injected UI HTML, // generating an "Access Denied" error. node.id = options.id; } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create the Chrome as a "popup" else { var height = persistedState.popupHeight || 300; var browserWinSize = browserContext.getWindowSize(); var browserWinLeft = typeof browserWin.screenX == "number" ? browserWin.screenX : browserWin.screenLeft; var popupLeft = typeof persistedState.popupLeft == "number" ? persistedState.popupLeft : browserWinLeft; var browserWinTop = typeof browserWin.screenY == "number" ? browserWin.screenY : browserWin.screenTop; var popupTop = typeof persistedState.popupTop == "number" ? persistedState.popupTop : Math.max( 0, Math.min( browserWinTop + browserWinSize.height - height, // Google Chrome bug screen.availHeight - height - 61 ) ); var popupWidth = typeof persistedState.popupWidth == "number" ? persistedState.popupWidth : Math.max( 0, Math.min( browserWinSize.width, // Opera opens popup in a new tab if it's too big! screen.availWidth-10 ) ); var popupHeight = typeof persistedState.popupHeight == "number" ? persistedState.popupHeight : 300; var options = [ "true,top=", popupTop, ",left=", popupLeft, ",height=", popupHeight, ",width=", popupWidth, ",resizable" ].join(""), node = chrome.node = context.window.open( url, "popup", options ); if (node) { try { node.focus(); } catch(E) { alert("Firebug Error: Firebug popup was blocked."); return; } } else { alert("Firebug Error: Firebug popup was blocked."); return; } } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Inject the interface HTML if it is not using the local skin if (!useLocalSkin) { var tpl = getChromeTemplate(!isChromeFrame), doc = isChromeFrame ? node.contentWindow.document : node.document; doc.write(tpl); doc.close(); } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Wait the Window to be loaded var win, waitDelay = useLocalSkin ? isChromeFrame ? 200 : 300 : 100, waitForWindow = function() { if ( // Frame loaded... OR isChromeFrame && (win=node.contentWindow) && node.contentWindow.document.getElementById("fbCommandLine") || // Popup loaded !isChromeFrame && (win=node.window) && node.document && node.document.getElementById("fbCommandLine") ) { chrome.window = win.window; chrome.document = win.document; // Prevent getting the wrong chrome height in FF when opening a popup setTimeout(function(){ onChromeLoad(chrome); }, useLocalSkin ? 200 : 0); } else setTimeout(waitForWindow, waitDelay); }; waitForWindow(); } catch(e) { var msg = e.message || e; if (/access/i.test(msg)) { // Firebug Lite could not create a window for its Graphical User Interface due to // a access restriction. This happens in some pages, when loading via bookmarklet. // In such cases, the only way is to load the GUI in a "windowless mode". if (isChromeFrame) body.removeChild(node); else if(type == "popup") node.close(); // Load the GUI in a "windowless mode" createChromeDiv(); } else { alert("Firebug Error: Firebug GUI could not be created."); } } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var onChromeLoad = function onChromeLoad(chrome) { Env.chrome = chrome; if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Chrome onChromeLoad", "chrome window loaded"); if (Env.Options.enablePersistent) { // TODO: xxxpedro persist - make better chrome synchronization when in persistent mode Env.FirebugChrome = FirebugChrome; chrome.window.Firebug = chrome.window.Firebug || {}; chrome.window.Firebug.SharedEnv = Env; if (Env.isDevelopmentMode) { Env.browser.window.FBDev.loadChromeApplication(chrome); } else { var doc = chrome.document; var script = doc.createElement("script"); script.src = Env.Location.app + "#remote,persist"; doc.getElementsByTagName("head")[0].appendChild(script); } } else { if (chrome.type == "frame" || chrome.type == "div") { // initialize the chrome application setTimeout(function(){ FBL.Firebug.initialize(); },0); } else if (chrome.type == "popup") { var oldChrome = FirebugChrome.chromeMap.frame; var newChrome = new Chrome(chrome); // TODO: xxxpedro sync detach reattach attach dispatch(newChrome.panelMap, "detach", [oldChrome, newChrome]); newChrome.reattach(oldChrome, newChrome); } } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var getChromeDivTemplate = function() { return FirebugChrome.Skin.HTML; }; var getChromeTemplate = function(isPopup) { var tpl = FirebugChrome.Skin; var r = [], i = -1; r[++i] = ''; r[++i] = ''; r[++i] = Firebug.version; /* r[++i] = ''; /**/ r[++i] = ''; /**/ r[++i] = ''; r[++i] = tpl.HTML; r[++i] = ''; return r.join(""); }; // ************************************************************************************************ // Chrome Class /**@class*/ var Chrome = function Chrome(chrome) { var type = chrome.type; var Base = type == "frame" || type == "div" ? ChromeFrameBase : ChromePopupBase; append(this, Base); // inherit from base class (ChromeFrameBase or ChromePopupBase) append(this, chrome); // inherit chrome window properties append(this, new Context(chrome.window)); // inherit from Context class FirebugChrome.chromeMap[type] = this; Firebug.chrome = this; Env.chrome = chrome.window; this.commandLineVisible = false; this.sidePanelVisible = false; this.create(); return this; }; // ************************************************************************************************ // ChromeBase /** * @namespace * @extends FBL.Controller * @extends FBL.PanelBar **/ var ChromeBase = {}; append(ChromeBase, Controller); append(ChromeBase, PanelBar); append(ChromeBase, /**@extend ns-chrome-ChromeBase*/ { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // inherited properties // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // inherited from createChrome function node: null, type: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // inherited from Context.prototype document: null, window: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // value properties sidePanelVisible: false, commandLineVisible: false, largeCommandLineVisible: false, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // object properties inspectButton: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * create: function() { PanelBar.create.call(this); if (Firebug.Inspector) this.inspectButton = new Button({ type: "toggle", element: $("fbChrome_btInspect"), owner: Firebug.Inspector, onPress: Firebug.Inspector.startInspecting, onUnpress: Firebug.Inspector.stopInspecting }); }, destroy: function() { if(Firebug.Inspector) this.inspectButton.destroy(); PanelBar.destroy.call(this); this.shutdown(); }, testMenu: function() { var firebugMenu = new Menu( { id: "fbFirebugMenu", items: [ { label: "Open Firebug", type: "shortcut", key: isFirefox ? "Shift+F12" : "F12", checked: true, command: "toggleChrome" }, { label: "Open Firebug in New Window", type: "shortcut", key: isFirefox ? "Ctrl+Shift+F12" : "Ctrl+F12", command: "openPopup" }, { label: "Inspect Element", type: "shortcut", key: "Ctrl+Shift+C", command: "toggleInspect" }, { label: "Command Line", type: "shortcut", key: "Ctrl+Shift+L", command: "focusCommandLine" }, "-", { label: "Options", type: "group", child: "fbFirebugOptionsMenu" }, "-", { label: "Firebug Lite Website...", command: "visitWebsite" }, { label: "Discussion Group...", command: "visitDiscussionGroup" }, { label: "Issue Tracker...", command: "visitIssueTracker" } ], onHide: function() { iconButton.restore(); }, toggleChrome: function() { Firebug.chrome.toggle(); }, openPopup: function() { Firebug.chrome.toggle(true, true); }, toggleInspect: function() { Firebug.Inspector.toggleInspect(); }, focusCommandLine: function() { Firebug.chrome.focusCommandLine(); }, visitWebsite: function() { this.visit("http://getfirebug.com/lite.html"); }, visitDiscussionGroup: function() { this.visit("http://groups.google.com/group/firebug"); }, visitIssueTracker: function() { this.visit("http://code.google.com/p/fbug/issues/list"); }, visit: function(url) { window.open(url); } }); /**@private*/ var firebugOptionsMenu = { id: "fbFirebugOptionsMenu", getItems: function() { var cookiesDisabled = !Firebug.saveCookies; return [ { label: "Start Opened", type: "checkbox", value: "startOpened", checked: Firebug.startOpened, disabled: cookiesDisabled }, { label: "Start in New Window", type: "checkbox", value: "startInNewWindow", checked: Firebug.startInNewWindow, disabled: cookiesDisabled }, { label: "Show Icon When Hidden", type: "checkbox", value: "showIconWhenHidden", checked: Firebug.showIconWhenHidden, disabled: cookiesDisabled }, { label: "Override Console Object", type: "checkbox", value: "overrideConsole", checked: Firebug.overrideConsole, disabled: cookiesDisabled }, { label: "Ignore Firebug Elements", type: "checkbox", value: "ignoreFirebugElements", checked: Firebug.ignoreFirebugElements, disabled: cookiesDisabled }, { label: "Disable When Firebug Active", type: "checkbox", value: "disableWhenFirebugActive", checked: Firebug.disableWhenFirebugActive, disabled: cookiesDisabled }, { label: "Disable XHR Listener", type: "checkbox", value: "disableXHRListener", checked: Firebug.disableXHRListener, disabled: cookiesDisabled }, { label: "Disable Resource Fetching", type: "checkbox", value: "disableResourceFetching", checked: Firebug.disableResourceFetching, disabled: cookiesDisabled }, { label: "Enable Trace Mode", type: "checkbox", value: "enableTrace", checked: Firebug.enableTrace, disabled: cookiesDisabled }, { label: "Enable Persistent Mode (experimental)", type: "checkbox", value: "enablePersistent", checked: Firebug.enablePersistent, disabled: cookiesDisabled }, "-", { label: "Reset All Firebug Options", command: "restorePrefs", disabled: cookiesDisabled } ]; }, onCheck: function(target, value, checked) { Firebug.setPref(value, checked); }, restorePrefs: function(target) { Firebug.erasePrefs(); if (target) this.updateMenu(target); }, updateMenu: function(target) { var options = getElementsByClass(target.parentNode, "fbMenuOption"); var firstOption = options[0]; var enabled = Firebug.saveCookies; if (enabled) Menu.check(firstOption); else Menu.uncheck(firstOption); if (enabled) Menu.check(options[0]); else Menu.uncheck(options[0]); for (var i = 1, length = options.length; i < length; i++) { var option = options[i]; var value = option.getAttribute("value"); var pref = Firebug[value]; if (pref) Menu.check(option); else Menu.uncheck(option); if (enabled) Menu.enable(option); else Menu.disable(option); } } }; Menu.register(firebugOptionsMenu); var menu = firebugMenu; var testMenuClick = function(event) { //console.log("testMenuClick"); cancelEvent(event, true); var target = event.target || event.srcElement; if (menu.isVisible) menu.hide(); else { var offsetLeft = isIE6 ? 1 : -4, // IE6 problem with fixed position chrome = Firebug.chrome, box = chrome.getElementBox(target), offset = chrome.type == "div" ? chrome.getElementPosition(chrome.node) : {top: 0, left: 0}; menu.show( box.left + offsetLeft - offset.left, box.top + box.height -5 - offset.top ); } return false; }; var iconButton = new IconButton({ type: "toggle", element: $("fbFirebugButton"), onClick: testMenuClick }); iconButton.initialize(); //addEvent($("fbToolbarIcon"), "click", testMenuClick); }, initialize: function() { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (Env.bookmarkletOutdated) Firebug.Console.logFormatted([ "A new bookmarklet version is available. " + "Please visit http://getfirebug.com/firebuglite#Install and update it." ], Firebug.context, "warn"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (Firebug.Console) Firebug.Console.flush(); if (Firebug.Trace) FBTrace.flush(Firebug.Trace); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.chrome.initialize", "initializing chrome application"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize inherited classes Controller.initialize.call(this); PanelBar.initialize.call(this); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create the interface elements cache fbTop = $("fbTop"); fbContent = $("fbContent"); fbContentStyle = fbContent.style; fbBottom = $("fbBottom"); fbBtnInspect = $("fbBtnInspect"); fbToolbar = $("fbToolbar"); fbPanelBox1 = $("fbPanelBox1"); fbPanelBox1Style = fbPanelBox1.style; fbPanelBox2 = $("fbPanelBox2"); fbPanelBox2Style = fbPanelBox2.style; fbPanelBar2Box = $("fbPanelBar2Box"); fbPanelBar2BoxStyle = fbPanelBar2Box.style; fbHSplitter = $("fbHSplitter"); fbVSplitter = $("fbVSplitter"); fbVSplitterStyle = fbVSplitter.style; fbPanel1 = $("fbPanel1"); fbPanel1Style = fbPanel1.style; fbPanel2 = $("fbPanel2"); fbPanel2Style = fbPanel2.style; fbConsole = $("fbConsole"); fbConsoleStyle = fbConsole.style; fbHTML = $("fbHTML"); fbCommandLine = $("fbCommandLine"); fbLargeCommandLine = $("fbLargeCommandLine"); fbLargeCommandButtons = $("fbLargeCommandButtons"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // static values cache topHeight = fbTop.offsetHeight; topPartialHeight = fbToolbar.offsetHeight; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * disableTextSelection($("fbToolbar")); disableTextSelection($("fbPanelBarBox")); disableTextSelection($("fbPanelBar1")); disableTextSelection($("fbPanelBar2")); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Add the "javascript:void(0)" href attributes used to make the hover effect in IE6 if (isIE6 && Firebug.Selector) { // TODO: xxxpedro change to getElementsByClass var as = $$(".fbHover"); for (var i=0, a; a=as[i]; i++) { a.setAttribute("href", "javascript:void(0)"); } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize all panels /* var panelMap = Firebug.panelTypes; for (var i=0, p; p=panelMap[i]; i++) { if (!p.parentPanel) { this.addPanel(p.prototype.name); } } /**/ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ if(Firebug.Inspector) this.inspectButton.initialize(); // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ this.addController( [$("fbLargeCommandLineIcon"), "click", this.showLargeCommandLine] ); // ************************************************************************************************ // Select the first registered panel // TODO: BUG IE7 var self = this; setTimeout(function(){ self.selectPanel(Firebug.context.persistedState.selectedPanelName); if (Firebug.context.persistedState.selectedPanelName == "Console" && Firebug.CommandLine) Firebug.chrome.focusCommandLine(); },0); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //this.draw(); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var onPanelMouseDown = function onPanelMouseDown(event) { //console.log("onPanelMouseDown", event.target || event.srcElement, event); var target = event.target || event.srcElement; if (FBL.isLeftClick(event)) { var editable = FBL.getAncestorByClass(target, "editable"); // if an editable element has been clicked then start editing if (editable) { Firebug.Editor.startEditing(editable); FBL.cancelEvent(event); } // if any other element has been clicked then stop editing else { if (!hasClass(target, "textEditorInner")) Firebug.Editor.stopEditing(); } } else if (FBL.isMiddleClick(event) && Firebug.getRepNode(target)) { // Prevent auto-scroll when middle-clicking a rep object FBL.cancelEvent(event); } }; Firebug.getElementPanel = function(element) { var panelNode = getAncestorByClass(element, "fbPanel"); var id = panelNode.id.substr(2); var panel = Firebug.chrome.panelMap[id]; if (!panel) { if (Firebug.chrome.selectedPanel.sidePanelBar) panel = Firebug.chrome.selectedPanel.sidePanelBar.panelMap[id]; } return panel; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // TODO: xxxpedro port to Firebug // Improved window key code event listener. Only one "keydown" event will be attached // to the window, and the onKeyCodeListen() function will delegate which listeners // should be called according to the event.keyCode fired. var onKeyCodeListenersMap = []; var onKeyCodeListen = function(event) { for (var keyCode in onKeyCodeListenersMap) { var listeners = onKeyCodeListenersMap[keyCode]; for (var i = 0, listener; listener = listeners[i]; i++) { var filter = listener.filter || FBL.noKeyModifiers; if (event.keyCode == keyCode && (!filter || filter(event))) { listener.listener(); FBL.cancelEvent(event, true); return false; } } } }; addEvent(Firebug.chrome.document, "keydown", onKeyCodeListen); /** * @name keyCodeListen * @memberOf FBL.FirebugChrome */ Firebug.chrome.keyCodeListen = function(key, filter, listener, capture) { var keyCode = KeyEvent["DOM_VK_"+key]; if (!onKeyCodeListenersMap[keyCode]) onKeyCodeListenersMap[keyCode] = []; onKeyCodeListenersMap[keyCode].push({ filter: filter, listener: listener }); return keyCode; }; /** * @name keyIgnore * @memberOf FBL.FirebugChrome */ Firebug.chrome.keyIgnore = function(keyCode) { onKeyCodeListenersMap[keyCode] = null; delete onKeyCodeListenersMap[keyCode]; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /**/ // move to shutdown //removeEvent(Firebug.chrome.document, "keydown", listener[0]); /* Firebug.chrome.keyCodeListen = function(key, filter, listener, capture) { if (!filter) filter = FBL.noKeyModifiers; var keyCode = KeyEvent["DOM_VK_"+key]; var fn = function fn(event) { if (event.keyCode == keyCode && (!filter || filter(event))) { listener(); FBL.cancelEvent(event, true); return false; } } addEvent(Firebug.chrome.document, "keydown", fn); return [fn, capture]; }; Firebug.chrome.keyIgnore = function(listener) { removeEvent(Firebug.chrome.document, "keydown", listener[0]); }; /**/ this.addController( [fbPanel1, "mousedown", onPanelMouseDown], [fbPanel2, "mousedown", onPanelMouseDown] ); /**/ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // menus can be used without domplate if (FBL.domplate) this.testMenu(); /**/ //test XHR /* setTimeout(function(){ FBL.Ajax.request({url: "../content/firebug/boot.js"}); FBL.Ajax.request({url: "../content/firebug/boot.js.invalid"}); },1000); /**/ }, shutdown: function() { // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ if(Firebug.Inspector) this.inspectButton.shutdown(); // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // remove disableTextSelection event handlers restoreTextSelection($("fbToolbar")); restoreTextSelection($("fbPanelBarBox")); restoreTextSelection($("fbPanelBar1")); restoreTextSelection($("fbPanelBar2")); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // shutdown inherited classes Controller.shutdown.call(this); PanelBar.shutdown.call(this); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Remove the interface elements cache (this must happen after calling // the shutdown method of all dependent components to avoid errors) fbTop = null; fbContent = null; fbContentStyle = null; fbBottom = null; fbBtnInspect = null; fbToolbar = null; fbPanelBox1 = null; fbPanelBox1Style = null; fbPanelBox2 = null; fbPanelBox2Style = null; fbPanelBar2Box = null; fbPanelBar2BoxStyle = null; fbHSplitter = null; fbVSplitter = null; fbVSplitterStyle = null; fbPanel1 = null; fbPanel1Style = null; fbPanel2 = null; fbConsole = null; fbConsoleStyle = null; fbHTML = null; fbCommandLine = null; fbLargeCommandLine = null; fbLargeCommandButtons = null; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // static values cache topHeight = null; topPartialHeight = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * toggle: function(forceOpen, popup) { if(popup) { this.detach(); } else { if (isOpera && Firebug.chrome.type == "popup" && Firebug.chrome.node.closed) { var frame = FirebugChrome.chromeMap.frame; frame.reattach(); FirebugChrome.chromeMap.popup = null; frame.open(); return; } // If the context is a popup, ignores the toggle process if (Firebug.chrome.type == "popup") return; var shouldOpen = forceOpen || !Firebug.context.persistedState.isOpen; if(shouldOpen) this.open(); else this.close(); } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * detach: function() { if(!FirebugChrome.chromeMap.popup) { this.close(); createChromeWindow({type: "popup"}); } }, reattach: function(oldChrome, newChrome) { Firebug.browser.window.Firebug = Firebug; // chrome synchronization var newPanelMap = newChrome.panelMap; var oldPanelMap = oldChrome.panelMap; var panel; for(var name in newPanelMap) { // TODO: xxxpedro innerHTML panel = newPanelMap[name]; if (panel.options.innerHTMLSync) panel.panelNode.innerHTML = oldPanelMap[name].panelNode.innerHTML; } Firebug.chrome = newChrome; // TODO: xxxpedro sync detach reattach attach //dispatch(Firebug.chrome.panelMap, "detach", [oldChrome, newChrome]); if (newChrome.type == "popup") { newChrome.initialize(); //dispatch(Firebug.modules, "initialize", []); } else { // TODO: xxxpedro only needed in persistent // should use FirebugChrome.clone, but popup FBChrome // isn't acessible Firebug.context.persistedState.selectedPanelName = oldChrome.selectedPanel.name; } dispatch(newPanelMap, "reattach", [oldChrome, newChrome]); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * draw: function() { var size = this.getSize(); // Height related values var commandLineHeight = Firebug.chrome.commandLineVisible ? fbCommandLine.offsetHeight : 0, y = Math.max(size.height /* chrome height */, topHeight), heightValue = Math.max(y - topHeight - commandLineHeight /* fixed height */, 0), height = heightValue + "px", // Width related values sideWidthValue = Firebug.chrome.sidePanelVisible ? Firebug.context.persistedState.sidePanelWidth : 0, width = Math.max(size.width /* chrome width */ - sideWidthValue, 0) + "px"; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Height related rendering fbPanelBox1Style.height = height; fbPanel1Style.height = height; if (isIE || isOpera) { // Fix IE and Opera problems with auto resizing the verticall splitter fbVSplitterStyle.height = Math.max(y - topPartialHeight - commandLineHeight, 0) + "px"; } //xxxpedro FF2 only? /* else if (isFirefox) { // Fix Firefox problem with table rows with 100% height (fit height) fbContentStyle.maxHeight = Math.max(y - fixedHeight, 0)+ "px"; }/**/ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Width related rendering fbPanelBox1Style.width = width; fbPanel1Style.width = width; // SidePanel rendering if (Firebug.chrome.sidePanelVisible) { sideWidthValue = Math.max(sideWidthValue - 6, 0); var sideWidth = sideWidthValue + "px"; fbPanelBox2Style.width = sideWidth; fbVSplitterStyle.right = sideWidth; if (Firebug.chrome.largeCommandLineVisible) { fbLargeCommandLine = $("fbLargeCommandLine"); fbLargeCommandLine.style.height = heightValue - 4 + "px"; fbLargeCommandLine.style.width = sideWidthValue - 2 + "px"; fbLargeCommandButtons = $("fbLargeCommandButtons"); fbLargeCommandButtons.style.width = sideWidth; } else { fbPanel2Style.height = height; fbPanel2Style.width = sideWidth; fbPanelBar2BoxStyle.width = sideWidth; } } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * getSize: function() { return this.type == "div" ? { height: this.node.offsetHeight, width: this.node.offsetWidth } : this.getWindowSize(); }, resize: function() { var self = this; // avoid partial resize when maximizing window setTimeout(function(){ self.draw(); if (noFixedPosition && (self.type == "frame" || self.type == "div")) self.fixIEPosition(); }, 0); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * layout: function(panel) { if (FBTrace.DBG_CHROME) FBTrace.sysout("Chrome.layout", ""); var options = panel.options; changeCommandLineVisibility(options.hasCommandLine); changeSidePanelVisibility(panel.hasSidePanel); Firebug.chrome.draw(); }, showLargeCommandLine: function(hideToggleIcon) { var chrome = Firebug.chrome; if (!chrome.largeCommandLineVisible) { chrome.largeCommandLineVisible = true; if (chrome.selectedPanel.options.hasCommandLine) { if (Firebug.CommandLine) Firebug.CommandLine.blur(); changeCommandLineVisibility(false); } changeSidePanelVisibility(true); fbLargeCommandLine.style.display = "block"; fbLargeCommandButtons.style.display = "block"; fbPanel2Style.display = "none"; fbPanelBar2BoxStyle.display = "none"; chrome.draw(); fbLargeCommandLine.focus(); if (Firebug.CommandLine) Firebug.CommandLine.setMultiLine(true); } }, hideLargeCommandLine: function() { if (Firebug.chrome.largeCommandLineVisible) { Firebug.chrome.largeCommandLineVisible = false; if (Firebug.CommandLine) Firebug.CommandLine.setMultiLine(false); fbLargeCommandLine.blur(); fbPanel2Style.display = "block"; fbPanelBar2BoxStyle.display = "block"; fbLargeCommandLine.style.display = "none"; fbLargeCommandButtons.style.display = "none"; changeSidePanelVisibility(false); if (Firebug.chrome.selectedPanel.options.hasCommandLine) changeCommandLineVisibility(true); Firebug.chrome.draw(); } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * focusCommandLine: function() { var selectedPanelName = this.selectedPanel.name, panelToSelect; if (focusCommandLineState == 0 || selectedPanelName != "Console") { focusCommandLineState = 0; lastFocusedPanelName = selectedPanelName; panelToSelect = "Console"; } if (focusCommandLineState == 1) { panelToSelect = lastFocusedPanelName; } this.selectPanel(panelToSelect); try { if (Firebug.CommandLine) { if (panelToSelect == "Console") Firebug.CommandLine.focus(); else Firebug.CommandLine.blur(); } } catch(e) { //TODO: xxxpedro trace error } focusCommandLineState = ++focusCommandLineState % 2; } }); // ************************************************************************************************ // ChromeFrameBase /** * @namespace * @extends ns-chrome-ChromeBase */ var ChromeFrameBase = extend(ChromeBase, /**@extend ns-chrome-ChromeFrameBase*/ { create: function() { ChromeBase.create.call(this); // restore display for the anti-flicker trick if (isFirefox) this.node.style.display = "block"; if (Env.Options.startInNewWindow) { this.close(); this.toggle(true, true); return; } if (Env.Options.startOpened) this.open(); else this.close(); }, destroy: function() { var size = Firebug.chrome.getWindowSize(); Firebug.context.persistedState.height = size.height; if (Firebug.saveCookies) Firebug.savePrefs(); removeGlobalEvent("keydown", onGlobalKeyDown); ChromeBase.destroy.call(this); this.document = null; delete this.document; this.window = null; delete this.window; this.node.parentNode.removeChild(this.node); this.node = null; delete this.node; }, initialize: function() { //FBTrace.sysout("Frame", "initialize();") ChromeBase.initialize.call(this); this.addController( [Firebug.browser.window, "resize", this.resize], [$("fbWindow_btClose"), "click", this.close], [$("fbWindow_btDetach"), "click", this.detach], [$("fbWindow_btDeactivate"), "click", this.deactivate] ); if (!Env.Options.enablePersistent) this.addController([Firebug.browser.window, "unload", Firebug.shutdown]); if (noFixedPosition) { this.addController( [Firebug.browser.window, "scroll", this.fixIEPosition] ); } fbVSplitter.onmousedown = onVSplitterMouseDown; fbHSplitter.onmousedown = onHSplitterMouseDown; this.isInitialized = true; }, shutdown: function() { fbVSplitter.onmousedown = null; fbHSplitter.onmousedown = null; ChromeBase.shutdown.apply(this); this.isInitialized = false; }, reattach: function() { var frame = FirebugChrome.chromeMap.frame; ChromeBase.reattach(FirebugChrome.chromeMap.popup, this); }, open: function() { if (!Firebug.context.persistedState.isOpen) { Firebug.context.persistedState.isOpen = true; if (Env.isChromeExtension) localStorage.setItem("Firebug", "1,1"); var node = this.node; node.style.visibility = "hidden"; // Avoid flickering if (Firebug.showIconWhenHidden) { if (ChromeMini.isInitialized) { ChromeMini.shutdown(); } } else node.style.display = "block"; var main = $("fbChrome"); // IE6 throws an error when setting this property! why? //main.style.display = "table"; main.style.display = ""; var self = this; /// TODO: xxxpedro FOUC node.style.visibility = "visible"; setTimeout(function(){ ///node.style.visibility = "visible"; //dispatch(Firebug.modules, "initialize", []); self.initialize(); if (noFixedPosition) self.fixIEPosition(); self.draw(); }, 10); } }, close: function() { if (Firebug.context.persistedState.isOpen) { if (this.isInitialized) { //dispatch(Firebug.modules, "shutdown", []); this.shutdown(); } Firebug.context.persistedState.isOpen = false; if (Env.isChromeExtension) localStorage.setItem("Firebug", "1,0"); var node = this.node; if (Firebug.showIconWhenHidden) { node.style.visibility = "hidden"; // Avoid flickering // TODO: xxxpedro - persist IE fixed? var main = $("fbChrome", FirebugChrome.chromeMap.frame.document); main.style.display = "none"; ChromeMini.initialize(); node.style.visibility = "visible"; } else node.style.display = "none"; } }, deactivate: function() { // if it is running as a Chrome extension, dispatch a message to the extension signaling // that Firebug should be deactivated for the current tab if (Env.isChromeExtension) { localStorage.removeItem("Firebug"); Firebug.GoogleChrome.dispatch("FB_deactivate"); // xxxpedro problem here regarding Chrome extension. We can't deactivate the whole // app, otherwise it won't be able to be reactivated without reloading the page. // but we need to stop listening global keys, otherwise the key activation won't work. Firebug.chrome.close(); } else { Firebug.shutdown(); } }, fixIEPosition: function() { // fix IE problem with offset when not in fullscreen mode var doc = this.document; var offset = isIE ? doc.body.clientTop || doc.documentElement.clientTop: 0; var size = Firebug.browser.getWindowSize(); var scroll = Firebug.browser.getWindowScrollPosition(); var maxHeight = size.height; var height = this.node.offsetHeight; var bodyStyle = doc.body.currentStyle; this.node.style.top = maxHeight - height + scroll.top + "px"; if ((this.type == "frame" || this.type == "div") && (bodyStyle.marginLeft || bodyStyle.marginRight)) { this.node.style.width = size.width + "px"; } if (fbVSplitterStyle) fbVSplitterStyle.right = Firebug.context.persistedState.sidePanelWidth + "px"; this.draw(); } }); // ************************************************************************************************ // ChromeMini /** * @namespace * @extends FBL.Controller */ var ChromeMini = extend(Controller, /**@extend ns-chrome-ChromeMini*/ { create: function(chrome) { append(this, chrome); this.type = "mini"; }, initialize: function() { Controller.initialize.apply(this); var doc = FirebugChrome.chromeMap.frame.document; var mini = $("fbMiniChrome", doc); mini.style.display = "block"; var miniIcon = $("fbMiniIcon", doc); var width = miniIcon.offsetWidth + 10; miniIcon.title = "Open " + Firebug.version; var errors = $("fbMiniErrors", doc); if (errors.offsetWidth) width += errors.offsetWidth + 10; var node = this.node; node.style.height = "27px"; node.style.width = width + "px"; node.style.left = ""; node.style.right = 0; if (this.node.nodeName.toLowerCase() == "iframe") { node.setAttribute("allowTransparency", "true"); this.document.body.style.backgroundColor = "transparent"; } else node.style.background = "transparent"; if (noFixedPosition) this.fixIEPosition(); this.addController( [$("fbMiniIcon", doc), "click", onMiniIconClick] ); if (noFixedPosition) { this.addController( [Firebug.browser.window, "scroll", this.fixIEPosition] ); } this.isInitialized = true; }, shutdown: function() { var node = this.node; node.style.height = Firebug.context.persistedState.height + "px"; node.style.width = "100%"; node.style.left = 0; node.style.right = ""; if (this.node.nodeName.toLowerCase() == "iframe") { node.setAttribute("allowTransparency", "false"); this.document.body.style.backgroundColor = "#fff"; } else node.style.background = "#fff"; if (noFixedPosition) this.fixIEPosition(); var doc = FirebugChrome.chromeMap.frame.document; var mini = $("fbMiniChrome", doc); mini.style.display = "none"; Controller.shutdown.apply(this); this.isInitialized = false; }, draw: function() { }, fixIEPosition: ChromeFrameBase.fixIEPosition }); // ************************************************************************************************ // ChromePopupBase /** * @namespace * @extends ns-chrome-ChromeBase */ var ChromePopupBase = extend(ChromeBase, /**@extend ns-chrome-ChromePopupBase*/ { initialize: function() { setClass(this.document.body, "FirebugPopup"); ChromeBase.initialize.call(this); this.addController( [Firebug.chrome.window, "resize", this.resize], [Firebug.chrome.window, "unload", this.destroy] //[Firebug.chrome.window, "beforeunload", this.destroy] ); if (Env.Options.enablePersistent) { this.persist = bind(this.persist, this); addEvent(Firebug.browser.window, "unload", this.persist); } else this.addController( [Firebug.browser.window, "unload", this.close] ); fbVSplitter.onmousedown = onVSplitterMouseDown; }, destroy: function() { var chromeWin = Firebug.chrome.window; var left = chromeWin.screenX || chromeWin.screenLeft; var top = chromeWin.screenY || chromeWin.screenTop; var size = Firebug.chrome.getWindowSize(); Firebug.context.persistedState.popupTop = top; Firebug.context.persistedState.popupLeft = left; Firebug.context.persistedState.popupWidth = size.width; Firebug.context.persistedState.popupHeight = size.height; if (Firebug.saveCookies) Firebug.savePrefs(); // TODO: xxxpedro sync detach reattach attach var frame = FirebugChrome.chromeMap.frame; if(frame) { dispatch(frame.panelMap, "detach", [this, frame]); frame.reattach(this, frame); } if (Env.Options.enablePersistent) { removeEvent(Firebug.browser.window, "unload", this.persist); } ChromeBase.destroy.apply(this); FirebugChrome.chromeMap.popup = null; this.node.close(); }, persist: function() { persistTimeStart = new Date().getTime(); removeEvent(Firebug.browser.window, "unload", this.persist); Firebug.Inspector.destroy(); Firebug.browser.window.FirebugOldBrowser = true; var persistTimeStart = new Date().getTime(); var waitMainWindow = function() { var doc, head; try { if (window.opener && !window.opener.FirebugOldBrowser && (doc = window.opener.document)/* && doc.documentElement && (head = doc.documentElement.firstChild)*/) { try { // exposes the FBL to the global namespace when in debug mode if (Env.isDebugMode) { window.FBL = FBL; } window.Firebug = Firebug; window.opener.Firebug = Firebug; Env.browser = window.opener; Firebug.browser = Firebug.context = new Context(Env.browser); Firebug.loadPrefs(); registerConsole(); // the delay time should be calculated right after registering the // console, once right after the console registration, call log messages // will be properly handled var persistDelay = new Date().getTime() - persistTimeStart; var chrome = Firebug.chrome; addEvent(Firebug.browser.window, "unload", chrome.persist); FBL.cacheDocument(); Firebug.Inspector.create(); Firebug.Console.logFormatted( ["Firebug could not capture console calls during " + persistDelay + "ms"], Firebug.context, "info" ); setTimeout(function(){ var htmlPanel = chrome.getPanel("HTML"); htmlPanel.createUI(); },50); } catch(pE) { alert("persist error: " + (pE.message || pE)); } } else { window.setTimeout(waitMainWindow, 0); } } catch (E) { window.close(); } }; waitMainWindow(); }, close: function() { this.destroy(); } }); //************************************************************************************************ // UI helpers var changeCommandLineVisibility = function changeCommandLineVisibility(visibility) { var last = Firebug.chrome.commandLineVisible; var visible = Firebug.chrome.commandLineVisible = typeof visibility == "boolean" ? visibility : !Firebug.chrome.commandLineVisible; if (visible != last) { if (visible) { fbBottom.className = ""; if (Firebug.CommandLine) Firebug.CommandLine.activate(); } else { if (Firebug.CommandLine) Firebug.CommandLine.deactivate(); fbBottom.className = "hide"; } } }; var changeSidePanelVisibility = function changeSidePanelVisibility(visibility) { var last = Firebug.chrome.sidePanelVisible; Firebug.chrome.sidePanelVisible = typeof visibility == "boolean" ? visibility : !Firebug.chrome.sidePanelVisible; if (Firebug.chrome.sidePanelVisible != last) { fbPanelBox2.className = Firebug.chrome.sidePanelVisible ? "" : "hide"; fbPanelBar2Box.className = Firebug.chrome.sidePanelVisible ? "" : "hide"; } }; // ************************************************************************************************ // F12 Handler var onGlobalKeyDown = function onGlobalKeyDown(event) { var keyCode = event.keyCode; var shiftKey = event.shiftKey; var ctrlKey = event.ctrlKey; if (keyCode == 123 /* F12 */ && (!isFirefox && !shiftKey || shiftKey && isFirefox)) { Firebug.chrome.toggle(false, ctrlKey); cancelEvent(event, true); // TODO: xxxpedro replace with a better solution. we're doing this // to allow reactivating with the F12 key after being deactivated if (Env.isChromeExtension) { Firebug.GoogleChrome.dispatch("FB_enableIcon"); } } else if (keyCode == 67 /* C */ && ctrlKey && shiftKey) { Firebug.Inspector.toggleInspect(); cancelEvent(event, true); } else if (keyCode == 76 /* L */ && ctrlKey && shiftKey) { Firebug.chrome.focusCommandLine(); cancelEvent(event, true); } }; var onMiniIconClick = function onMiniIconClick(event) { Firebug.chrome.toggle(false, event.ctrlKey); cancelEvent(event, true); }; // ************************************************************************************************ // Horizontal Splitter Handling var onHSplitterMouseDown = function onHSplitterMouseDown(event) { addGlobalEvent("mousemove", onHSplitterMouseMove); addGlobalEvent("mouseup", onHSplitterMouseUp); if (isIE) addEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp); fbHSplitter.className = "fbOnMovingHSplitter"; return false; }; var onHSplitterMouseMove = function onHSplitterMouseMove(event) { cancelEvent(event, true); var clientY = event.clientY; var win = isIE ? event.srcElement.ownerDocument.parentWindow : event.target.defaultView || event.target.ownerDocument && event.target.ownerDocument.defaultView; if (!win) return; if (win != win.parent) { var frameElement = win.frameElement; if (frameElement) { var framePos = Firebug.browser.getElementPosition(frameElement).top; clientY += framePos; if (frameElement.style.position != "fixed") clientY -= Firebug.browser.getWindowScrollPosition().top; } } if (isOpera && isQuiksMode && win.frameElement.id == "FirebugUI") { clientY = Firebug.browser.getWindowSize().height - win.frameElement.offsetHeight + clientY; } /* console.log( typeof win.FBL != "undefined" ? "no-Chrome" : "Chrome", //win.frameElement.id, event.target, clientY );/**/ onHSplitterMouseMoveBuffer = clientY; // buffer if (new Date().getTime() - lastHSplitterMouseMove > chromeRedrawSkipRate) // frame skipping { lastHSplitterMouseMove = new Date().getTime(); handleHSplitterMouseMove(); } else if (!onHSplitterMouseMoveTimer) onHSplitterMouseMoveTimer = setTimeout(handleHSplitterMouseMove, chromeRedrawSkipRate); // improving the resizing performance by canceling the mouse event. // canceling events will prevent the page to receive such events, which would imply // in more processing being expended. cancelEvent(event, true); return false; }; var handleHSplitterMouseMove = function() { if (onHSplitterMouseMoveTimer) { clearTimeout(onHSplitterMouseMoveTimer); onHSplitterMouseMoveTimer = null; } var clientY = onHSplitterMouseMoveBuffer; var windowSize = Firebug.browser.getWindowSize(); var scrollSize = Firebug.browser.getWindowScrollSize(); // compute chrome fixed size (top bar and command line) var commandLineHeight = Firebug.chrome.commandLineVisible ? fbCommandLine.offsetHeight : 0; var fixedHeight = topHeight + commandLineHeight; var chromeNode = Firebug.chrome.node; var scrollbarSize = !isIE && (scrollSize.width > windowSize.width) ? 17 : 0; //var height = !isOpera ? chromeNode.offsetTop + chromeNode.clientHeight : windowSize.height; var height = windowSize.height; // compute the min and max size of the chrome var chromeHeight = Math.max(height - clientY + 5 - scrollbarSize, fixedHeight); chromeHeight = Math.min(chromeHeight, windowSize.height - scrollbarSize); Firebug.context.persistedState.height = chromeHeight; chromeNode.style.height = chromeHeight + "px"; if (noFixedPosition) Firebug.chrome.fixIEPosition(); Firebug.chrome.draw(); }; var onHSplitterMouseUp = function onHSplitterMouseUp(event) { removeGlobalEvent("mousemove", onHSplitterMouseMove); removeGlobalEvent("mouseup", onHSplitterMouseUp); if (isIE) removeEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp); fbHSplitter.className = ""; Firebug.chrome.draw(); // avoid text selection in IE when returning to the document // after the mouse leaves the document during the resizing return false; }; // ************************************************************************************************ // Vertical Splitter Handling var onVSplitterMouseDown = function onVSplitterMouseDown(event) { addGlobalEvent("mousemove", onVSplitterMouseMove); addGlobalEvent("mouseup", onVSplitterMouseUp); return false; }; var onVSplitterMouseMove = function onVSplitterMouseMove(event) { if (new Date().getTime() - lastVSplitterMouseMove > chromeRedrawSkipRate) // frame skipping { var target = event.target || event.srcElement; if (target && target.ownerDocument) // avoid error when cursor reaches out of the chrome { var clientX = event.clientX; var win = document.all ? event.srcElement.ownerDocument.parentWindow : event.target.ownerDocument.defaultView; if (win != win.parent) clientX += win.frameElement ? win.frameElement.offsetLeft : 0; var size = Firebug.chrome.getSize(); var x = Math.max(size.width - clientX + 3, 6); Firebug.context.persistedState.sidePanelWidth = x; Firebug.chrome.draw(); } lastVSplitterMouseMove = new Date().getTime(); } cancelEvent(event, true); return false; }; var onVSplitterMouseUp = function onVSplitterMouseUp(event) { removeGlobalEvent("mousemove", onVSplitterMouseMove); removeGlobalEvent("mouseup", onVSplitterMouseUp); Firebug.chrome.draw(); }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite = { }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Cache = { ID: "firebug-" + new Date().getTime() }; // ************************************************************************************************ /** * TODO: if a cached element is cloned, the expando property will be cloned too in IE * which will result in a bug. Firebug Lite will think the new cloned node is the old * one. * * TODO: Investigate a possibility of cache validation, to be customized by each * kind of cache. For ElementCache it should validate if the element still is * inserted at the DOM. */ var cacheUID = 0; var createCache = function() { var map = {}; var data = {}; var CID = Firebug.Lite.Cache.ID; // better detection var supportsDeleteExpando = !document.all; var cacheFunction = function(element) { return cacheAPI.set(element); }; var cacheAPI = { get: function(key) { return map.hasOwnProperty(key) ? map[key] : null; }, set: function(element) { var id = getValidatedKey(element); if (!id) { id = ++cacheUID; element[CID] = id; } if (!map.hasOwnProperty(id)) { map[id] = element; data[id] = {}; } return id; }, unset: function(element) { var id = getValidatedKey(element); if (!id) return; if (supportsDeleteExpando) { delete element[CID]; } else if (element.removeAttribute) { element.removeAttribute(CID); } delete map[id]; delete data[id]; }, key: function(element) { return getValidatedKey(element); }, has: function(element) { var id = getValidatedKey(element); return id && map.hasOwnProperty(id); }, each: function(callback) { for (var key in map) { if (map.hasOwnProperty(key)) { callback(key, map[key]); } } }, data: function(element, name, value) { // set data if (value) { if (!name) return null; var id = cacheAPI.set(element); return data[id][name] = value; } // get data else { var id = cacheAPI.key(element); return data.hasOwnProperty(id) && data[id].hasOwnProperty(name) ? data[id][name] : null; } }, clear: function() { for (var id in map) { var element = map[id]; cacheAPI.unset(element); } } }; var getValidatedKey = function(element) { var id = element[CID]; // If a cached element is cloned in IE, the expando property CID will be also // cloned (differently than other browsers) resulting in a bug: Firebug Lite // will think the new cloned node is the old one. To prevent this problem we're // checking if the cached element matches the given element. if ( !supportsDeleteExpando && // the problem happens when supportsDeleteExpando is false id && // the element has the expando property map.hasOwnProperty(id) && // there is a cached element with the same id map[id] != element // but it is a different element than the current one ) { // remove the problematic property element.removeAttribute(CID); id = null; } return id; }; FBL.append(cacheFunction, cacheAPI); return cacheFunction; }; // ************************************************************************************************ // TODO: xxxpedro : check if we need really this on FBL scope Firebug.Lite.Cache.StyleSheet = createCache(); Firebug.Lite.Cache.Element = createCache(); // TODO: xxxpedro Firebug.Lite.Cache.Event = createCache(); // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ var sourceMap = {}; // ************************************************************************************************ Firebug.Lite.Proxy = { // jsonp callbacks _callbacks: {}, /** * Load a resource, either locally (directly) or externally (via proxy) using * synchronous XHR calls. Loading external resources requires the proxy plugin to * be installed and configured (see /plugin/proxy/proxy.php). */ load: function(url) { var resourceDomain = getDomain(url); var isLocalResource = // empty domain means local URL !resourceDomain || // same domain means local too resourceDomain == Firebug.context.window.location.host; // TODO: xxxpedro context return isLocalResource ? fetchResource(url) : fetchProxyResource(url); }, /** * Load a resource using JSONP technique. */ loadJSONP: function(url, callback) { var script = createGlobalElement("script"), doc = Firebug.context.document, uid = "" + new Date().getTime(), callbackName = "callback=Firebug.Lite.Proxy._callbacks." + uid, jsonpURL = url.indexOf("?") != -1 ? url + "&" + callbackName : url + "?" + callbackName; Firebug.Lite.Proxy._callbacks[uid] = function(data) { if (callback) callback(data); script.parentNode.removeChild(script); delete Firebug.Lite.Proxy._callbacks[uid]; }; script.src = jsonpURL; if (doc.documentElement) doc.documentElement.appendChild(script); }, /** * Load a resource using YQL (not reliable). */ YQL: function(url, callback) { var yql = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" + encodeURIComponent(url) + "%22&format=xml"; this.loadJSONP(yql, function(data) { var source = data.results[0]; // clean up YQL bogus elements var match = /\s+

([\s\S]+)<\/p>\s+<\/body>$/.exec(source); if (match) source = match[1]; console.log(source); }); } }; // ************************************************************************************************ Firebug.Lite.Proxy.fetchResourceDisabledMessage = "/* Firebug Lite resource fetching is disabled.\n" + "To enabled it set the Firebug Lite option \"disableResourceFetching\" to \"false\".\n" + "More info at http://getfirebug.com/firebuglite#Options */"; var fetchResource = function(url) { if (Firebug.disableResourceFetching) { var source = sourceMap[url] = Firebug.Lite.Proxy.fetchResourceDisabledMessage; return source; } if (sourceMap.hasOwnProperty(url)) return sourceMap[url]; // Getting the native XHR object so our calls won't be logged in the Console Panel var xhr = FBL.getNativeXHRObject(); xhr.open("get", url, false); xhr.send(); var source = sourceMap[url] = xhr.responseText; return source; }; var fetchProxyResource = function(url) { if (sourceMap.hasOwnProperty(url)) return sourceMap[url]; var proxyURL = Env.Location.baseDir + "plugin/proxy/proxy.php?url=" + encodeURIComponent(url); var response = fetchResource(proxyURL); try { var data = eval("(" + response + ")"); } catch(E) { return "ERROR: Firebug Lite Proxy plugin returned an invalid response."; } var source = data ? data.contents : ""; return source; }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Style = { }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Script = function(window) { this.fileName = null; this.isValid = null; this.baseLineNumber = null; this.lineExtent = null; this.tag = null; this.functionName = null; this.functionSource = null; }; Firebug.Lite.Script.prototype = { isLineExecutable: function(){}, pcToLine: function(){}, lineToPc: function(){}, toString: function() { return "Firebug.Lite.Script"; } }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Browser = function(window) { this.contentWindow = window; this.contentDocument = window.document; this.currentURI = { spec: window.location.href }; }; Firebug.Lite.Browser.prototype = { toString: function() { return "Firebug.Lite.Browser"; } }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ /* http://www.JSON.org/json2.js 2010-03-20 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL. This file creates a global JSON object containing two methods: stringify and parse. JSON.stringify(value, replacer, space) value any JavaScript value, usually an object or array. replacer an optional parameter that determines how object values are stringified for objects. It can be a function or an array of strings. space an optional parameter that specifies the indentation of nested structures. If it is omitted, the text will be packed without extra whitespace. If it is a number, it will specify the number of spaces to indent at each level. If it is a string (such as '\t' or ' '), it contains the characters used to indent at each level. This method produces a JSON text from a JavaScript value. When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified. A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized. The toJSON method will be passed the key associated with the value, and this will be bound to the value For example, this would serialize Dates as ISO strings. Date.prototype.toJSON = function (key) { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } return this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z'; }; You can provide an optional replacer method. It will be passed the key and value of each member, with this bound to the containing object. The value that is returned from your method will be serialized. If your method returns undefined, then the member will be excluded from the serialization. If the replacer parameter is an array of strings, then it will be used to select the members to be serialized. It filters the results such that only members with keys listed in the replacer array are stringified. Values that do not have JSON representations, such as undefined or functions, will not be serialized. Such values in objects will be dropped; in arrays they will be replaced with null. You can use a replacer function to replace those with JSON values. JSON.stringify(undefined) returns undefined. The optional space parameter produces a stringification of the value that is filled with line breaks and indentation to make it easier to read. If the space parameter is a non-empty string, then that string will be used for indentation. If the space parameter is a number, then the indentation will be that many spaces. Example: text = JSON.stringify(['e', {pluribus: 'unum'}]); // text is '["e",{"pluribus":"unum"}]' text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' text = JSON.stringify([new Date()], function (key, value) { return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value; }); // text is '["Date(---current time---)"]' JSON.parse(text, reviver) This method parses a JSON text to produce an object or array. It can throw a SyntaxError exception. The optional reviver parameter is a function that can filter and transform the results. It receives each of the keys and values, and its return value is used instead of the original value. If it returns what it received, then the structure is not modified. If it returns undefined then the member is deleted. Example: // Parse the text. Values that look like ISO date strings will // be converted to Date objects. myData = JSON.parse(text, function (key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }); myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { var d; if (typeof value === 'string' && value.slice(0, 5) === 'Date(' && value.slice(-1) === ')') { d = new Date(value.slice(5, -1)); if (d) { return d; } } return value; }); This is a reference implementation. You are free to copy, modify, or redistribute. */ /*jslint evil: true, strict: false */ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, prototype, push, replace, slice, stringify, test, toJSON, toString, valueOf */ // Create a JSON object only if one does not already exist. We create the // methods in a closure to avoid creating global variables. // ************************************************************************************************ var JSON = window.JSON || {}; // ************************************************************************************************ (function () { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') { Date.prototype.toJSON = function (key) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { // If the string contains no control characters, no quote characters, and no // backslash characters, then we can safely slap some quotes around it. // Otherwise we must also replace the offending characters with safe escape // sequences. escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function (a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { // Produce a string from holder[key]. var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // If we were called with a replacer function, then call the replacer to // obtain a replacement value. if (typeof rep === 'function') { value = rep.call(holder, key, value); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); // If the type is 'object', we might be dealing with an object or an array or // null. case 'object': // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // Is the value an array? if (Object.prototype.toString.apply(value) === '[object Array]') { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // If the replacer is an array, use it to select the members to be stringified. if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { k = rep[i]; if (typeof k === 'string') { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { // Otherwise, iterate through all of the keys in the object. for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } // If the JSON object does not yet have a stringify method, give it one. if (typeof JSON.stringify !== 'function') { JSON.stringify = function (value, replacer, space) { // The stringify method takes a value and an optional replacer, and an optional // space parameter, and returns a JSON text. The replacer can be a function // that can replace values, or an array of strings that will select the keys. // A default replacer method can be provided. Use of the space parameter can // produce text that is more easily readable. var i; gap = ''; indent = ''; // If the space parameter is a number, make an indent string containing that // many spaces. if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } // If the space parameter is a string, it will be used as the indent string. } else if (typeof space === 'string') { indent = space; } // If there is a replacer, it must be a function or an array. // Otherwise, throw an error. rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('JSON.stringify'); } // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', {'': value}); }; } // If the JSON object does not yet have a parse method, give it one. if (typeof JSON.parse !== 'function') { JSON.parse = function (text, reviver) { // The parse method takes a text and an optional reviver function, and returns // a JavaScript value if the text is a valid JSON text. var j; function walk(holder, key) { // The walk method is used to recursively walk the resulting structure so // that modifications can be made. var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } // Parsing happens in four stages. In the first stage, we replace certain // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } // In the second stage, we run the text against regular expressions that look // for non-JSON patterns. We are especially concerned with '()' and 'new' // because they can cause invocation, and '=' because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we // replace all simple value tokens with ']' characters. Third, we delete all // open brackets that follow a colon or comma or that begin the text. Finally, // we look to see that the remaining characters are only whitespace or ']' or // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. if (/^[\],:{}\s]*$/. test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { // In the third stage we use the eval function to compile the text into a // JavaScript structure. The '{' operator is subject to a syntactic ambiguity // in JavaScript: it can begin a block or an object literal. We wrap the text // in parens to eliminate the ambiguity. j = eval('(' + text + ')'); // In the optional fourth stage, we recursively walk the new structure, passing // each name/value pair to a reviver function for possible transformation. return typeof reviver === 'function' ? walk({'': j}, '') : j; } // If the text is not JSON parseable, then a SyntaxError is thrown. throw new SyntaxError('JSON.parse'); }; } // ************************************************************************************************ // registration FBL.JSON = JSON; // ************************************************************************************************ }()); /* See license.txt for terms of usage */ (function(){ // ************************************************************************************************ /* Copyright (c) 2010-2011 Marcus Westin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ var store = (function(){ var api = {}, win = window, doc = win.document, localStorageName = 'localStorage', globalStorageName = 'globalStorage', namespace = '__firebug__storejs__', storage api.disabled = false api.set = function(key, value) {} api.get = function(key) {} api.remove = function(key) {} api.clear = function() {} api.transact = function(key, transactionFn) { var val = api.get(key) if (typeof val == 'undefined') { val = {} } transactionFn(val) api.set(key, val) } api.serialize = function(value) { return JSON.stringify(value) } api.deserialize = function(value) { if (typeof value != 'string') { return undefined } return JSON.parse(value) } // Functions to encapsulate questionable FireFox 3.6.13 behavior // when about.config::dom.storage.enabled === false // See https://github.com/marcuswestin/store.js/issues#issue/13 function isLocalStorageNameSupported() { try { return (localStorageName in win && win[localStorageName]) } catch(err) { return false } } function isGlobalStorageNameSupported() { try { return (globalStorageName in win && win[globalStorageName] && win[globalStorageName][win.location.hostname]) } catch(err) { return false } } if (isLocalStorageNameSupported()) { storage = win[localStorageName] api.set = function(key, val) { storage.setItem(key, api.serialize(val)) } api.get = function(key) { return api.deserialize(storage.getItem(key)) } api.remove = function(key) { storage.removeItem(key) } api.clear = function() { storage.clear() } } else if (isGlobalStorageNameSupported()) { storage = win[globalStorageName][win.location.hostname] api.set = function(key, val) { storage[key] = api.serialize(val) } api.get = function(key) { return api.deserialize(storage[key] && storage[key].value) } api.remove = function(key) { delete storage[key] } api.clear = function() { for (var key in storage ) { delete storage[key] } } } else if (doc.documentElement.addBehavior) { var storage = doc.createElement('div') function withIEStorage(storeFunction) { return function() { var args = Array.prototype.slice.call(arguments, 0) args.unshift(storage) // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx // TODO: xxxpedro doc.body is not always available so we must use doc.documentElement. // We need to make sure this change won't affect the behavior of this library. doc.documentElement.appendChild(storage) storage.addBehavior('#default#userData') storage.load(localStorageName) var result = storeFunction.apply(api, args) doc.documentElement.removeChild(storage) return result } } api.set = withIEStorage(function(storage, key, val) { storage.setAttribute(key, api.serialize(val)) storage.save(localStorageName) }) api.get = withIEStorage(function(storage, key) { return api.deserialize(storage.getAttribute(key)) }) api.remove = withIEStorage(function(storage, key) { storage.removeAttribute(key) storage.save(localStorageName) }) api.clear = withIEStorage(function(storage) { var attributes = storage.XMLDocument.documentElement.attributes storage.load(localStorageName) for (var i=0, attr; attr = attributes[i]; i++) { storage.removeAttribute(attr.name) } storage.save(localStorageName) }) } try { api.set(namespace, namespace) if (api.get(namespace) != namespace) { api.disabled = true } api.remove(namespace) } catch(e) { api.disabled = true } return api })(); if (typeof module != 'undefined') { module.exports = store } // ************************************************************************************************ // registration FBL.Store = store; // ************************************************************************************************ })(); /* See license.txt for terms of usage */ FBL.ns( /**@scope s_selector*/ function() { with (FBL) { // ************************************************************************************************ /* * Sizzle CSS Selector Engine - v1.0 * Copyright 2009, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, done = 0, toString = Object.prototype.toString, hasDuplicate = false, baseHasDuplicate = true; // Here we check if the JavaScript engine is using some sort of // optimization where it does not always call our comparision // function. If that is the case, discard the hasDuplicate value. // Thus far that includes Google Chrome. [0, 0].sort(function(){ baseHasDuplicate = false; return 0; }); /** * @name Firebug.Selector * @namespace */ /** * @exports Sizzle as Firebug.Selector */ var Sizzle = function(selector, context, results, seed) { results = results || []; var origContext = context = context || document; if ( context.nodeType !== 1 && context.nodeType !== 9 ) { return []; } if ( !selector || typeof selector !== "string" ) { return results; } var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context), soFar = selector; // Reset the position of the chunker regexp (start from head) while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { soFar = m[3]; parts.push( m[1] ); if ( m[2] ) { extra = m[3]; break; } } if ( parts.length > 1 && origPOS.exec( selector ) ) { if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { set = posProcess( parts[0] + parts[1], context ); } else { set = Expr.relative[ parts[0] ] ? [ context ] : Sizzle( parts.shift(), context ); while ( parts.length ) { selector = parts.shift(); if ( Expr.relative[ selector ] ) selector += parts.shift(); set = posProcess( selector, set ); } } } else { // Take a shortcut and set the context if the root selector is an ID // (but not if it'll be faster if the inner selector is an ID) if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { var ret = Sizzle.find( parts.shift(), context, contextXML ); context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; } if ( context ) { var ret = seed ? { expr: parts.pop(), set: makeArray(seed) } : Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; if ( parts.length > 0 ) { checkSet = makeArray(set); } else { prune = false; } while ( parts.length ) { var cur = parts.pop(), pop = cur; if ( !Expr.relative[ cur ] ) { cur = ""; } else { pop = parts.pop(); } if ( pop == null ) { pop = context; } Expr.relative[ cur ]( checkSet, pop, contextXML ); } } else { checkSet = parts = []; } } if ( !checkSet ) { checkSet = set; } if ( !checkSet ) { throw "Syntax error, unrecognized expression: " + (cur || selector); } if ( toString.call(checkSet) === "[object Array]" ) { if ( !prune ) { results.push.apply( results, checkSet ); } else if ( context && context.nodeType === 1 ) { for ( var i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { results.push( set[i] ); } } } else { for ( var i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && checkSet[i].nodeType === 1 ) { results.push( set[i] ); } } } } else { makeArray( checkSet, results ); } if ( extra ) { Sizzle( extra, origContext, results, seed ); Sizzle.uniqueSort( results ); } return results; }; Sizzle.uniqueSort = function(results){ if ( sortOrder ) { hasDuplicate = baseHasDuplicate; results.sort(sortOrder); if ( hasDuplicate ) { for ( var i = 1; i < results.length; i++ ) { if ( results[i] === results[i-1] ) { results.splice(i--, 1); } } } } return results; }; Sizzle.matches = function(expr, set){ return Sizzle(expr, null, null, set); }; Sizzle.find = function(expr, context, isXML){ var set, match; if ( !expr ) { return []; } for ( var i = 0, l = Expr.order.length; i < l; i++ ) { var type = Expr.order[i], match; if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { var left = match[1]; match.splice(1,1); if ( left.substr( left.length - 1 ) !== "\\" ) { match[1] = (match[1] || "").replace(/\\/g, ""); set = Expr.find[ type ]( match, context, isXML ); if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break; } } } } if ( !set ) { set = context.getElementsByTagName("*"); } return {set: set, expr: expr}; }; Sizzle.filter = function(expr, set, inplace, not){ var old = expr, result = [], curLoop = set, match, anyFound, isXMLFilter = set && set[0] && isXML(set[0]); while ( expr && set.length ) { for ( var type in Expr.filter ) { if ( (match = Expr.match[ type ].exec( expr )) != null ) { var filter = Expr.filter[ type ], found, item; anyFound = false; if ( curLoop == result ) { result = []; } if ( Expr.preFilter[ type ] ) { match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); if ( !match ) { anyFound = found = true; } else if ( match === true ) { continue; } } if ( match ) { for ( var i = 0; (item = curLoop[i]) != null; i++ ) { if ( item ) { found = filter( item, match, i, curLoop ); var pass = not ^ !!found; if ( inplace && found != null ) { if ( pass ) { anyFound = true; } else { curLoop[i] = false; } } else if ( pass ) { result.push( item ); anyFound = true; } } } } if ( found !== undefined ) { if ( !inplace ) { curLoop = result; } expr = expr.replace( Expr.match[ type ], "" ); if ( !anyFound ) { return []; } break; } } } // Improper expression if ( expr == old ) { if ( anyFound == null ) { throw "Syntax error, unrecognized expression: " + expr; } else { break; } } old = expr; } return curLoop; }; /**#@+ @ignore */ var Expr = Sizzle.selectors = { order: [ "ID", "NAME", "TAG" ], match: { ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ }, leftMatch: {}, attrMap: { "class": "className", "for": "htmlFor" }, attrHandle: { href: function(elem){ return elem.getAttribute("href"); } }, relative: { "+": function(checkSet, part, isXML){ var isPartStr = typeof part === "string", isTag = isPartStr && !/\W/.test(part), isPartStrNotTag = isPartStr && !isTag; if ( isTag && !isXML ) { part = part.toUpperCase(); } for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { if ( (elem = checkSet[i]) ) { while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? elem || false : elem === part; } } if ( isPartStrNotTag ) { Sizzle.filter( part, checkSet, true ); } }, ">": function(checkSet, part, isXML){ var isPartStr = typeof part === "string"; if ( isPartStr && !/\W/.test(part) ) { part = isXML ? part : part.toUpperCase(); for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { var parent = elem.parentNode; checkSet[i] = parent.nodeName === part ? parent : false; } } } else { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { checkSet[i] = isPartStr ? elem.parentNode : elem.parentNode === part; } } if ( isPartStr ) { Sizzle.filter( part, checkSet, true ); } } }, "": function(checkSet, part, isXML){ var doneName = done++, checkFn = dirCheck; if ( !/\W/.test(part) ) { var nodeCheck = part = isXML ? part : part.toUpperCase(); checkFn = dirNodeCheck; } checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); }, "~": function(checkSet, part, isXML){ var doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !/\W/.test(part) ) { var nodeCheck = part = isXML ? part : part.toUpperCase(); checkFn = dirNodeCheck; } checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); } }, find: { ID: function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? [m] : []; } }, NAME: function(match, context, isXML){ if ( typeof context.getElementsByName !== "undefined" ) { var ret = [], results = context.getElementsByName(match[1]); for ( var i = 0, l = results.length; i < l; i++ ) { if ( results[i].getAttribute("name") === match[1] ) { ret.push( results[i] ); } } return ret.length === 0 ? null : ret; } }, TAG: function(match, context){ return context.getElementsByTagName(match[1]); } }, preFilter: { CLASS: function(match, curLoop, inplace, result, not, isXML){ match = " " + match[1].replace(/\\/g, "") + " "; if ( isXML ) { return match; } for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { if ( elem ) { if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { if ( !inplace ) result.push( elem ); } else if ( inplace ) { curLoop[i] = false; } } } return false; }, ID: function(match){ return match[1].replace(/\\/g, ""); }, TAG: function(match, curLoop){ for ( var i = 0; curLoop[i] === false; i++ ){} return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); }, CHILD: function(match){ if ( match[1] == "nth" ) { // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); // calculate the numbers (first)n+(last) including if they are negative match[2] = (test[1] + (test[2] || 1)) - 0; match[3] = test[3] - 0; } // TODO: Move to normal caching system match[0] = done++; return match; }, ATTR: function(match, curLoop, inplace, result, not, isXML){ var name = match[1].replace(/\\/g, ""); if ( !isXML && Expr.attrMap[name] ) { match[1] = Expr.attrMap[name]; } if ( match[2] === "~=" ) { match[4] = " " + match[4] + " "; } return match; }, PSEUDO: function(match, curLoop, inplace, result, not){ if ( match[1] === "not" ) { // If we're dealing with a complex expression, or a simple one if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { match[3] = Sizzle(match[3], null, null, curLoop); } else { var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); if ( !inplace ) { result.push.apply( result, ret ); } return false; } } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { return true; } return match; }, POS: function(match){ match.unshift( true ); return match; } }, filters: { enabled: function(elem){ return elem.disabled === false && elem.type !== "hidden"; }, disabled: function(elem){ return elem.disabled === true; }, checked: function(elem){ return elem.checked === true; }, selected: function(elem){ // Accessing this property makes selected-by-default // options in Safari work properly elem.parentNode.selectedIndex; return elem.selected === true; }, parent: function(elem){ return !!elem.firstChild; }, empty: function(elem){ return !elem.firstChild; }, has: function(elem, i, match){ return !!Sizzle( match[3], elem ).length; }, header: function(elem){ return /h\d/i.test( elem.nodeName ); }, text: function(elem){ return "text" === elem.type; }, radio: function(elem){ return "radio" === elem.type; }, checkbox: function(elem){ return "checkbox" === elem.type; }, file: function(elem){ return "file" === elem.type; }, password: function(elem){ return "password" === elem.type; }, submit: function(elem){ return "submit" === elem.type; }, image: function(elem){ return "image" === elem.type; }, reset: function(elem){ return "reset" === elem.type; }, button: function(elem){ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; }, input: function(elem){ return /input|select|textarea|button/i.test(elem.nodeName); } }, setFilters: { first: function(elem, i){ return i === 0; }, last: function(elem, i, match, array){ return i === array.length - 1; }, even: function(elem, i){ return i % 2 === 0; }, odd: function(elem, i){ return i % 2 === 1; }, lt: function(elem, i, match){ return i < match[3] - 0; }, gt: function(elem, i, match){ return i > match[3] - 0; }, nth: function(elem, i, match){ return match[3] - 0 == i; }, eq: function(elem, i, match){ return match[3] - 0 == i; } }, filter: { PSEUDO: function(elem, match, i, array){ var name = match[1], filter = Expr.filters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } else if ( name === "contains" ) { return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; } else if ( name === "not" ) { var not = match[3]; for ( var i = 0, l = not.length; i < l; i++ ) { if ( not[i] === elem ) { return false; } } return true; } }, CHILD: function(elem, match){ var type = match[1], node = elem; switch (type) { case 'only': case 'first': while ( (node = node.previousSibling) ) { if ( node.nodeType === 1 ) return false; } if ( type == 'first') return true; node = elem; case 'last': while ( (node = node.nextSibling) ) { if ( node.nodeType === 1 ) return false; } return true; case 'nth': var first = match[2], last = match[3]; if ( first == 1 && last == 0 ) { return true; } var doneName = match[0], parent = elem.parentNode; if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { var count = 0; for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { node.nodeIndex = ++count; } } parent.sizcache = doneName; } var diff = elem.nodeIndex - last; if ( first == 0 ) { return diff == 0; } else { return ( diff % first == 0 && diff / first >= 0 ); } } }, ID: function(elem, match){ return elem.nodeType === 1 && elem.getAttribute("id") === match; }, TAG: function(elem, match){ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; }, CLASS: function(elem, match){ return (" " + (elem.className || elem.getAttribute("class")) + " ") .indexOf( match ) > -1; }, ATTR: function(elem, match){ var name = match[1], result = Expr.attrHandle[ name ] ? Expr.attrHandle[ name ]( elem ) : elem[ name ] != null ? elem[ name ] : elem.getAttribute( name ), value = result + "", type = match[2], check = match[4]; return result == null ? type === "!=" : type === "=" ? value === check : type === "*=" ? value.indexOf(check) >= 0 : type === "~=" ? (" " + value + " ").indexOf(check) >= 0 : !check ? value && result !== false : type === "!=" ? value != check : type === "^=" ? value.indexOf(check) === 0 : type === "$=" ? value.substr(value.length - check.length) === check : type === "|=" ? value === check || value.substr(0, check.length + 1) === check + "-" : false; }, POS: function(elem, match, i, array){ var name = match[2], filter = Expr.setFilters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } } } }; var origPOS = Expr.match.POS; for ( var type in Expr.match ) { Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source ); } var makeArray = function(array, results) { array = Array.prototype.slice.call( array, 0 ); if ( results ) { results.push.apply( results, array ); return results; } return array; }; // Perform a simple check to determine if the browser is capable of // converting a NodeList to an array using builtin methods. try { Array.prototype.slice.call( document.documentElement.childNodes, 0 ); // Provide a fallback method if it does not work } catch(e){ makeArray = function(array, results) { var ret = results || []; if ( toString.call(array) === "[object Array]" ) { Array.prototype.push.apply( ret, array ); } else { if ( typeof array.length === "number" ) { for ( var i = 0, l = array.length; i < l; i++ ) { ret.push( array[i] ); } } else { for ( var i = 0; array[i]; i++ ) { ret.push( array[i] ); } } } return ret; }; } var sortOrder; if ( document.documentElement.compareDocumentPosition ) { sortOrder = function( a, b ) { if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { if ( a == b ) { hasDuplicate = true; } return 0; } var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } else if ( "sourceIndex" in document.documentElement ) { sortOrder = function( a, b ) { if ( !a.sourceIndex || !b.sourceIndex ) { if ( a == b ) { hasDuplicate = true; } return 0; } var ret = a.sourceIndex - b.sourceIndex; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } else if ( document.createRange ) { sortOrder = function( a, b ) { if ( !a.ownerDocument || !b.ownerDocument ) { if ( a == b ) { hasDuplicate = true; } return 0; } var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); aRange.setStart(a, 0); aRange.setEnd(a, 0); bRange.setStart(b, 0); bRange.setEnd(b, 0); var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } // Check to see if the browser returns elements by name when // querying by getElementById (and provide a workaround) (function(){ // We're going to inject a fake input element with a specified name var form = document.createElement("div"), id = "script" + (new Date).getTime(); form.innerHTML = ""; // Inject it into the root element, check its status, and remove it quickly var root = document.documentElement; root.insertBefore( form, root.firstChild ); // The workaround has to do additional checks after a getElementById // Which slows things down for other browsers (hence the branching) if ( !!document.getElementById( id ) ) { Expr.find.ID = function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; } }; Expr.filter.ID = function(elem, match){ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return elem.nodeType === 1 && node && node.nodeValue === match; }; } root.removeChild( form ); root = form = null; // release memory in IE })(); (function(){ // Check to see if the browser returns only elements // when doing getElementsByTagName("*") // Create a fake element var div = document.createElement("div"); div.appendChild( document.createComment("") ); // Make sure no comments are found if ( div.getElementsByTagName("*").length > 0 ) { Expr.find.TAG = function(match, context){ var results = context.getElementsByTagName(match[1]); // Filter out possible comments if ( match[1] === "*" ) { var tmp = []; for ( var i = 0; results[i]; i++ ) { if ( results[i].nodeType === 1 ) { tmp.push( results[i] ); } } results = tmp; } return results; }; } // Check to see if an attribute returns normalized href attributes div.innerHTML = ""; if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && div.firstChild.getAttribute("href") !== "#" ) { Expr.attrHandle.href = function(elem){ return elem.getAttribute("href", 2); }; } div = null; // release memory in IE })(); if ( document.querySelectorAll ) (function(){ var oldSizzle = Sizzle, div = document.createElement("div"); div.innerHTML = "

"; // Safari can't handle uppercase or unicode characters when // in quirks mode. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { return; } Sizzle = function(query, context, extra, seed){ context = context || document; // Only use querySelectorAll on non-XML documents // (ID selectors don't work in non-HTML documents) if ( !seed && context.nodeType === 9 && !isXML(context) ) { try { return makeArray( context.querySelectorAll(query), extra ); } catch(e){} } return oldSizzle(query, context, extra, seed); }; for ( var prop in oldSizzle ) { Sizzle[ prop ] = oldSizzle[ prop ]; } div = null; // release memory in IE })(); if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ var div = document.createElement("div"); div.innerHTML = "
"; // Opera can't find a second classname (in 9.6) if ( div.getElementsByClassName("e").length === 0 ) return; // Safari caches class attributes, doesn't catch changes (in 3.2) div.lastChild.className = "e"; if ( div.getElementsByClassName("e").length === 1 ) return; Expr.order.splice(1, 0, "CLASS"); Expr.find.CLASS = function(match, context, isXML) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { return context.getElementsByClassName(match[1]); } }; div = null; // release memory in IE })(); function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { var sibDir = dir == "previousSibling" && !isXML; for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { if ( sibDir && elem.nodeType === 1 ){ elem.sizcache = doneName; elem.sizset = i; } elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 && !isXML ){ elem.sizcache = doneName; elem.sizset = i; } if ( elem.nodeName === cur ) { match = elem; break; } elem = elem[dir]; } checkSet[i] = match; } } } function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { var sibDir = dir == "previousSibling" && !isXML; for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { if ( sibDir && elem.nodeType === 1 ) { elem.sizcache = doneName; elem.sizset = i; } elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { if ( !isXML ) { elem.sizcache = doneName; elem.sizset = i; } if ( typeof cur !== "string" ) { if ( elem === cur ) { match = true; break; } } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { match = elem; break; } } elem = elem[dir]; } checkSet[i] = match; } } } var contains = document.compareDocumentPosition ? function(a, b){ return a.compareDocumentPosition(b) & 16; } : function(a, b){ return a !== b && (a.contains ? a.contains(b) : true); }; var isXML = function(elem){ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML"; }; var posProcess = function(selector, context){ var tmpSet = [], later = "", match, root = context.nodeType ? [context] : context; // Position selectors must be done after the filter // And so must :not(positional) so we move all PSEUDOs to the end while ( (match = Expr.match.PSEUDO.exec( selector )) ) { later += match[0]; selector = selector.replace( Expr.match.PSEUDO, "" ); } selector = Expr.relative[selector] ? selector + "*" : selector; for ( var i = 0, l = root.length; i < l; i++ ) { Sizzle( selector, root[i], tmpSet ); } return Sizzle.filter( later, tmpSet ); }; // EXPOSE Firebug.Selector = Sizzle; /**#@-*/ // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Inspector Module var ElementCache = Firebug.Lite.Cache.Element; var inspectorTS, inspectorTimer, isInspecting; Firebug.Inspector = { create: function() { offlineFragment = Env.browser.document.createDocumentFragment(); createBoxModelInspector(); createOutlineInspector(); }, destroy: function() { destroyBoxModelInspector(); destroyOutlineInspector(); offlineFragment = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Inspect functions toggleInspect: function() { if (isInspecting) { this.stopInspecting(); } else { Firebug.chrome.inspectButton.changeState("pressed"); this.startInspecting(); } }, startInspecting: function() { isInspecting = true; Firebug.chrome.selectPanel("HTML"); createInspectorFrame(); var size = Firebug.browser.getWindowScrollSize(); fbInspectFrame.style.width = size.width + "px"; fbInspectFrame.style.height = size.height + "px"; //addEvent(Firebug.browser.document.documentElement, "mousemove", Firebug.Inspector.onInspectingBody); addEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting); addEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick); }, stopInspecting: function() { isInspecting = false; if (outlineVisible) this.hideOutline(); removeEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting); removeEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick); destroyInspectorFrame(); Firebug.chrome.inspectButton.restore(); if (Firebug.chrome.type == "popup") Firebug.chrome.node.focus(); }, onInspectingClick: function(e) { fbInspectFrame.style.display = "none"; var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY); fbInspectFrame.style.display = "block"; // Avoid inspecting the outline, and the FirebugUI var id = targ.id; if (id && /^fbOutline\w$/.test(id)) return; if (id == "FirebugUI") return; // Avoid looking at text nodes in Opera while (targ.nodeType != 1) targ = targ.parentNode; //Firebug.Console.log(targ); Firebug.Inspector.stopInspecting(); }, onInspecting: function(e) { if (new Date().getTime() - lastInspecting > 30) { fbInspectFrame.style.display = "none"; var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY); fbInspectFrame.style.display = "block"; // Avoid inspecting the outline, and the FirebugUI var id = targ.id; if (id && /^fbOutline\w$/.test(id)) return; if (id == "FirebugUI") return; // Avoid looking at text nodes in Opera while (targ.nodeType != 1) targ = targ.parentNode; if (targ.nodeName.toLowerCase() == "body") return; //Firebug.Console.log(e.clientX, e.clientY, targ); Firebug.Inspector.drawOutline(targ); if (ElementCache(targ)) { var target = ""+ElementCache.key(targ); var lazySelect = function() { inspectorTS = new Date().getTime(); if (Firebug.HTML) Firebug.HTML.selectTreeNode(""+ElementCache.key(targ)); }; if (inspectorTimer) { clearTimeout(inspectorTimer); inspectorTimer = null; } if (new Date().getTime() - inspectorTS > 200) setTimeout(lazySelect, 0); else inspectorTimer = setTimeout(lazySelect, 300); } lastInspecting = new Date().getTime(); } }, // TODO: xxxpedro remove this? onInspectingBody: function(e) { if (new Date().getTime() - lastInspecting > 30) { var targ = e.target; // Avoid inspecting the outline, and the FirebugUI var id = targ.id; if (id && /^fbOutline\w$/.test(id)) return; if (id == "FirebugUI") return; // Avoid looking at text nodes in Opera while (targ.nodeType != 1) targ = targ.parentNode; if (targ.nodeName.toLowerCase() == "body") return; //Firebug.Console.log(e.clientX, e.clientY, targ); Firebug.Inspector.drawOutline(targ); if (ElementCache.has(targ)) FBL.Firebug.HTML.selectTreeNode(""+ElementCache.key(targ)); lastInspecting = new Date().getTime(); } }, /** * * llttttttrr * llttttttrr * ll rr * ll rr * llbbbbbbrr * llbbbbbbrr */ drawOutline: function(el) { var border = 2; var scrollbarSize = 17; var windowSize = Firebug.browser.getWindowSize(); var scrollSize = Firebug.browser.getWindowScrollSize(); var scrollPosition = Firebug.browser.getWindowScrollPosition(); var box = Firebug.browser.getElementBox(el); var top = box.top; var left = box.left; var height = box.height; var width = box.width; var freeHorizontalSpace = scrollPosition.left + windowSize.width - left - width - (!isIE && scrollSize.height > windowSize.height ? // is *vertical* scrollbar visible scrollbarSize : 0); var freeVerticalSpace = scrollPosition.top + windowSize.height - top - height - (!isIE && scrollSize.width > windowSize.width ? // is *horizontal* scrollbar visible scrollbarSize : 0); var numVerticalBorders = freeVerticalSpace > 0 ? 2 : 1; var o = outlineElements; var style; style = o.fbOutlineT.style; style.top = top-border + "px"; style.left = left + "px"; style.height = border + "px"; // TODO: on initialize() style.width = width + "px"; style = o.fbOutlineL.style; style.top = top-border + "px"; style.left = left-border + "px"; style.height = height+ numVerticalBorders*border + "px"; style.width = border + "px"; // TODO: on initialize() style = o.fbOutlineB.style; if (freeVerticalSpace > 0) { style.top = top+height + "px"; style.left = left + "px"; style.width = width + "px"; //style.height = border + "px"; // TODO: on initialize() or worst case? } else { style.top = -2*border + "px"; style.left = -2*border + "px"; style.width = border + "px"; //style.height = border + "px"; } style = o.fbOutlineR.style; if (freeHorizontalSpace > 0) { style.top = top-border + "px"; style.left = left+width + "px"; style.height = height + numVerticalBorders*border + "px"; style.width = (freeHorizontalSpace < border ? freeHorizontalSpace : border) + "px"; } else { style.top = -2*border + "px"; style.left = -2*border + "px"; style.height = border + "px"; style.width = border + "px"; } if (!outlineVisible) this.showOutline(); }, hideOutline: function() { if (!outlineVisible) return; for (var name in outline) offlineFragment.appendChild(outlineElements[name]); outlineVisible = false; }, showOutline: function() { if (outlineVisible) return; if (boxModelVisible) this.hideBoxModel(); for (var name in outline) Firebug.browser.document.getElementsByTagName("body")[0].appendChild(outlineElements[name]); outlineVisible = true; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Box Model drawBoxModel: function(el) { // avoid error when the element is not attached a document if (!el || !el.parentNode) return; var box = Firebug.browser.getElementBox(el); var windowSize = Firebug.browser.getWindowSize(); var scrollPosition = Firebug.browser.getWindowScrollPosition(); // element may be occluded by the chrome, when in frame mode var offsetHeight = Firebug.chrome.type == "frame" ? Firebug.context.persistedState.height : 0; // if element box is not inside the viewport, don't draw the box model if (box.top > scrollPosition.top + windowSize.height - offsetHeight || box.left > scrollPosition.left + windowSize.width || scrollPosition.top > box.top + box.height || scrollPosition.left > box.left + box.width ) return; var top = box.top; var left = box.left; var height = box.height; var width = box.width; var margin = Firebug.browser.getMeasurementBox(el, "margin"); var padding = Firebug.browser.getMeasurementBox(el, "padding"); var border = Firebug.browser.getMeasurementBox(el, "border"); boxModelStyle.top = top - margin.top + "px"; boxModelStyle.left = left - margin.left + "px"; boxModelStyle.height = height + margin.top + margin.bottom + "px"; boxModelStyle.width = width + margin.left + margin.right + "px"; boxBorderStyle.top = margin.top + "px"; boxBorderStyle.left = margin.left + "px"; boxBorderStyle.height = height + "px"; boxBorderStyle.width = width + "px"; boxPaddingStyle.top = margin.top + border.top + "px"; boxPaddingStyle.left = margin.left + border.left + "px"; boxPaddingStyle.height = height - border.top - border.bottom + "px"; boxPaddingStyle.width = width - border.left - border.right + "px"; boxContentStyle.top = margin.top + border.top + padding.top + "px"; boxContentStyle.left = margin.left + border.left + padding.left + "px"; boxContentStyle.height = height - border.top - padding.top - padding.bottom - border.bottom + "px"; boxContentStyle.width = width - border.left - padding.left - padding.right - border.right + "px"; if (!boxModelVisible) this.showBoxModel(); }, hideBoxModel: function() { if (!boxModelVisible) return; offlineFragment.appendChild(boxModel); boxModelVisible = false; }, showBoxModel: function() { if (boxModelVisible) return; if (outlineVisible) this.hideOutline(); Firebug.browser.document.getElementsByTagName("body")[0].appendChild(boxModel); boxModelVisible = true; } }; // ************************************************************************************************ // Inspector Internals // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Shared variables // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Internal variables var offlineFragment = null; var boxModelVisible = false; var boxModel, boxModelStyle, boxMargin, boxMarginStyle, boxBorder, boxBorderStyle, boxPadding, boxPaddingStyle, boxContent, boxContentStyle; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;"; var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;"; var inspectStyle = resetStyle + "z-index: 2147483500;"; var inspectFrameStyle = resetStyle + "z-index: 2147483550; top:0; left:0; background:url(" + Env.Location.skinDir + "pixel_transparent.gif);"; //if (Env.Options.enableTrace) inspectFrameStyle = resetStyle + "z-index: 2147483550; top: 0; left: 0; background: #ff0; opacity: 0.05; _filter: alpha(opacity=5);"; var inspectModelOpacity = isIE ? "filter:alpha(opacity=80);" : "opacity:0.8;"; var inspectModelStyle = inspectStyle + inspectModelOpacity; var inspectMarginStyle = inspectStyle + "background: #EDFF64; height:100%; width:100%;"; var inspectBorderStyle = inspectStyle + "background: #666;"; var inspectPaddingStyle = inspectStyle + "background: SlateBlue;"; var inspectContentStyle = inspectStyle + "background: SkyBlue;"; var outlineStyle = { fbHorizontalLine: "background: #3875D7;height: 2px;", fbVerticalLine: "background: #3875D7;width: 2px;" }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var lastInspecting = 0; var fbInspectFrame = null; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var outlineVisible = false; var outlineElements = {}; var outline = { "fbOutlineT": "fbHorizontalLine", "fbOutlineL": "fbVerticalLine", "fbOutlineB": "fbHorizontalLine", "fbOutlineR": "fbVerticalLine" }; var getInspectingTarget = function() { }; // ************************************************************************************************ // Section var createInspectorFrame = function createInspectorFrame() { fbInspectFrame = createGlobalElement("div"); fbInspectFrame.id = "fbInspectFrame"; fbInspectFrame.firebugIgnore = true; fbInspectFrame.style.cssText = inspectFrameStyle; Firebug.browser.document.getElementsByTagName("body")[0].appendChild(fbInspectFrame); }; var destroyInspectorFrame = function destroyInspectorFrame() { if (fbInspectFrame) { Firebug.browser.document.getElementsByTagName("body")[0].removeChild(fbInspectFrame); fbInspectFrame = null; } }; var createOutlineInspector = function createOutlineInspector() { for (var name in outline) { var el = outlineElements[name] = createGlobalElement("div"); el.id = name; el.firebugIgnore = true; el.style.cssText = inspectStyle + outlineStyle[outline[name]]; offlineFragment.appendChild(el); } }; var destroyOutlineInspector = function destroyOutlineInspector() { for (var name in outline) { var el = outlineElements[name]; el.parentNode.removeChild(el); } }; var createBoxModelInspector = function createBoxModelInspector() { boxModel = createGlobalElement("div"); boxModel.id = "fbBoxModel"; boxModel.firebugIgnore = true; boxModelStyle = boxModel.style; boxModelStyle.cssText = inspectModelStyle; boxMargin = createGlobalElement("div"); boxMargin.id = "fbBoxMargin"; boxMarginStyle = boxMargin.style; boxMarginStyle.cssText = inspectMarginStyle; boxModel.appendChild(boxMargin); boxBorder = createGlobalElement("div"); boxBorder.id = "fbBoxBorder"; boxBorderStyle = boxBorder.style; boxBorderStyle.cssText = inspectBorderStyle; boxModel.appendChild(boxBorder); boxPadding = createGlobalElement("div"); boxPadding.id = "fbBoxPadding"; boxPaddingStyle = boxPadding.style; boxPaddingStyle.cssText = inspectPaddingStyle; boxModel.appendChild(boxPadding); boxContent = createGlobalElement("div"); boxContent.id = "fbBoxContent"; boxContentStyle = boxContent.style; boxContentStyle.cssText = inspectContentStyle; boxModel.appendChild(boxContent); offlineFragment.appendChild(boxModel); }; var destroyBoxModelInspector = function destroyBoxModelInspector() { boxModel.parentNode.removeChild(boxModel); }; // ************************************************************************************************ // Section // ************************************************************************************************ }}); // Problems in IE // FIXED - eval return // FIXED - addEventListener problem in IE // FIXED doc.createRange? // // class reserved word // test all honza examples in IE6 and IE7 /* See license.txt for terms of usage */ ( /** @scope s_domplate */ function() { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** @class */ FBL.DomplateTag = function DomplateTag(tagName) { this.tagName = tagName; }; /** * @class * @extends FBL.DomplateTag */ FBL.DomplateEmbed = function DomplateEmbed() { }; /** * @class * @extends FBL.DomplateTag */ FBL.DomplateLoop = function DomplateLoop() { }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var DomplateTag = FBL.DomplateTag; var DomplateEmbed = FBL.DomplateEmbed; var DomplateLoop = FBL.DomplateLoop; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var womb = null; FBL.domplate = function() { var lastSubject; for (var i = 0; i < arguments.length; ++i) lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i]; for (var name in lastSubject) { var val = lastSubject[name]; if (isTag(val)) val.tag.subject = lastSubject; } return lastSubject; }; var domplate = FBL.domplate; FBL.domplate.context = function(context, fn) { var lastContext = domplate.lastContext; domplate.topContext = context; fn.apply(context); domplate.topContext = lastContext; }; FBL.TAG = function() { var embed = new DomplateEmbed(); return embed.merge(arguments); }; FBL.FOR = function() { var loop = new DomplateLoop(); return loop.merge(arguments); }; FBL.DomplateTag.prototype = { merge: function(args, oldTag) { if (oldTag) this.tagName = oldTag.tagName; this.context = oldTag ? oldTag.context : null; this.subject = oldTag ? oldTag.subject : null; this.attrs = oldTag ? copyObject(oldTag.attrs) : {}; this.classes = oldTag ? copyObject(oldTag.classes) : {}; this.props = oldTag ? copyObject(oldTag.props) : null; this.listeners = oldTag ? copyArray(oldTag.listeners) : null; this.children = oldTag ? copyArray(oldTag.children) : []; this.vars = oldTag ? copyArray(oldTag.vars) : []; var attrs = args.length ? args[0] : null; var hasAttrs = typeof(attrs) == "object" && !isTag(attrs); this.children = []; if (domplate.topContext) this.context = domplate.topContext; if (args.length) parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children); if (hasAttrs) this.parseAttrs(attrs); return creator(this, DomplateTag); }, parseAttrs: function(args) { for (var name in args) { var val = parseValue(args[name]); readPartNames(val, this.vars); if (name.indexOf("on") == 0) { var eventName = name.substr(2); if (!this.listeners) this.listeners = []; this.listeners.push(eventName, val); } else if (name.indexOf("_") == 0) { var propName = name.substr(1); if (!this.props) this.props = {}; this.props[propName] = val; } else if (name.indexOf("$") == 0) { var className = name.substr(1); if (!this.classes) this.classes = {}; this.classes[className] = val; } else { if (name == "class" && this.attrs.hasOwnProperty(name) ) this.attrs[name] += " " + val; else this.attrs[name] = val; } } }, compile: function() { if (this.renderMarkup) return; this.compileMarkup(); this.compileDOM(); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate renderMarkup: ", this.renderMarkup); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate renderDOM:", this.renderDOM); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate domArgs:", this.domArgs); }, compileMarkup: function() { this.markupArgs = []; var topBlock = [], topOuts = [], blocks = [], info = {args: this.markupArgs, argIndex: 0}; this.generateMarkup(topBlock, topOuts, blocks, info); this.addCode(topBlock, topOuts, blocks); var fnBlock = ['r=(function (__code__, __context__, __in__, __out__']; for (var i = 0; i < info.argIndex; ++i) fnBlock.push(', s', i); fnBlock.push(') {'); if (this.subject) fnBlock.push('with (this) {'); if (this.context) fnBlock.push('with (__context__) {'); fnBlock.push('with (__in__) {'); fnBlock.push.apply(fnBlock, blocks); if (this.subject) fnBlock.push('}'); if (this.context) fnBlock.push('}'); fnBlock.push('}})'); function __link__(tag, code, outputs, args) { if (!tag || !tag.tag) return; tag.tag.compile(); var tagOutputs = []; var markupArgs = [code, tag.tag.context, args, tagOutputs]; markupArgs.push.apply(markupArgs, tag.tag.markupArgs); tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs); outputs.push(tag); outputs.push(tagOutputs); } function __escape__(value) { function replaceChars(ch) { switch (ch) { case "<": return "<"; case ">": return ">"; case "&": return "&"; case "'": return "'"; case '"': return """; } return "?"; }; return String(value).replace(/[<>&"']/g, replaceChars); } function __loop__(iter, outputs, fn) { var iterOuts = []; outputs.push(iterOuts); if (iter instanceof Array) iter = new ArrayIterator(iter); try { while (1) { var value = iter.next(); var itemOuts = [0,0]; iterOuts.push(itemOuts); fn.apply(this, [value, itemOuts]); } } catch (exc) { if (exc != StopIteration) throw exc; } } var js = fnBlock.join(""); var r = null; eval(js); this.renderMarkup = r; }, getVarNames: function(args) { if (this.vars) args.push.apply(args, this.vars); for (var i = 0; i < this.children.length; ++i) { var child = this.children[i]; if (isTag(child)) child.tag.getVarNames(args); else if (child instanceof Parts) { for (var i = 0; i < child.parts.length; ++i) { if (child.parts[i] instanceof Variable) { var name = child.parts[i].name; var names = name.split("."); args.push(names[0]); } } } } }, generateMarkup: function(topBlock, topOuts, blocks, info) { topBlock.push(',"<', this.tagName, '"'); for (var name in this.attrs) { if (name != "class") { var val = this.attrs[name]; topBlock.push(', " ', name, '=\\""'); addParts(val, ',', topBlock, info, true); topBlock.push(', "\\""'); } } if (this.listeners) { for (var i = 0; i < this.listeners.length; i += 2) readPartNames(this.listeners[i+1], topOuts); } if (this.props) { for (var name in this.props) readPartNames(this.props[name], topOuts); } if ( this.attrs.hasOwnProperty("class") || this.classes) { topBlock.push(', " class=\\""'); if (this.attrs.hasOwnProperty("class")) addParts(this.attrs["class"], ',', topBlock, info, true); topBlock.push(', " "'); for (var name in this.classes) { topBlock.push(', ('); addParts(this.classes[name], '', topBlock, info); topBlock.push(' ? "', name, '" + " " : "")'); } topBlock.push(', "\\""'); } topBlock.push(',">"'); this.generateChildMarkup(topBlock, topOuts, blocks, info); topBlock.push(',""'); }, generateChildMarkup: function(topBlock, topOuts, blocks, info) { for (var i = 0; i < this.children.length; ++i) { var child = this.children[i]; if (isTag(child)) child.tag.generateMarkup(topBlock, topOuts, blocks, info); else addParts(child, ',', topBlock, info, true); } }, addCode: function(topBlock, topOuts, blocks) { if (topBlock.length) blocks.push('__code__.push(""', topBlock.join(""), ');'); if (topOuts.length) blocks.push('__out__.push(', topOuts.join(","), ');'); topBlock.splice(0, topBlock.length); topOuts.splice(0, topOuts.length); }, addLocals: function(blocks) { var varNames = []; this.getVarNames(varNames); var map = {}; for (var i = 0; i < varNames.length; ++i) { var name = varNames[i]; if ( map.hasOwnProperty(name) ) continue; map[name] = 1; var names = name.split("."); blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';'); } }, compileDOM: function() { var path = []; var blocks = []; this.domArgs = []; path.embedIndex = 0; path.loopIndex = 0; path.staticIndex = 0; path.renderIndex = 0; var nodeCount = this.generateDOM(path, blocks, this.domArgs); var fnBlock = ['r=(function (root, context, o']; for (var i = 0; i < path.staticIndex; ++i) fnBlock.push(', ', 's'+i); for (var i = 0; i < path.renderIndex; ++i) fnBlock.push(', ', 'd'+i); fnBlock.push(') {'); for (var i = 0; i < path.loopIndex; ++i) fnBlock.push('var l', i, ' = 0;'); for (var i = 0; i < path.embedIndex; ++i) fnBlock.push('var e', i, ' = 0;'); if (this.subject) fnBlock.push('with (this) {'); if (this.context) fnBlock.push('with (context) {'); fnBlock.push(blocks.join("")); if (this.subject) fnBlock.push('}'); if (this.context) fnBlock.push('}'); fnBlock.push('return ', nodeCount, ';'); fnBlock.push('})'); function __bind__(object, fn) { return function(event) { return fn.apply(object, [event]); }; } function __link__(node, tag, args) { if (!tag || !tag.tag) return; tag.tag.compile(); var domArgs = [node, tag.tag.context, 0]; domArgs.push.apply(domArgs, tag.tag.domArgs); domArgs.push.apply(domArgs, args); //if (FBTrace.DBG_DOM) FBTrace.dumpProperties("domplate__link__ domArgs:", domArgs); return tag.tag.renderDOM.apply(tag.tag.subject, domArgs); } var self = this; function __loop__(iter, fn) { var nodeCount = 0; for (var i = 0; i < iter.length; ++i) { iter[i][0] = i; iter[i][1] = nodeCount; nodeCount += fn.apply(this, iter[i]); //if (FBTrace.DBG_DOM) FBTrace.sysout("nodeCount", nodeCount); } return nodeCount; } function __path__(parent, offset) { //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate __path__ offset: "+ offset+"\n"); var root = parent; for (var i = 2; i < arguments.length; ++i) { var index = arguments[i]; if (i == 3) index += offset; if (index == -1) parent = parent.parentNode; else parent = parent.childNodes[index]; } //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate: "+arguments[2]+", root: "+ root+", parent: "+ parent+"\n"); return parent; } var js = fnBlock.join(""); //if (FBTrace.DBG_DOM) FBTrace.sysout(js.replace(/(\;|\{)/g, "$1\n")); var r = null; eval(js); this.renderDOM = r; }, generateDOM: function(path, blocks, args) { if (this.listeners || this.props) this.generateNodePath(path, blocks); if (this.listeners) { for (var i = 0; i < this.listeners.length; i += 2) { var val = this.listeners[i+1]; var arg = generateArg(val, path, args); //blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);'); blocks.push('addEvent(node, "', this.listeners[i], '", __bind__(this, ', arg, '), false);'); } } if (this.props) { for (var name in this.props) { var val = this.props[name]; var arg = generateArg(val, path, args); blocks.push('node.', name, ' = ', arg, ';'); } } this.generateChildDOM(path, blocks, args); return 1; }, generateNodePath: function(path, blocks) { blocks.push("var node = __path__(root, o"); for (var i = 0; i < path.length; ++i) blocks.push(",", path[i]); blocks.push(");"); }, generateChildDOM: function(path, blocks, args) { path.push(0); for (var i = 0; i < this.children.length; ++i) { var child = this.children[i]; if (isTag(child)) path[path.length-1] += '+' + child.tag.generateDOM(path, blocks, args); else path[path.length-1] += '+1'; } path.pop(); } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * FBL.DomplateEmbed.prototype = copyObject(FBL.DomplateTag.prototype, /** @lends FBL.DomplateEmbed.prototype */ { merge: function(args, oldTag) { this.value = oldTag ? oldTag.value : parseValue(args[0]); this.attrs = oldTag ? oldTag.attrs : {}; this.vars = oldTag ? copyArray(oldTag.vars) : []; var attrs = args[1]; for (var name in attrs) { var val = parseValue(attrs[name]); this.attrs[name] = val; readPartNames(val, this.vars); } return creator(this, DomplateEmbed); }, getVarNames: function(names) { if (this.value instanceof Parts) names.push(this.value.parts[0].name); if (this.vars) names.push.apply(names, this.vars); }, generateMarkup: function(topBlock, topOuts, blocks, info) { this.addCode(topBlock, topOuts, blocks); blocks.push('__link__('); addParts(this.value, '', blocks, info); blocks.push(', __code__, __out__, {'); var lastName = null; for (var name in this.attrs) { if (lastName) blocks.push(','); lastName = name; var val = this.attrs[name]; blocks.push('"', name, '":'); addParts(val, '', blocks, info); } blocks.push('});'); //this.generateChildMarkup(topBlock, topOuts, blocks, info); }, generateDOM: function(path, blocks, args) { var embedName = 'e'+path.embedIndex++; this.generateNodePath(path, blocks); var valueName = 'd' + path.renderIndex++; var argsName = 'd' + path.renderIndex++; blocks.push(embedName + ' = __link__(node, ', valueName, ', ', argsName, ');'); return embedName; } }); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * FBL.DomplateLoop.prototype = copyObject(FBL.DomplateTag.prototype, /** @lends FBL.DomplateLoop.prototype */ { merge: function(args, oldTag) { this.varName = oldTag ? oldTag.varName : args[0]; this.iter = oldTag ? oldTag.iter : parseValue(args[1]); this.vars = []; this.children = oldTag ? copyArray(oldTag.children) : []; var offset = Math.min(args.length, 2); parseChildren(args, offset, this.vars, this.children); return creator(this, DomplateLoop); }, getVarNames: function(names) { if (this.iter instanceof Parts) names.push(this.iter.parts[0].name); DomplateTag.prototype.getVarNames.apply(this, [names]); }, generateMarkup: function(topBlock, topOuts, blocks, info) { this.addCode(topBlock, topOuts, blocks); var iterName; if (this.iter instanceof Parts) { var part = this.iter.parts[0]; iterName = part.name; if (part.format) { for (var i = 0; i < part.format.length; ++i) iterName = part.format[i] + "(" + iterName + ")"; } } else iterName = this.iter; blocks.push('__loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {'); this.generateChildMarkup(topBlock, topOuts, blocks, info); this.addCode(topBlock, topOuts, blocks); blocks.push('}]);'); }, generateDOM: function(path, blocks, args) { var iterName = 'd'+path.renderIndex++; var counterName = 'i'+path.loopIndex; var loopName = 'l'+path.loopIndex++; if (!path.length) path.push(-1, 0); var preIndex = path.renderIndex; path.renderIndex = 0; var nodeCount = 0; var subBlocks = []; var basePath = path[path.length-1]; for (var i = 0; i < this.children.length; ++i) { path[path.length-1] = basePath+'+'+loopName+'+'+nodeCount; var child = this.children[i]; if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args); else nodeCount += '+1'; } path[path.length-1] = basePath+'+'+loopName; blocks.push(loopName,' = __loop__.apply(this, [', iterName, ', function(', counterName,',',loopName); for (var i = 0; i < path.renderIndex; ++i) blocks.push(',d'+i); blocks.push(') {'); blocks.push(subBlocks.join("")); blocks.push('return ', nodeCount, ';'); blocks.push('}]);'); path.renderIndex = preIndex; return loopName; } }); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** @class */ function Variable(name, format) { this.name = name; this.format = format; } /** @class */ function Parts(parts) { this.parts = parts; } // ************************************************************************************************ function parseParts(str) { var re = /\$([_A-Za-z][_A-Za-z0-9.|]*)/g; var index = 0; var parts = []; var m; while (m = re.exec(str)) { var pre = str.substr(index, (re.lastIndex-m[0].length)-index); if (pre) parts.push(pre); var expr = m[1].split("|"); parts.push(new Variable(expr[0], expr.slice(1))); index = re.lastIndex; } if (!index) return str; var post = str.substr(index); if (post) parts.push(post); return new Parts(parts); } function parseValue(val) { return typeof(val) == 'string' ? parseParts(val) : val; } function parseChildren(args, offset, vars, children) { for (var i = offset; i < args.length; ++i) { var val = parseValue(args[i]); children.push(val); readPartNames(val, vars); } } function readPartNames(val, vars) { if (val instanceof Parts) { for (var i = 0; i < val.parts.length; ++i) { var part = val.parts[i]; if (part instanceof Variable) vars.push(part.name); } } } function generateArg(val, path, args) { if (val instanceof Parts) { var vals = []; for (var i = 0; i < val.parts.length; ++i) { var part = val.parts[i]; if (part instanceof Variable) { var varName = 'd'+path.renderIndex++; if (part.format) { for (var j = 0; j < part.format.length; ++j) varName = part.format[j] + '(' + varName + ')'; } vals.push(varName); } else vals.push('"'+part.replace(/"/g, '\\"')+'"'); } return vals.join('+'); } else { args.push(val); return 's' + path.staticIndex++; } } function addParts(val, delim, block, info, escapeIt) { var vals = []; if (val instanceof Parts) { for (var i = 0; i < val.parts.length; ++i) { var part = val.parts[i]; if (part instanceof Variable) { var partName = part.name; if (part.format) { for (var j = 0; j < part.format.length; ++j) partName = part.format[j] + "(" + partName + ")"; } if (escapeIt) vals.push("__escape__(" + partName + ")"); else vals.push(partName); } else vals.push('"'+ part + '"'); } } else if (isTag(val)) { info.args.push(val); vals.push('s'+info.argIndex++); } else vals.push('"'+ val + '"'); var parts = vals.join(delim); if (parts) block.push(delim, parts); } function isTag(obj) { return (typeof(obj) == "function" || obj instanceof Function) && !!obj.tag; } function creator(tag, cons) { var fn = new Function( "var tag = arguments.callee.tag;" + "var cons = arguments.callee.cons;" + "var newTag = new cons();" + "return newTag.merge(arguments, tag);"); fn.tag = tag; fn.cons = cons; extend(fn, Renderer); return fn; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function copyArray(oldArray) { var ary = []; if (oldArray) for (var i = 0; i < oldArray.length; ++i) ary.push(oldArray[i]); return ary; } function copyObject(l, r) { var m = {}; extend(m, l); extend(m, r); return m; } function extend(l, r) { for (var n in r) l[n] = r[n]; } function addEvent(object, name, handler) { if (document.all) object.attachEvent("on"+name, handler); else object.addEventListener(name, handler, false); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** @class */ function ArrayIterator(array) { var index = -1; this.next = function() { if (++index >= array.length) throw StopIteration; return array[index]; }; } /** @class */ function StopIteration() {} FBL.$break = function() { throw StopIteration; }; // ************************************************************************************************ /** @namespace */ var Renderer = { renderHTML: function(args, outputs, self) { var code = []; var markupArgs = [code, this.tag.context, args, outputs]; markupArgs.push.apply(markupArgs, this.tag.markupArgs); this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs); return code.join(""); }, insertRows: function(args, before, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); var doc = before.ownerDocument; var div = doc.createElement("div"); div.innerHTML = ""+html+"
"; var tbody = div.firstChild.firstChild; var parent = before.tagName == "TR" ? before.parentNode : before; var after = before.tagName == "TR" ? before.nextSibling : null; var firstRow = tbody.firstChild, lastRow; while (tbody.firstChild) { lastRow = tbody.firstChild; if (after) parent.insertBefore(lastRow, after); else parent.appendChild(lastRow); } var offset = 0; if (before.tagName == "TR") { var node = firstRow.parentNode.firstChild; for (; node && node != firstRow; node = node.nextSibling) ++offset; } var domArgs = [firstRow, this.tag.context, offset]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return [firstRow, lastRow]; }, insertBefore: function(args, before, self) { return this.insertNode(args, before.ownerDocument, before, false, self); }, insertAfter: function(args, after, self) { return this.insertNode(args, after.ownerDocument, after, true, self); }, insertNode: function(args, doc, element, isAfter, self) { if (!args) args = {}; this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); //if (FBTrace.DBG_DOM) // FBTrace.sysout("domplate.insertNode html: "+html+"\n"); var doc = element.ownerDocument; if (!womb || womb.ownerDocument != doc) womb = doc.createElement("div"); womb.innerHTML = html; var root = womb.firstChild; if (isAfter) { while (womb.firstChild) if (element.nextSibling) element.parentNode.insertBefore(womb.firstChild, element.nextSibling); else element.parentNode.appendChild(womb.firstChild); } else { while (womb.lastChild) element.parentNode.insertBefore(womb.lastChild, element); } var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); //if (FBTrace.DBG_DOM) // FBTrace.sysout("domplate.insertNode domArgs:", domArgs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return root; }, /**/ /* insertAfter: function(args, before, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); var doc = before.ownerDocument; if (!womb || womb.ownerDocument != doc) womb = doc.createElement("div"); womb.innerHTML = html; var root = womb.firstChild; while (womb.firstChild) if (before.nextSibling) before.parentNode.insertBefore(womb.firstChild, before.nextSibling); else before.parentNode.appendChild(womb.firstChild); var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); this.tag.renderDOM.apply(self ? self : (this.tag.subject ? this.tag.subject : null), domArgs); return root; }, /**/ replace: function(args, parent, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); var root; if (parent.nodeType == 1) { parent.innerHTML = html; root = parent.firstChild; } else { if (!parent || parent.nodeType != 9) parent = document; if (!womb || womb.ownerDocument != parent) womb = parent.createElement("div"); womb.innerHTML = html; root = womb.firstChild; //womb.removeChild(root); } var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return root; }, append: function(args, parent, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate.append html: "+html+"\n"); if (!womb || womb.ownerDocument != parent.ownerDocument) womb = parent.ownerDocument.createElement("div"); womb.innerHTML = html; // TODO: xxxpedro domplate port to Firebug var root = womb.firstChild; while (womb.firstChild) parent.appendChild(womb.firstChild); // clearing element reference to avoid reference error in IE8 when switching contexts womb = null; var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); //if (FBTrace.DBG_DOM) FBTrace.dumpProperties("domplate append domArgs:", domArgs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return root; } }; // ************************************************************************************************ function defineTags() { for (var i = 0; i < arguments.length; ++i) { var tagName = arguments[i]; var fn = new Function("var newTag = new arguments.callee.DomplateTag('"+tagName+"'); return newTag.merge(arguments);"); fn.DomplateTag = DomplateTag; var fnName = tagName.toUpperCase(); FBL[fnName] = fn; } } defineTags( "a", "button", "br", "canvas", "code", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select", "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul", "iframe" ); })(); /* See license.txt for terms of usage */ var FirebugReps = FBL.ns(function() { with (FBL) { // ************************************************************************************************ // Common Tags var OBJECTBOX = this.OBJECTBOX = SPAN({"class": "objectBox objectBox-$className"}); var OBJECTBLOCK = this.OBJECTBLOCK = DIV({"class": "objectBox objectBox-$className"}); var OBJECTLINK = this.OBJECTLINK = isIE6 ? // IE6 object link representation A({ "class": "objectLink objectLink-$className a11yFocus", href: "javascript:void(0)", // workaround to show XPath (a better approach would use the tooltip on mouseover, // so the XPath information would be calculated dynamically, but we need to create // a tooltip class/wrapper around Menu or InfoTip) title: "$object|FBL.getElementXPath", _repObject: "$object" }) : // Other browsers A({ "class": "objectLink objectLink-$className a11yFocus", // workaround to show XPath (a better approach would use the tooltip on mouseover, // so the XPath information would be calculated dynamically, but we need to create // a tooltip class/wrapper around Menu or InfoTip) title: "$object|FBL.getElementXPath", _repObject: "$object" }); // ************************************************************************************************ this.Undefined = domplate(Firebug.Rep, { tag: OBJECTBOX("undefined"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "undefined", supportsObject: function(object, type) { return type == "undefined"; } }); // ************************************************************************************************ this.Null = domplate(Firebug.Rep, { tag: OBJECTBOX("null"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "null", supportsObject: function(object, type) { return object == null; } }); // ************************************************************************************************ this.Nada = domplate(Firebug.Rep, { tag: SPAN(""), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "nada" }); // ************************************************************************************************ this.Number = domplate(Firebug.Rep, { tag: OBJECTBOX("$object"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "number", supportsObject: function(object, type) { return type == "boolean" || type == "number"; } }); // ************************************************************************************************ this.String = domplate(Firebug.Rep, { tag: OBJECTBOX(""$object""), shortTag: OBJECTBOX(""$object|cropString""), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "string", supportsObject: function(object, type) { return type == "string"; } }); // ************************************************************************************************ this.Text = domplate(Firebug.Rep, { tag: OBJECTBOX("$object"), shortTag: OBJECTBOX("$object|cropString"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "text" }); // ************************************************************************************************ this.Caption = domplate(Firebug.Rep, { tag: SPAN({"class": "caption"}, "$object") }); // ************************************************************************************************ this.Warning = domplate(Firebug.Rep, { tag: DIV({"class": "warning focusRow", role : 'listitem'}, "$object|STR") }); // ************************************************************************************************ this.Func = domplate(Firebug.Rep, { tag: OBJECTLINK("$object|summarizeFunction"), summarizeFunction: function(fn) { var fnRegex = /function ([^(]+\([^)]*\)) \{/; var fnText = safeToString(fn); var m = fnRegex.exec(fnText); return m ? m[1] : "function()"; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * copySource: function(fn) { copyToClipboard(safeToString(fn)); }, monitor: function(fn, script, monitored) { if (monitored) Firebug.Debugger.unmonitorScript(fn, script, "monitor"); else Firebug.Debugger.monitorScript(fn, script, "monitor"); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "function", supportsObject: function(object, type) { return isFunction(object); }, inspectObject: function(fn, context) { var sourceLink = findSourceForFunction(fn, context); if (sourceLink) Firebug.chrome.select(sourceLink); if (FBTrace.DBG_FUNCTION_NAME) FBTrace.sysout("reps.function.inspectObject selected sourceLink is ", sourceLink); }, getTooltip: function(fn, context) { var script = findScriptForFunctionInContext(context, fn); if (script) return $STRF("Line", [normalizeURL(script.fileName), script.baseLineNumber]); else if (fn.toString) return fn.toString(); }, getTitle: function(fn, context) { var name = fn.name ? fn.name : "function"; return name + "()"; }, getContextMenuItems: function(fn, target, context, script) { if (!script) script = findScriptForFunctionInContext(context, fn); if (!script) return; var scriptInfo = getSourceFileAndLineByScript(context, script); var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false; var name = script ? getFunctionName(script, context) : fn.name; return [ {label: "CopySource", command: bindFixed(this.copySource, this, fn) }, "-", {label: $STRF("ShowCallsInConsole", [name]), nol10n: true, type: "checkbox", checked: monitored, command: bindFixed(this.monitor, this, fn, script, monitored) } ]; } }); // ************************************************************************************************ /* this.jsdScript = domplate(Firebug.Rep, { copySource: function(script) { var fn = script.functionObject.getWrappedValue(); return FirebugReps.Func.copySource(fn); }, monitor: function(fn, script, monitored) { fn = script.functionObject.getWrappedValue(); return FirebugReps.Func.monitor(fn, script, monitored); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "jsdScript", inspectable: false, supportsObject: function(object, type) { return object instanceof jsdIScript; }, inspectObject: function(script, context) { var sourceLink = getSourceLinkForScript(script, context); if (sourceLink) Firebug.chrome.select(sourceLink); }, getRealObject: function(script, context) { return script; }, getTooltip: function(script) { return $STRF("jsdIScript", [script.tag]); }, getTitle: function(script, context) { var fn = script.functionObject.getWrappedValue(); return FirebugReps.Func.getTitle(fn, context); }, getContextMenuItems: function(script, target, context) { var fn = script.functionObject.getWrappedValue(); var scriptInfo = getSourceFileAndLineByScript(context, script); var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false; var name = getFunctionName(script, context); return [ {label: "CopySource", command: bindFixed(this.copySource, this, script) }, "-", {label: $STRF("ShowCallsInConsole", [name]), nol10n: true, type: "checkbox", checked: monitored, command: bindFixed(this.monitor, this, fn, script, monitored) } ]; } }); /**/ //************************************************************************************************ this.Obj = domplate(Firebug.Rep, { tag: OBJECTLINK( SPAN({"class": "objectTitle"}, "$object|getTitle "), SPAN({"class": "objectProps"}, SPAN({"class": "objectLeftBrace", role: "presentation"}, "{"), FOR("prop", "$object|propIterator", SPAN({"class": "objectPropName", role: "presentation"}, "$prop.name"), SPAN({"class": "objectEqual", role: "presentation"}, "$prop.equal"), TAG("$prop.tag", {object: "$prop.object"}), SPAN({"class": "objectComma", role: "presentation"}, "$prop.delim") ), SPAN({"class": "objectRightBrace"}, "}") ) ), propNumberTag: SPAN({"class": "objectProp-number"}, "$object"), propStringTag: SPAN({"class": "objectProp-string"}, ""$object""), propObjectTag: SPAN({"class": "objectProp-object"}, "$object"), propIterator: function (object) { ///Firebug.ObjectShortIteratorMax; var maxLength = 55; // default max length for long representation if (!object) return []; var props = []; var length = 0; var numProperties = 0; var numPropertiesShown = 0; var maxLengthReached = false; var lib = this; var propRepsMap = { "boolean": this.propNumberTag, "number": this.propNumberTag, "string": this.propStringTag, "object": this.propObjectTag }; try { var title = Firebug.Rep.getTitle(object); length += title.length; for (var name in object) { var value; try { value = object[name]; } catch (exc) { continue; } var type = typeof(value); if (type == "boolean" || type == "number" || (type == "string" && value) || (type == "object" && value && value.toString)) { var tag = propRepsMap[type]; var value = (type == "object") ? Firebug.getRep(value).getTitle(value) : value + ""; length += name.length + value.length + 4; if (length <= maxLength) { props.push({ tag: tag, name: name, object: value, equal: "=", delim: ", " }); numPropertiesShown++; } else maxLengthReached = true; } numProperties++; if (maxLengthReached && numProperties > numPropertiesShown) break; } if (numProperties > numPropertiesShown) { props.push({ object: "...", //xxxHonza localization tag: FirebugReps.Caption.tag, name: "", equal:"", delim:"" }); } else if (props.length > 0) { props[props.length-1].delim = ''; } } catch (exc) { // Sometimes we get exceptions when trying to read from certain objects, like // StorageList, but don't let that gum up the works // XXXjjb also History.previous fails because object is a web-page object which does not have // permission to read the history } return props; }, fb_1_6_propIterator: function (object, max) { max = max || 3; if (!object) return []; var props = []; var len = 0, count = 0; try { for (var name in object) { var value; try { value = object[name]; } catch (exc) { continue; } var t = typeof(value); if (t == "boolean" || t == "number" || (t == "string" && value) || (t == "object" && value && value.toString)) { var rep = Firebug.getRep(value); var tag = rep.shortTag || rep.tag; if (t == "object") { value = rep.getTitle(value); tag = rep.titleTag; } count++; if (count <= max) props.push({tag: tag, name: name, object: value, equal: "=", delim: ", "}); else break; } } if (count > max) { props[Math.max(1,max-1)] = { object: "more...", //xxxHonza localization tag: FirebugReps.Caption.tag, name: "", equal:"", delim:"" }; } else if (props.length > 0) { props[props.length-1].delim = ''; } } catch (exc) { // Sometimes we get exceptions when trying to read from certain objects, like // StorageList, but don't let that gum up the works // XXXjjb also History.previous fails because object is a web-page object which does not have // permission to read the history } return props; }, /* propIterator: function (object) { if (!object) return []; var props = []; var len = 0; try { for (var name in object) { var val; try { val = object[name]; } catch (exc) { continue; } var t = typeof val; if (t == "boolean" || t == "number" || (t == "string" && val) || (t == "object" && !isFunction(val) && val && val.toString)) { var title = (t == "object") ? Firebug.getRep(val).getTitle(val) : val+""; len += name.length + title.length + 1; if (len < 50) props.push({name: name, value: title}); else break; } } } catch (exc) { // Sometimes we get exceptions when trying to read from certain objects, like // StorageList, but don't let that gum up the works // XXXjjb also History.previous fails because object is a web-page object which does not have // permission to read the history } return props; }, /**/ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "object", supportsObject: function(object, type) { return true; } }); // ************************************************************************************************ this.Arr = domplate(Firebug.Rep, { tag: OBJECTBOX({_repObject: "$object"}, SPAN({"class": "arrayLeftBracket", role : "presentation"}, "["), FOR("item", "$object|arrayIterator", TAG("$item.tag", {object: "$item.object"}), SPAN({"class": "arrayComma", role : "presentation"}, "$item.delim") ), SPAN({"class": "arrayRightBracket", role : "presentation"}, "]") ), shortTag: OBJECTBOX({_repObject: "$object"}, SPAN({"class": "arrayLeftBracket", role : "presentation"}, "["), FOR("item", "$object|shortArrayIterator", TAG("$item.tag", {object: "$item.object"}), SPAN({"class": "arrayComma", role : "presentation"}, "$item.delim") ), // TODO: xxxpedro - confirm this on Firebug //FOR("prop", "$object|shortPropIterator", // " $prop.name=", // SPAN({"class": "objectPropValue"}, "$prop.value|cropString") //), SPAN({"class": "arrayRightBracket"}, "]") ), arrayIterator: function(array) { var items = []; for (var i = 0; i < array.length; ++i) { var value = array[i]; var rep = Firebug.getRep(value); var tag = rep.shortTag ? rep.shortTag : rep.tag; var delim = (i == array.length-1 ? "" : ", "); items.push({object: value, tag: tag, delim: delim}); } return items; }, shortArrayIterator: function(array) { var items = []; for (var i = 0; i < array.length && i < 3; ++i) { var value = array[i]; var rep = Firebug.getRep(value); var tag = rep.shortTag ? rep.shortTag : rep.tag; var delim = (i == array.length-1 ? "" : ", "); items.push({object: value, tag: tag, delim: delim}); } if (array.length > 3) items.push({object: (array.length-3) + " more...", tag: FirebugReps.Caption.tag, delim: ""}); return items; }, shortPropIterator: this.Obj.propIterator, getItemIndex: function(child) { var arrayIndex = 0; for (child = child.previousSibling; child; child = child.previousSibling) { if (child.repObject) ++arrayIndex; } return arrayIndex; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "array", supportsObject: function(object) { return this.isArray(object); }, // http://code.google.com/p/fbug/issues/detail?id=874 // BEGIN Yahoo BSD Source (modified here) YAHOO.lang.isArray, YUI 2.2.2 June 2007 isArray: function(obj) { try { if (!obj) return false; else if (isIE && !isFunction(obj) && typeof obj == "object" && isFinite(obj.length) && obj.nodeType != 8) return true; else if (isFinite(obj.length) && isFunction(obj.splice)) return true; else if (isFinite(obj.length) && isFunction(obj.callee)) // arguments return true; else if (instanceOf(obj, "HTMLCollection")) return true; else if (instanceOf(obj, "NodeList")) return true; else return false; } catch(exc) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("isArray FAILS:", exc); /* Something weird: without the try/catch, OOM, with no exception?? */ FBTrace.sysout("isArray Fails on obj", obj); } } return false; }, // END Yahoo BSD SOURCE See license below. getTitle: function(object, context) { return "[" + object.length + "]"; } }); // ************************************************************************************************ this.Property = domplate(Firebug.Rep, { supportsObject: function(object) { return object instanceof Property; }, getRealObject: function(prop, context) { return prop.object[prop.name]; }, getTitle: function(prop, context) { return prop.name; } }); // ************************************************************************************************ this.NetFile = domplate(this.Obj, { supportsObject: function(object) { return object instanceof Firebug.NetFile; }, browseObject: function(file, context) { openNewTab(file.href); return true; }, getRealObject: function(file, context) { return null; } }); // ************************************************************************************************ this.Except = domplate(Firebug.Rep, { tag: OBJECTBOX({_repObject: "$object"}, "$object.message"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "exception", supportsObject: function(object) { return object instanceof ErrorCopy; } }); // ************************************************************************************************ this.Element = domplate(Firebug.Rep, { tag: OBJECTLINK( "<", SPAN({"class": "nodeTag"}, "$object.nodeName|toLowerCase"), FOR("attr", "$object|attrIterator", " $attr.nodeName="", SPAN({"class": "nodeValue"}, "$attr.nodeValue"), """ ), ">" ), shortTag: OBJECTLINK( SPAN({"class": "$object|getVisible"}, SPAN({"class": "selectorTag"}, "$object|getSelectorTag"), SPAN({"class": "selectorId"}, "$object|getSelectorId"), SPAN({"class": "selectorClass"}, "$object|getSelectorClass"), SPAN({"class": "selectorValue"}, "$object|getValue") ) ), getVisible: function(elt) { return isVisible(elt) ? "" : "selectorHidden"; }, getSelectorTag: function(elt) { return elt.nodeName.toLowerCase(); }, getSelectorId: function(elt) { return elt.id ? "#" + elt.id : ""; }, getSelectorClass: function(elt) { return elt.className ? "." + elt.className.split(" ")[0] : ""; }, getValue: function(elt) { // TODO: xxxpedro return ""; var value; if (elt instanceof HTMLImageElement) value = getFileName(elt.src); else if (elt instanceof HTMLAnchorElement) value = getFileName(elt.href); else if (elt instanceof HTMLInputElement) value = elt.value; else if (elt instanceof HTMLFormElement) value = getFileName(elt.action); else if (elt instanceof HTMLScriptElement) value = getFileName(elt.src); return value ? " " + cropString(value, 20) : ""; }, attrIterator: function(elt) { var attrs = []; var idAttr, classAttr; if (elt.attributes) { for (var i = 0; i < elt.attributes.length; ++i) { var attr = elt.attributes[i]; // we must check if the attribute is specified otherwise IE will show them if (!attr.specified || attr.nodeName && attr.nodeName.indexOf("firebug-") != -1) continue; else if (attr.nodeName == "id") idAttr = attr; else if (attr.nodeName == "class") classAttr = attr; else if (attr.nodeName == "style") attrs.push({ nodeName: attr.nodeName, nodeValue: attr.nodeValue || // IE won't recognize the attr.nodeValue of
     
 
 
>>>
lodash-2.4.1/vendor/firebug-lite/skin/xp/tree_open.gif0000755000175000017500000000031212247303154021047 0ustar ovdovdGIF89a ³ôõĶȺ!Created with GIMP! , 2d zh] t\6nh4b@?@X(0 ;lodash-2.4.1/vendor/firebug-lite/skin/xp/firebug.css0000755000175000017500000020150712247303154020546 0ustar ovdovd/*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* Loose */ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* .netInfoResponseHeadersTitle, netInfoResponseHeadersBody { display: none; } /**/ .obscured { left: -999999px !important; } /* IE6 need a separated rule, otherwise it will not recognize it */ .collapsed { display: none; } [collapsed="true"] { display: none; } #fbCSS { padding: 0 !important; } .cssPropDisable { float: left; display: block; width: 2em; cursor: default; } /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* panelBase */ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /************************************************************************************************/ .infoTip { z-index: 2147483647; position: fixed; padding: 2px 3px; border: 1px solid #CBE087; background: LightYellow; font-family: Monaco, monospace; color: #000000; display: none; white-space: nowrap; pointer-events: none; } .infoTip[active="true"] { display: block; } .infoTipLoading { width: 16px; height: 16px; background: url(chrome://firebug/skin/loading_16.gif) no-repeat; } .infoTipImageBox { font-size: 11px; min-width: 100px; text-align: center; } .infoTipCaption { font-size: 11px; font: Monaco, monospace; } .infoTipLoading > .infoTipImage, .infoTipLoading > .infoTipCaption { display: none; } /************************************************************************************************/ h1.groupHeader { padding: 2px 4px; margin: 0 0 4px 0; border-top: 1px solid #CCCCCC; border-bottom: 1px solid #CCCCCC; background: #eee url(group.gif) repeat-x; font-size: 11px; font-weight: bold; _position: relative; } /************************************************************************************************/ .inlineEditor, .fixedWidthEditor { z-index: 2147483647; position: absolute; display: none; } .inlineEditor { margin-left: -6px; margin-top: -3px; /* _margin-left: -7px; _margin-top: -5px; /**/ } .textEditorInner, .fixedWidthEditor { margin: 0 0 0 0 !important; padding: 0; border: none !important; font: inherit; text-decoration: inherit; background-color: #FFFFFF; } .fixedWidthEditor { border-top: 1px solid #888888 !important; border-bottom: 1px solid #888888 !important; } .textEditorInner { position: relative; top: -7px; left: -5px; outline: none; resize: none; /* _border: 1px solid #999 !important; _padding: 1px !important; _filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color="#55404040"); /**/ } .textEditorInner1 { padding-left: 11px; background: url(textEditorBorders.png) repeat-y; _background: url(textEditorBorders.gif) repeat-y; _overflow: hidden; } .textEditorInner2 { position: relative; padding-right: 2px; background: url(textEditorBorders.png) repeat-y 100% 0; _background: url(textEditorBorders.gif) repeat-y 100% 0; _position: fixed; } .textEditorTop1 { background: url(textEditorCorners.png) no-repeat 100% 0; margin-left: 11px; height: 10px; _background: url(textEditorCorners.gif) no-repeat 100% 0; _overflow: hidden; } .textEditorTop2 { position: relative; left: -11px; width: 11px; height: 10px; background: url(textEditorCorners.png) no-repeat; _background: url(textEditorCorners.gif) no-repeat; } .textEditorBottom1 { position: relative; background: url(textEditorCorners.png) no-repeat 100% 100%; margin-left: 11px; height: 12px; _background: url(textEditorCorners.gif) no-repeat 100% 100%; } .textEditorBottom2 { position: relative; left: -11px; width: 11px; height: 12px; background: url(textEditorCorners.png) no-repeat 0 100%; _background: url(textEditorCorners.gif) no-repeat 0 100%; } /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* CSS */ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* See license.txt for terms of usage */ .panelNode-css { overflow-x: hidden; } .cssSheet > .insertBefore { height: 1.5em; } .cssRule { position: relative; margin: 0; padding: 1em 0 0 6px; font-family: Monaco, monospace; color: #000000; } .cssRule:first-child { padding-top: 6px; } .cssElementRuleContainer { position: relative; } .cssHead { padding-right: 150px; } .cssProp { /*padding-left: 2em;*/ } .cssPropName { color: DarkGreen; } .cssPropValue { margin-left: 8px; color: DarkBlue; } .cssOverridden span { text-decoration: line-through; } .cssInheritedRule { } .cssInheritLabel { margin-right: 0.5em; font-weight: bold; } .cssRule .objectLink-sourceLink { top: 0; } .cssProp.editGroup:hover { background: url(disable.png) no-repeat 2px 1px; _background: url(disable.gif) no-repeat 2px 1px; } .cssProp.editGroup.editing { background: none; } .cssProp.disabledStyle { background: url(disableHover.png) no-repeat 2px 1px; _background: url(disableHover.gif) no-repeat 2px 1px; opacity: 1; color: #CCCCCC; } .disabledStyle .cssPropName, .disabledStyle .cssPropValue { color: #CCCCCC; } .cssPropValue.editing + .cssSemi, .inlineExpander + .cssSemi { display: none; } .cssPropValue.editing { white-space: nowrap; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .stylePropName { font-weight: bold; padding: 0 4px 4px 4px; width: 50%; } .stylePropValue { width: 50%; } /* .useA11y .a11yCSSView .focusRow:focus { outline: none; background-color: transparent } .useA11y .a11yCSSView .focusRow:focus .cssSelector, .useA11y .a11yCSSView .focusRow:focus .cssPropName, .useA11y .a11yCSSView .focusRow:focus .cssPropValue, .useA11y .a11yCSSView .computedStyleRow:focus, .useA11y .a11yCSSView .groupHeader:focus { outline: 2px solid #FF9933; outline-offset: -2px; background-color: #FFFFD6; } .useA11y .a11yCSSView .groupHeader:focus { outline-offset: -2px; } /**/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* Net */ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* See license.txt for terms of usage */ .panelNode-net { overflow-x: hidden; } .netTable { width: 100%; } /************************************************************************************************/ .hideCategory-undefined .category-undefined, .hideCategory-html .category-html, .hideCategory-css .category-css, .hideCategory-js .category-js, .hideCategory-image .category-image, .hideCategory-xhr .category-xhr, .hideCategory-flash .category-flash, .hideCategory-txt .category-txt, .hideCategory-bin .category-bin { display: none; } /************************************************************************************************/ .netHeadRow { background: url(chrome://firebug/skin/group.gif) repeat-x #FFFFFF; } .netHeadCol { border-bottom: 1px solid #CCCCCC; padding: 2px 4px 2px 18px; font-weight: bold; } .netHeadLabel { white-space: nowrap; overflow: hidden; } /************************************************************************************************/ /* Header for Net panel table */ .netHeaderRow { height: 16px; } .netHeaderCell { cursor: pointer; -moz-user-select: none; border-bottom: 1px solid #9C9C9C; padding: 0 !important; font-weight: bold; background: #BBBBBB url(chrome://firebug/skin/tableHeader.gif) repeat-x; white-space: nowrap; } .netHeaderRow > .netHeaderCell:first-child > .netHeaderCellBox { padding: 2px 14px 2px 18px; } .netHeaderCellBox { padding: 2px 14px 2px 10px; border-left: 1px solid #D9D9D9; border-right: 1px solid #9C9C9C; } .netHeaderCell:hover:active { background: #959595 url(chrome://firebug/skin/tableHeaderActive.gif) repeat-x; } .netHeaderSorted { background: #7D93B2 url(chrome://firebug/skin/tableHeaderSorted.gif) repeat-x; } .netHeaderSorted > .netHeaderCellBox { border-right-color: #6B7C93; background: url(chrome://firebug/skin/arrowDown.png) no-repeat right; } .netHeaderSorted.sortedAscending > .netHeaderCellBox { background-image: url(chrome://firebug/skin/arrowUp.png); } .netHeaderSorted:hover:active { background: #536B90 url(chrome://firebug/skin/tableHeaderSortedActive.gif) repeat-x; } /************************************************************************************************/ /* Breakpoints */ .panelNode-net .netRowHeader { display: block; } .netRowHeader { cursor: pointer; display: none; height: 15px; margin-right: 0 !important; } /* Display brekpoint disc */ .netRow .netRowHeader { background-position: 5px 1px; } .netRow[breakpoint="true"] .netRowHeader { background-image: url(chrome://firebug/skin/breakpoint.png); } .netRow[breakpoint="true"][disabledBreakpoint="true"] .netRowHeader { background-image: url(chrome://firebug/skin/breakpointDisabled.png); } .netRow.category-xhr:hover .netRowHeader { background-color: #F6F6F6; } #netBreakpointBar { max-width: 38px; } #netHrefCol > .netHeaderCellBox { border-left: 0px; } .netRow .netRowHeader { width: 3px; } .netInfoRow .netRowHeader { display: table-cell; } /************************************************************************************************/ /* Column visibility */ .netTable[hiddenCols~=netHrefCol] TD[id="netHrefCol"], .netTable[hiddenCols~=netHrefCol] TD.netHrefCol, .netTable[hiddenCols~=netStatusCol] TD[id="netStatusCol"], .netTable[hiddenCols~=netStatusCol] TD.netStatusCol, .netTable[hiddenCols~=netDomainCol] TD[id="netDomainCol"], .netTable[hiddenCols~=netDomainCol] TD.netDomainCol, .netTable[hiddenCols~=netSizeCol] TD[id="netSizeCol"], .netTable[hiddenCols~=netSizeCol] TD.netSizeCol, .netTable[hiddenCols~=netTimeCol] TD[id="netTimeCol"], .netTable[hiddenCols~=netTimeCol] TD.netTimeCol { display: none; } /************************************************************************************************/ .netRow { background: LightYellow; } .netRow.loaded { background: #FFFFFF; } .netRow.loaded:hover { background: #EFEFEF; } .netCol { padding: 0; vertical-align: top; border-bottom: 1px solid #EFEFEF; white-space: nowrap; height: 17px; } .netLabel { width: 100%; } .netStatusCol { padding-left: 10px; color: rgb(128, 128, 128); } .responseError > .netStatusCol { color: red; } .netDomainCol { padding-left: 5px; } .netSizeCol { text-align: right; padding-right: 10px; } .netHrefLabel { -moz-box-sizing: padding-box; overflow: hidden; z-index: 10; position: absolute; padding-left: 18px; padding-top: 1px; max-width: 15%; font-weight: bold; } .netFullHrefLabel { display: none; -moz-user-select: none; padding-right: 10px; padding-bottom: 3px; max-width: 100%; background: #FFFFFF; z-index: 200; } .netHrefCol:hover > .netFullHrefLabel { display: block; } .netRow.loaded:hover .netCol > .netFullHrefLabel { background-color: #EFEFEF; } .useA11y .a11yShowFullLabel { display: block; background-image: none !important; border: 1px solid #CBE087; background-color: LightYellow; font-family: Monaco, monospace; color: #000000; font-size: 10px; z-index: 2147483647; } .netSizeLabel { padding-left: 6px; } .netStatusLabel, .netDomainLabel, .netSizeLabel, .netBar { padding: 1px 0 2px 0 !important; } .responseError { color: red; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .hasHeaders .netHrefLabel:hover { cursor: pointer; color: blue; text-decoration: underline; } /************************************************************************************************/ .netLoadingIcon { position: absolute; border: 0; margin-left: 14px; width: 16px; height: 16px; background: transparent no-repeat 0 0; background-image: url(chrome://firebug/skin/loading_16.gif); display:inline-block; } .loaded .netLoadingIcon { display: none; } /************************************************************************************************/ .netBar, .netSummaryBar { position: relative; border-right: 50px solid transparent; } .netResolvingBar { position: absolute; left: 0; top: 0; bottom: 0; background: #FFFFFF url(chrome://firebug/skin/netBarResolving.gif) repeat-x; z-index:60; } .netConnectingBar { position: absolute; left: 0; top: 0; bottom: 0; background: #FFFFFF url(chrome://firebug/skin/netBarConnecting.gif) repeat-x; z-index:50; } .netBlockingBar { position: absolute; left: 0; top: 0; bottom: 0; background: #FFFFFF url(chrome://firebug/skin/netBarWaiting.gif) repeat-x; z-index:40; } .netSendingBar { position: absolute; left: 0; top: 0; bottom: 0; background: #FFFFFF url(chrome://firebug/skin/netBarSending.gif) repeat-x; z-index:30; } .netWaitingBar { position: absolute; left: 0; top: 0; bottom: 0; background: #FFFFFF url(chrome://firebug/skin/netBarResponded.gif) repeat-x; z-index:20; min-width: 1px; } .netReceivingBar { position: absolute; left: 0; top: 0; bottom: 0; background: #38D63B url(chrome://firebug/skin/netBarLoading.gif) repeat-x; z-index:10; } .netWindowLoadBar, .netContentLoadBar { position: absolute; left: 0; top: 0; bottom: 0; width: 1px; background-color: red; z-index: 70; opacity: 0.5; display: none; margin-bottom:-1px; } .netContentLoadBar { background-color: Blue; } .netTimeLabel { -moz-box-sizing: padding-box; position: absolute; top: 1px; left: 100%; padding-left: 6px; color: #444444; min-width: 16px; } /* * Timing info tip is reusing net timeline styles to display the same * colors for individual request phases. Notice that the info tip must * respect also loaded and fromCache styles that also modify the * actual color. These are used both on the same element in case * of the tooltip. */ .loaded .netReceivingBar, .loaded.netReceivingBar { background: #B6B6B6 url(chrome://firebug/skin/netBarLoaded.gif) repeat-x; border-color: #B6B6B6; } .fromCache .netReceivingBar, .fromCache.netReceivingBar { background: #D6D6D6 url(chrome://firebug/skin/netBarCached.gif) repeat-x; border-color: #D6D6D6; } .netSummaryRow .netTimeLabel, .loaded .netTimeLabel { background: transparent; } /************************************************************************************************/ /* Time Info tip */ .timeInfoTip { width: 150px; height: 40px } .timeInfoTipBar, .timeInfoTipEventBar { position: relative; display: block; margin: 0; opacity: 1; height: 15px; width: 4px; } .timeInfoTipEventBar { width: 1px !important; } .timeInfoTipCell.startTime { padding-right: 8px; } .timeInfoTipCell.elapsedTime { text-align: right; padding-right: 8px; } /************************************************************************************************/ /* Size Info tip */ .sizeInfoLabelCol { font-weight: bold; padding-right: 10px; font-family: Lucida Grande, Tahoma, sans-serif; font-size: 11px; } .sizeInfoSizeCol { font-weight: bold; } .sizeInfoDetailCol { color: gray; text-align: right; } .sizeInfoDescCol { font-style: italic; } /************************************************************************************************/ /* Summary */ .netSummaryRow .netReceivingBar { background: #BBBBBB; border: none; } .netSummaryLabel { color: #222222; } .netSummaryRow { background: #BBBBBB !important; font-weight: bold; } .netSummaryRow .netBar { border-right-color: #BBBBBB; } .netSummaryRow > .netCol { border-top: 1px solid #999999; border-bottom: 2px solid; -moz-border-bottom-colors: #EFEFEF #999999; padding-top: 1px; padding-bottom: 2px; } .netSummaryRow > .netHrefCol:hover { background: transparent !important; } .netCountLabel { padding-left: 18px; } .netTotalSizeCol { text-align: right; padding-right: 10px; } .netTotalTimeCol { text-align: right; } .netCacheSizeLabel { position: absolute; z-index: 1000; left: 0; top: 0; } /************************************************************************************************/ .netLimitRow { background: rgb(255, 255, 225) !important; font-weight:normal; color: black; font-weight:normal; } .netLimitLabel { padding-left: 18px; } .netLimitRow > .netCol { border-bottom: 2px solid; -moz-border-bottom-colors: #EFEFEF #999999; vertical-align: middle !important; padding-top: 2px; padding-bottom: 2px; } .netLimitButton { font-size: 11px; padding-top: 1px; padding-bottom: 1px; } /************************************************************************************************/ .netInfoCol { border-top: 1px solid #EEEEEE; background: url(chrome://firebug/skin/group.gif) repeat-x #FFFFFF; } .netInfoBody { margin: 10px 0 4px 10px; } .netInfoTabs { position: relative; padding-left: 17px; } .netInfoTab { position: relative; top: -3px; margin-top: 10px; padding: 4px 6px; border: 1px solid transparent; border-bottom: none; _border: none; font-weight: bold; color: #565656; cursor: pointer; } /*.netInfoTab:hover { cursor: pointer; }*/ /* replaced by .netInfoTabSelected for IE6 support .netInfoTab[selected="true"] { cursor: default !important; border: 1px solid #D7D7D7 !important; border-bottom: none !important; -moz-border-radius: 4px 4px 0 0; background-color: #FFFFFF; } /**/ .netInfoTabSelected { cursor: default !important; border: 1px solid #D7D7D7 !important; border-bottom: none !important; -moz-border-radius: 4px 4px 0 0; -webkit-border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0; background-color: #FFFFFF; } .logRow-netInfo.error .netInfoTitle { color: red; } .logRow-netInfo.loading .netInfoResponseText { font-style: italic; color: #888888; } .loading .netInfoResponseHeadersTitle { display: none; } .netInfoResponseSizeLimit { font-family: Lucida Grande, Tahoma, sans-serif; padding-top: 10px; font-size: 11px; } .netInfoText { display: none; margin: 0; border: 1px solid #D7D7D7; border-right: none; padding: 8px; background-color: #FFFFFF; font-family: Monaco, monospace; white-space: pre-wrap; /*overflow-x: auto; HTML is damaged in case of big (2-3MB) responses */ } /* replaced by .netInfoTextSelected for IE6 support .netInfoText[selected="true"] { display: block; } /**/ .netInfoTextSelected { display: block; } .netInfoParamName { padding-right: 10px; font-family: Lucida Grande, Tahoma, sans-serif; font-weight: bold; vertical-align: top; text-align: right; white-space: nowrap; } .netInfoPostText .netInfoParamName { width: 1px; /* Google Chrome need this otherwise the first column of the post variables table will be larger than expected */ } .netInfoParamValue { width: 100%; } .netInfoHeadersText, .netInfoPostText, .netInfoPutText { padding-top: 0; } .netInfoHeadersGroup, .netInfoPostParams, .netInfoPostSource { margin-bottom: 4px; border-bottom: 1px solid #D7D7D7; padding-top: 8px; padding-bottom: 2px; font-family: Lucida Grande, Tahoma, sans-serif; font-weight: bold; color: #565656; } .netInfoPostParamsTable, .netInfoPostPartsTable, .netInfoPostJSONTable, .netInfoPostXMLTable, .netInfoPostSourceTable { margin-bottom: 10px; width: 100%; } .netInfoPostContentType { color: #bdbdbd; padding-left: 50px; font-weight: normal; } .netInfoHtmlPreview { border: 0; width: 100%; height:100%; } /************************************************************************************************/ /* Request & Response Headers */ .netHeadersViewSource { color: #bdbdbd; margin-left: 200px; font-weight: normal; } .netHeadersViewSource:hover { color: blue; cursor: pointer; } /************************************************************************************************/ .netActivationRow, .netPageSeparatorRow { background: rgb(229, 229, 229) !important; font-weight: normal; color: black; } .netActivationLabel { background: url(chrome://firebug/skin/infoIcon.png) no-repeat 3px 2px; padding-left: 22px; } /************************************************************************************************/ .netPageSeparatorRow { height: 5px !important; } .netPageSeparatorLabel { padding-left: 22px; height: 5px !important; } .netPageRow { background-color: rgb(255, 255, 255); } .netPageRow:hover { background: #EFEFEF; } .netPageLabel { padding: 1px 0 2px 18px !important; font-weight: bold; } /************************************************************************************************/ .netActivationRow > .netCol { border-bottom: 2px solid; -moz-border-bottom-colors: #EFEFEF #999999; padding-top: 2px; padding-bottom: 3px; } /* .useA11y .panelNode-net .a11yFocus:focus, .useA11y .panelNode-net .focusRow:focus { outline-offset: -2px; background-color: #FFFFD6 !important; } .useA11y .panelNode-net .netHeaderCell:focus, .useA11y .panelNode-net :focus .netHeaderCell, .useA11y .panelNode-net :focus .netReceivingBar, .useA11y .netSummaryRow :focus .netBar, .useA11y .netSummaryRow:focus .netBar { background-color: #FFFFD6; background-image: none; border-color: #FFFFD6; } /**/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* Windows */ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /************************************************************************************************/ /* Twisties */ .twisty, .logRow-errorMessage > .hasTwisty > .errorTitle, .logRow-log > .objectBox-array.hasTwisty, .logRow-spy .spyHead .spyTitle, .logGroup > .logRow, .memberRow.hasChildren > .memberLabelCell > .memberLabel, .hasHeaders .netHrefLabel, .netPageRow > .netCol > .netPageTitle { background-image: url(tree_open.gif); background-repeat: no-repeat; background-position: 2px 2px; min-height: 12px; } .logRow-errorMessage > .hasTwisty.opened > .errorTitle, .logRow-log > .objectBox-array.hasTwisty.opened, .logRow-spy.opened .spyHead .spyTitle, .logGroup.opened > .logRow, .memberRow.hasChildren.opened > .memberLabelCell > .memberLabel, .nodeBox.highlightOpen > .nodeLabel > .twisty, .nodeBox.open > .nodeLabel > .twisty, .netRow.opened > .netCol > .netHrefLabel, .netPageRow.opened > .netCol > .netPageTitle { background-image: url(tree_close.gif); } .twisty { background-position: 4px 4px; } /************************************************************************************************/ /* Twisties IE6 */ /* IE6 has problems with > operator, and multiple classes */ * html .logRow-spy .spyHead .spyTitle, * html .logGroup .logGroupLabel, * html .hasChildren .memberLabelCell .memberLabel, * html .hasHeaders .netHrefLabel { background-image: url(tree_open.gif); background-repeat: no-repeat; background-position: 2px 2px; } * html .opened .spyHead .spyTitle, * html .opened .logGroupLabel, * html .opened .memberLabelCell .memberLabel { background-image: url(tree_close.gif); background-repeat: no-repeat; background-position: 2px 2px; } /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* Console */ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* See license.txt for terms of usage */ .panelNode-console { overflow-x: hidden; } .objectLink { text-decoration: none; } .objectLink:hover { cursor: pointer; text-decoration: underline; } .logRow { position: relative; margin: 0; border-bottom: 1px solid #D7D7D7; padding: 2px 4px 1px 6px; background-color: #FFFFFF; overflow: hidden !important; /* IE need this to avoid disappearing bug with collapsed logs */ } .useA11y .logRow:focus { border-bottom: 1px solid #000000 !important; outline: none !important; background-color: #FFFFAD !important; } .useA11y .logRow:focus a.objectLink-sourceLink { background-color: #FFFFAD; } .useA11y .a11yFocus:focus, .useA11y .objectBox:focus { outline: 2px solid #FF9933; background-color: #FFFFAD; } .useA11y .objectBox-null:focus, .useA11y .objectBox-undefined:focus{ background-color: #888888 !important; } .useA11y .logGroup.opened > .logRow { border-bottom: 1px solid #ffffff; } .logGroup { background: url(group.gif) repeat-x #FFFFFF; padding: 0 !important; border: none !important; } .logGroupBody { display: none; margin-left: 16px; border-left: 1px solid #D7D7D7; border-top: 1px solid #D7D7D7; background: #FFFFFF; } .logGroup > .logRow { background-color: transparent !important; font-weight: bold; } .logGroup.opened > .logRow { border-bottom: none; } .logGroup.opened > .logGroupBody { display: block; } /*****************************************************************************************/ .logRow-command > .objectBox-text { font-family: Monaco, monospace; color: #0000FF; white-space: pre-wrap; } .logRow-info, .logRow-warn, .logRow-error, .logRow-assert, .logRow-warningMessage, .logRow-errorMessage { padding-left: 22px; background-repeat: no-repeat; background-position: 4px 2px; } .logRow-assert, .logRow-warningMessage, .logRow-errorMessage { padding-top: 0; padding-bottom: 0; } .logRow-info, .logRow-info .objectLink-sourceLink { background-color: #FFFFFF; } .logRow-warn, .logRow-warningMessage, .logRow-warn .objectLink-sourceLink, .logRow-warningMessage .objectLink-sourceLink { background-color: cyan; } .logRow-error, .logRow-assert, .logRow-errorMessage, .logRow-error .objectLink-sourceLink, .logRow-errorMessage .objectLink-sourceLink { background-color: LightYellow; } .logRow-error, .logRow-assert, .logRow-errorMessage { color: #FF0000; } .logRow-info { /*background-image: url(chrome://firebug/skin/infoIcon.png);*/ } .logRow-warn, .logRow-warningMessage { /*background-image: url(chrome://firebug/skin/warningIcon.png);*/ } .logRow-error, .logRow-assert, .logRow-errorMessage { /*background-image: url(chrome://firebug/skin/errorIcon.png);*/ } /*****************************************************************************************/ .objectBox-string, .objectBox-text, .objectBox-number, .objectLink-element, .objectLink-textNode, .objectLink-function, .objectBox-stackTrace, .objectLink-profile { font-family: Monaco, monospace; } .objectBox-string, .objectBox-text, .objectLink-textNode { white-space: pre-wrap; } .objectBox-number, .objectLink-styleRule, .objectLink-element, .objectLink-textNode { color: #000088; } .objectBox-string { color: #FF0000; } .objectLink-function, .objectBox-stackTrace, .objectLink-profile { color: DarkGreen; } .objectBox-null, .objectBox-undefined { padding: 0 2px; border: 1px solid #666666; background-color: #888888; color: #FFFFFF; } .objectBox-exception { padding: 0 2px 0 18px; /*background: url(chrome://firebug/skin/errorIcon-sm.png) no-repeat 0 0;*/ color: red; } .objectLink-sourceLink { position: absolute; right: 4px; top: 2px; padding-left: 8px; font-family: Lucida Grande, sans-serif; font-weight: bold; color: #0000FF; } /************************************************************************************************/ .errorTitle { margin-top: 0px; margin-bottom: 1px; padding-top: 2px; padding-bottom: 2px; } .errorTrace { margin-left: 17px; } .errorSourceBox { margin: 2px 0; } .errorSource-none { display: none; } .errorSource-syntax > .errorBreak { visibility: hidden; } .errorSource { cursor: pointer; font-family: Monaco, monospace; color: DarkGreen; } .errorSource:hover { text-decoration: underline; } .errorBreak { cursor: pointer; display: none; margin: 0 6px 0 0; width: 13px; height: 14px; vertical-align: bottom; /*background: url(chrome://firebug/skin/breakpoint.png) no-repeat;*/ opacity: 0.1; } .hasBreakSwitch .errorBreak { display: inline; } .breakForError .errorBreak { opacity: 1; } .assertDescription { margin: 0; } /************************************************************************************************/ .logRow-profile > .logRow > .objectBox-text { font-family: Lucida Grande, Tahoma, sans-serif; color: #000000; } .logRow-profile > .logRow > .objectBox-text:last-child { color: #555555; font-style: italic; } .logRow-profile.opened > .logRow { padding-bottom: 4px; } .profilerRunning > .logRow { /*background: transparent url(chrome://firebug/skin/loading_16.gif) no-repeat 2px 0 !important;*/ padding-left: 22px !important; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .profileSizer { width:100%; overflow-x:auto; overflow-y: scroll; } .profileTable { border-bottom: 1px solid #D7D7D7; padding: 0 0 4px 0; } .profileTable tr[odd="1"] { background-color: #F5F5F5; vertical-align:middle; } .profileTable a { vertical-align:middle; } .profileTable td { padding: 1px 4px 0 4px; } .headerCell { cursor: pointer; -moz-user-select: none; border-bottom: 1px solid #9C9C9C; padding: 0 !important; font-weight: bold; /*background: #BBBBBB url(chrome://firebug/skin/tableHeader.gif) repeat-x;*/ } .headerCellBox { padding: 2px 4px; border-left: 1px solid #D9D9D9; border-right: 1px solid #9C9C9C; } .headerCell:hover:active { /*background: #959595 url(chrome://firebug/skin/tableHeaderActive.gif) repeat-x;*/ } .headerSorted { /*background: #7D93B2 url(chrome://firebug/skin/tableHeaderSorted.gif) repeat-x;*/ } .headerSorted > .headerCellBox { border-right-color: #6B7C93; /*background: url(chrome://firebug/skin/arrowDown.png) no-repeat right;*/ } .headerSorted.sortedAscending > .headerCellBox { /*background-image: url(chrome://firebug/skin/arrowUp.png);*/ } .headerSorted:hover:active { /*background: #536B90 url(chrome://firebug/skin/tableHeaderSortedActive.gif) repeat-x;*/ } .linkCell { text-align: right; } .linkCell > .objectLink-sourceLink { position: static; } /*****************************************************************************************/ .logRow-stackTrace { padding-top: 0; background: #f8f8f8; } .logRow-stackTrace > .objectBox-stackFrame { position: relative; padding-top: 2px; } /************************************************************************************************/ .objectLink-object { font-family: Lucida Grande, sans-serif; font-weight: bold; color: DarkGreen; white-space: pre-wrap; } /* xxxpedro reps object representation .................................... */ .objectProp-object { color: DarkGreen; } .objectProps { color: #000; font-weight: normal; } .objectPropName { /*font-style: italic;*/ color: #777; } /* .objectProps .objectProp-string, .objectProps .objectProp-number, .objectProps .objectProp-object { font-style: italic; } /**/ .objectProps .objectProp-string { /*font-family: Monaco, monospace;*/ color: #f55; } .objectProps .objectProp-number { /*font-family: Monaco, monospace;*/ color: #55a; } .objectProps .objectProp-object { /*font-family: Lucida Grande,sans-serif;*/ color: #585; } /* xxxpedro reps object representation .................................... */ /************************************************************************************************/ .selectorTag, .selectorId, .selectorClass { font-family: Monaco, monospace; font-weight: normal; } .selectorTag { color: #0000FF; } .selectorId { color: DarkBlue; } .selectorClass { color: red; } .selectorHidden > .selectorTag { color: #5F82D9; } .selectorHidden > .selectorId { color: #888888; } .selectorHidden > .selectorClass { color: #D86060; } .selectorValue { font-family: Lucida Grande, sans-serif; font-style: italic; color: #555555; } /*****************************************************************************************/ .panelNode.searching .logRow { display: none; } .logRow.matched { display: block !important; } .logRow.matching { position: absolute; left: -1000px; top: -1000px; max-width: 0; max-height: 0; overflow: hidden; } /*****************************************************************************************/ .objectLeftBrace, .objectRightBrace, .objectEqual, .objectComma, .arrayLeftBracket, .arrayRightBracket, .arrayComma { font-family: Monaco, monospace; } .objectLeftBrace, .objectRightBrace, .arrayLeftBracket, .arrayRightBracket { font-weight: bold; } .objectLeftBrace, .arrayLeftBracket { margin-right: 4px; } .objectRightBrace, .arrayRightBracket { margin-left: 4px; } /*****************************************************************************************/ .logRow-dir { padding: 0; } /************************************************************************************************/ /* .logRow-errorMessage > .hasTwisty > .errorTitle, .logRow-spy .spyHead .spyTitle, .logGroup > .logRow */ .logRow-errorMessage .hasTwisty .errorTitle, .logRow-spy .spyHead .spyTitle, .logGroup .logRow { cursor: pointer; padding-left: 18px; background-repeat: no-repeat; background-position: 3px 3px; } .logRow-errorMessage > .hasTwisty > .errorTitle { background-position: 2px 3px; } .logRow-errorMessage > .hasTwisty > .errorTitle:hover, .logRow-spy .spyHead .spyTitle:hover, .logGroup > .logRow:hover { text-decoration: underline; } /*****************************************************************************************/ .logRow-spy { padding: 0 !important; } .logRow-spy, .logRow-spy .objectLink-sourceLink { background: url(group.gif) repeat-x #FFFFFF; padding-right: 4px; right: 0; } .logRow-spy.opened { padding-bottom: 4px; border-bottom: none; } .spyTitle { color: #000000; font-weight: bold; -moz-box-sizing: padding-box; overflow: hidden; z-index: 100; padding-left: 18px; } .spyCol { padding: 0; white-space: nowrap; height: 16px; } .spyTitleCol:hover > .objectLink-sourceLink, .spyTitleCol:hover > .spyTime, .spyTitleCol:hover > .spyStatus, .spyTitleCol:hover > .spyTitle { display: none; } .spyFullTitle { display: none; -moz-user-select: none; max-width: 100%; background-color: Transparent; } .spyTitleCol:hover > .spyFullTitle { display: block; } .spyStatus { padding-left: 10px; color: rgb(128, 128, 128); } .spyTime { margin-left:4px; margin-right:4px; color: rgb(128, 128, 128); } .spyIcon { margin-right: 4px; margin-left: 4px; width: 16px; height: 16px; vertical-align: middle; background: transparent no-repeat 0 0; display: none; } .loading .spyHead .spyRow .spyIcon { background-image: url(loading_16.gif); display: block; } .logRow-spy.loaded:not(.error) .spyHead .spyRow .spyIcon { width: 0; margin: 0; } .logRow-spy.error .spyHead .spyRow .spyIcon { background-image: url(errorIcon-sm.png); display: block; background-position: 2px 2px; } .logRow-spy .spyHead .netInfoBody { display: none; } .logRow-spy.opened .spyHead .netInfoBody { margin-top: 10px; display: block; } .logRow-spy.error .spyTitle, .logRow-spy.error .spyStatus, .logRow-spy.error .spyTime { color: red; } .logRow-spy.loading .spyResponseText { font-style: italic; color: #888888; } /************************************************************************************************/ .caption { font-family: Lucida Grande, Tahoma, sans-serif; font-weight: bold; color: #444444; } .warning { padding: 10px; font-family: Lucida Grande, Tahoma, sans-serif; font-weight: bold; color: #888888; } /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* DOM */ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* See license.txt for terms of usage */ .panelNode-dom { overflow-x: hidden !important; } .domTable { font-size: 1em; width: 100%; table-layout: fixed; background: #fff; } .domTableIE { width: auto; } .memberLabelCell { padding: 2px 0 2px 0; vertical-align: top; } .memberValueCell { padding: 1px 0 1px 5px; display: block; overflow: hidden; } .memberLabel { display: block; cursor: default; -moz-user-select: none; overflow: hidden; /*position: absolute;*/ padding-left: 18px; /*max-width: 30%;*/ /*white-space: nowrap;*/ background-color: #FFFFFF; text-decoration: none; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .memberRow.hasChildren .memberLabelCell .memberLabel:hover { cursor: pointer; color: blue; text-decoration: underline; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .userLabel { color: #000000; font-weight: bold; } .userClassLabel { color: #E90000; font-weight: bold; } .userFunctionLabel { color: #025E2A; font-weight: bold; } .domLabel { color: #000000; } .domFunctionLabel { color: #025E2A; } .ordinalLabel { color: SlateBlue; font-weight: bold; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .scopesRow { padding: 2px 18px; background-color: LightYellow; border-bottom: 5px solid #BEBEBE; color: #666666; } .scopesLabel { background-color: LightYellow; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .watchEditCell { padding: 2px 18px; background-color: LightYellow; border-bottom: 1px solid #BEBEBE; color: #666666; } .editor-watchNewRow, .editor-memberRow { font-family: Monaco, monospace !important; } .editor-memberRow { padding: 1px 0 !important; } .editor-watchRow { padding-bottom: 0 !important; } .watchRow > .memberLabelCell { font-family: Monaco, monospace; padding-top: 1px; padding-bottom: 1px; } .watchRow > .memberLabelCell > .memberLabel { background-color: transparent; } .watchRow > .memberValueCell { padding-top: 2px; padding-bottom: 2px; } .watchRow > .memberLabelCell, .watchRow > .memberValueCell { background-color: #F5F5F5; border-bottom: 1px solid #BEBEBE; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .watchToolbox { z-index: 2147483647; position: absolute; right: 0; padding: 1px 2px; } /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /*************************************************************************************************/ /* FROM ORIGINAL FIREBUG */ /************************************************************************************************ CSS Not organized *************************************************************************************************/ #fbConsole { overflow-x: hidden !important; } #fbCSS { font: 1em Monaco, monospace; padding: 0 7px; } #fbstylesheetButtons select, #fbScriptButtons select { font: 11px Lucida Grande, Tahoma, sans-serif; margin-top: 1px; padding-left: 3px; background: #fafafa; border: 1px inset #fff; width: 220px; outline: none; } .Selector { margin-top:10px } .CSSItem {margin-left: 4% } .CSSText { padding-left:20px; } .CSSProperty { color:#005500; } .CSSValue { padding-left:5px; color:#000088; } /************************************************************************************************ Not organized *************************************************************************************************/ #fbHTMLStatusBar { display: inline; } .fbToolbarButtons { display: none; } .fbStatusSeparator{ display: block; float: left; padding-top: 4px; } #fbStatusBarBox { display: none; } #fbToolbarContent { display: block; position: absolute; _position: absolute; top: 0; padding-top: 4px; height: 23px; clip: rect(0, 2048px, 27px, 0); } .fbTabMenuTarget { display: none !important; float: left; width: 10px; height: 10px; margin-top: 6px; background: url(tabMenuTarget.png); } .fbTabMenuTarget:hover { background: url(tabMenuTargetHover.png); } .fbShadow { float: left; background: url(shadowAlpha.png) no-repeat bottom right !important; background: url(shadow2.gif) no-repeat bottom right; margin: 10px 0 0 10px !important; margin: 10px 0 0 5px; } .fbShadowContent { display: block; position: relative; background-color: #fff; border: 1px solid #a9a9a9; top: -6px; left: -6px; } .fbMenu { display: none; position: absolute; font-size: 11px; line-height: 13px; z-index: 2147483647; } .fbMenuContent { padding: 2px; } .fbMenuSeparator { display: block; position: relative; padding: 1px 18px 0; text-decoration: none; color: #000; cursor: default; background: #ACA899; margin: 4px 0; } .fbMenuOption { display: block; position: relative; padding: 2px 18px; text-decoration: none; color: #000; cursor: default; } .fbMenuOption:hover { color: #fff; background: #316AC5; } .fbMenuGroup { background: transparent url(tabMenuPin.png) no-repeat right 0; } .fbMenuGroup:hover { background: #316AC5 url(tabMenuPin.png) no-repeat right -17px; } .fbMenuGroupSelected { color: #fff; background: #316AC5 url(tabMenuPin.png) no-repeat right -17px; } .fbMenuChecked { background: transparent url(tabMenuCheckbox.png) no-repeat 4px 0; } .fbMenuChecked:hover { background: #316AC5 url(tabMenuCheckbox.png) no-repeat 4px -17px; } .fbMenuRadioSelected { background: transparent url(tabMenuRadio.png) no-repeat 4px 0; } .fbMenuRadioSelected:hover { background: #316AC5 url(tabMenuRadio.png) no-repeat 4px -17px; } .fbMenuShortcut { padding-right: 85px; } .fbMenuShortcutKey { position: absolute; right: 0; top: 2px; width: 77px; } #fbFirebugMenu { top: 22px; left: 0; } .fbMenuDisabled { color: #ACA899 !important; } #fbFirebugSettingsMenu { left: 245px; top: 99px; } #fbConsoleMenu { top: 42px; left: 48px; } .fbIconButton { display: block; } .fbIconButton { display: block; } .fbIconButton { display: block; float: left; height: 20px; width: 20px; color: #000; margin-right: 2px; text-decoration: none; cursor: default; } .fbIconButton:hover { position: relative; top: -1px; left: -1px; margin-right: 0; _margin-right: 1px; color: #333; border: 1px solid #fff; border-bottom: 1px solid #bbb; border-right: 1px solid #bbb; } .fbIconPressed { position: relative; margin-right: 0; _margin-right: 1px; top: 0 !important; left: 0 !important; height: 19px; color: #333 !important; border: 1px solid #bbb !important; border-bottom: 1px solid #cfcfcf !important; border-right: 1px solid #ddd !important; } /************************************************************************************************ Error Popup *************************************************************************************************/ #fbErrorPopup { position: absolute; right: 0; bottom: 0; height: 19px; width: 75px; background: url(sprite.png) #f1f2ee 0 0; z-index: 999; } #fbErrorPopupContent { position: absolute; right: 0; top: 1px; height: 18px; width: 75px; _width: 74px; border-left: 1px solid #aca899; } #fbErrorIndicator { position: absolute; top: 2px; right: 5px; } .fbBtnInspectActive { background: #aaa; color: #fff !important; } /************************************************************************************************ General *************************************************************************************************/ .fbBody { margin: 0; padding: 0; overflow: hidden; font-family: Lucida Grande, Tahoma, sans-serif; font-size: 11px; background: #fff; } .clear { clear: both; } /************************************************************************************************ Mini Chrome *************************************************************************************************/ #fbMiniChrome { display: none; right: 0; height: 27px; background: url(sprite.png) #f1f2ee 0 0; margin-left: 1px; } #fbMiniContent { display: block; position: relative; left: -1px; right: 0; top: 1px; height: 25px; border-left: 1px solid #aca899; } #fbToolbarSearch { float: right; border: 1px solid #ccc; margin: 0 5px 0 0; background: #fff url(search.png) no-repeat 4px 2px !important; background: #fff url(search.gif) no-repeat 4px 2px; padding-left: 20px; font-size: 11px; } #fbToolbarErrors { float: right; margin: 1px 4px 0 0; font-size: 11px; } #fbLeftToolbarErrors { float: left; margin: 7px 0px 0 5px; font-size: 11px; } .fbErrors { padding-left: 20px; height: 14px; background: url(errorIcon.png) no-repeat !important; background: url(errorIcon.gif) no-repeat; color: #f00; font-weight: bold; } #fbMiniErrors { display: inline; display: none; float: right; margin: 5px 2px 0 5px; } #fbMiniIcon { float: right; margin: 3px 4px 0; height: 20px; width: 20px; float: right; background: url(sprite.png) 0 -135px; cursor: pointer; } /************************************************************************************************ Master Layout *************************************************************************************************/ #fbChrome { font-family: Lucida Grande, Tahoma, sans-serif; font-size: 11px; position: absolute; _position: static; top: 0; left: 0; height: 100%; width: 100%; border-collapse: collapse; border-spacing: 0; background: #fff; overflow: hidden; } #fbChrome > tbody > tr > td { padding: 0; } #fbTop { height: 49px; } #fbToolbar { background: url(sprite.png) #f1f2ee 0 0; height: 27px; font-size: 11px; line-height: 13px; } #fbPanelBarBox { background: url(sprite.png) #dbd9c9 0 -27px; height: 22px; } #fbContent { height: 100%; vertical-align: top; } #fbBottom { height: 18px; background: #fff; } /************************************************************************************************ Sub-Layout *************************************************************************************************/ /* fbToolbar *************************************************************************************************/ #fbToolbarIcon { float: left; padding: 0 5px 0; } #fbToolbarIcon a { background: url(sprite.png) 0 -135px; } #fbToolbarButtons { padding: 0 2px 0 5px; } #fbToolbarButtons { padding: 0 2px 0 5px; } /* #fbStatusBarBox a { text-decoration: none; display: block; float: left; color: #000; padding: 4px 5px; margin: 0 0 0 1px; cursor: default; } #fbStatusBarBox a:hover { color: #333; padding: 3px 4px; border: 1px solid #fff; border-bottom: 1px solid #bbb; border-right: 1px solid #bbb; } /**/ .fbButton { text-decoration: none; display: block; float: left; color: #000; padding: 4px 6px 4px 7px; cursor: default; } .fbButton:hover { color: #333; background: #f5f5ef url(buttonBg.png); padding: 3px 5px 3px 6px; border: 1px solid #fff; border-bottom: 1px solid #bbb; border-right: 1px solid #bbb; } .fbBtnPressed { background: #e3e3db url(buttonBgHover.png) !important; padding: 3px 4px 2px 6px !important; margin: 1px 0 0 1px !important; border: 1px solid #ACA899 !important; border-color: #ACA899 #ECEBE3 #ECEBE3 #ACA899 !important; } #fbStatusBarBox { top: 4px; cursor: default; } .fbToolbarSeparator { overflow: hidden; border: 1px solid; border-color: transparent #fff transparent #777; _border-color: #eee #fff #eee #777; height: 7px; margin: 6px 3px; float: left; } .fbBtnSelected { font-weight: bold; } .fbStatusBar { color: #aca899; } .fbStatusBar a { text-decoration: none; color: black; } .fbStatusBar a:hover { color: blue; cursor: pointer; } #fbWindowButtons { position: absolute; white-space: nowrap; right: 0; top: 0; height: 17px; width: 48px; padding: 5px; z-index: 6; background: url(sprite.png) #f1f2ee 0 0; } /* fbPanelBarBox *************************************************************************************************/ #fbPanelBar1 { width: 1024px; /* fixed width to avoid tabs breaking line */ z-index: 8; left: 0; white-space: nowrap; background: url(sprite.png) #dbd9c9 0 -27px; position: absolute; left: 4px; } #fbPanelBar2Box { background: url(sprite.png) #dbd9c9 0 -27px; position: absolute; height: 22px; width: 300px; /* fixed width to avoid tabs breaking line */ z-index: 9; right: 0; } #fbPanelBar2 { position: absolute; width: 290px; /* fixed width to avoid tabs breaking line */ height: 22px; padding-left: 4px; } /* body *************************************************************************************************/ .fbPanel { display: none; } #fbPanelBox1, #fbPanelBox2 { max-height: inherit; height: 100%; font-size: 1em; } #fbPanelBox2 { background: #fff; } #fbPanelBox2 { width: 300px; background: #fff; } #fbPanel2 { margin-left: 6px; background: #fff; } #fbLargeCommandLine { display: none; position: absolute; z-index: 9; top: 27px; right: 0; width: 294px; height: 201px; border-width: 0; margin: 0; padding: 2px 0 0 2px; resize: none; outline: none; font-size: 11px; overflow: auto; border-top: 1px solid #B9B7AF; _right: -1px; _border-left: 1px solid #fff; } #fbLargeCommandButtons { display: none; background: #ECE9D8; bottom: 0; right: 0; width: 294px; height: 21px; padding-top: 1px; position: fixed; border-top: 1px solid #ACA899; z-index: 9; } #fbSmallCommandLineIcon { background: url(down.png) no-repeat; position: absolute; right: 2px; bottom: 3px; z-index: 99; } #fbSmallCommandLineIcon:hover { background: url(downHover.png) no-repeat; } .hide { overflow: hidden !important; position: fixed !important; display: none !important; visibility: hidden !important; } /* fbBottom *************************************************************************************************/ #fbCommand { height: 18px; } #fbCommandBox { position: fixed; _position: absolute; width: 100%; height: 18px; bottom: 0; overflow: hidden; z-index: 9; background: #fff; border: 0; border-top: 1px solid #ccc; } #fbCommandIcon { position: absolute; color: #00f; top: 2px; left: 6px; display: inline; font: 11px Monaco, monospace; z-index: 10; } #fbCommandLine { position: absolute; width: 100%; top: 0; left: 0; border: 0; margin: 0; padding: 2px 0 2px 32px; font: 11px Monaco, monospace; z-index: 9; outline: none; } #fbLargeCommandLineIcon { background: url(up.png) no-repeat; position: absolute; right: 1px; bottom: 1px; z-index: 10; } #fbLargeCommandLineIcon:hover { background: url(upHover.png) no-repeat; } div.fbFitHeight { overflow: auto; position: relative; } /************************************************************************************************ Layout Controls *************************************************************************************************/ /* fbToolbar buttons *************************************************************************************************/ .fbSmallButton { overflow: hidden; width: 16px; height: 16px; display: block; text-decoration: none; cursor: default; } #fbWindowButtons .fbSmallButton { float: right; } #fbWindow_btClose { background: url(min.png); } #fbWindow_btClose:hover { background: url(minHover.png); } #fbWindow_btDetach { background: url(detach.png); } #fbWindow_btDetach:hover { background: url(detachHover.png); } #fbWindow_btDeactivate { background: url(off.png); } #fbWindow_btDeactivate:hover { background: url(offHover.png); } /* fbPanelBarBox tabs *************************************************************************************************/ .fbTab { text-decoration: none; display: none; float: left; width: auto; float: left; cursor: default; font-family: Lucida Grande, Tahoma, sans-serif; font-size: 11px; line-height: 13px; font-weight: bold; height: 22px; color: #565656; } .fbPanelBar span { /*display: block; TODO: safe to remove this? */ float: left; } .fbPanelBar .fbTabL,.fbPanelBar .fbTabR { height: 22px; width: 8px; } .fbPanelBar .fbTabText { padding: 4px 1px 0; } a.fbTab:hover { background: url(sprite.png) 0 -73px; } a.fbTab:hover .fbTabL { background: url(sprite.png) -16px -96px; } a.fbTab:hover .fbTabR { background: url(sprite.png) -24px -96px; } .fbSelectedTab { background: url(sprite.png) #f1f2ee 0 -50px !important; color: #000; } .fbSelectedTab .fbTabL { background: url(sprite.png) 0 -96px !important; } .fbSelectedTab .fbTabR { background: url(sprite.png) -8px -96px !important; } /* splitters *************************************************************************************************/ #fbHSplitter { position: fixed; _position: absolute; left: 0; top: 0; width: 100%; height: 5px; overflow: hidden; cursor: n-resize !important; background: url(pixel_transparent.gif); z-index: 9; } #fbHSplitter.fbOnMovingHSplitter { height: 100%; z-index: 100; } .fbVSplitter { background: #ece9d8; color: #000; border: 1px solid #716f64; border-width: 0 1px; border-left-color: #aca899; width: 4px; cursor: e-resize; overflow: hidden; right: 294px; text-decoration: none; z-index: 10; position: absolute; height: 100%; top: 27px; } /************************************************************************************************/ div.lineNo { font: 1em/1.4545em Monaco, monospace; position: relative; float: left; top: 0; left: 0; margin: 0 5px 0 0; padding: 0 5px 0 10px; background: #eee; color: #888; border-right: 1px solid #ccc; text-align: right; } .sourceBox { position: absolute; } .sourceCode { font: 1em Monaco, monospace; overflow: hidden; white-space: pre; display: inline; } /************************************************************************************************/ .nodeControl { margin-top: 3px; margin-left: -14px; float: left; width: 9px; height: 9px; overflow: hidden; cursor: default; background: url(tree_open.gif); _float: none; _display: inline; _position: absolute; } div.nodeMaximized { background: url(tree_close.gif); } div.objectBox-element { padding: 1px 3px; } .objectBox-selector{ cursor: default; } .selectedElement{ background: highlight; /* background: url(roundCorner.svg); Opera */ color: #fff !important; } .selectedElement span{ color: #fff !important; } /* IE6 need this hack */ * html .selectedElement { position: relative; } /* Webkit CSS Hack - bug in "highlight" named color */ @media screen and (-webkit-min-device-pixel-ratio:0) { .selectedElement{ background: #316AC5; color: #fff !important; } } /************************************************************************************************/ /************************************************************************************************/ .logRow * { font-size: 1em; } /* TODO: remove this? */ /* TODO: xxxpedro - IE need this in windowless mode (cnn.com) check if the issue is related to position. if so, override it at chrome.js initialization when creating the div */ .logRow { position: relative; border-bottom: 1px solid #D7D7D7; padding: 2px 4px 1px 6px; zbackground-color: #FFFFFF; } /**/ .logRow-command { font-family: Monaco, monospace; color: blue; } .objectBox-string, .objectBox-text, .objectBox-number, .objectBox-function, .objectLink-element, .objectLink-textNode, .objectLink-function, .objectBox-stackTrace, .objectLink-profile { font-family: Monaco, monospace; } .objectBox-null { padding: 0 2px; border: 1px solid #666666; background-color: #888888; color: #FFFFFF; } .objectBox-string { color: red; /* TODO: xxxpedro make long strings break line */ /*white-space: pre; */ } .objectBox-number { color: #000088; } .objectBox-function { color: DarkGreen; } .objectBox-object { color: DarkGreen; font-weight: bold; font-family: Lucida Grande, sans-serif; } .objectBox-array { color: #000; } /************************************************************************************************/ .logRow-info,.logRow-error,.logRow-warn { background: #fff no-repeat 2px 2px; padding-left: 20px; padding-bottom: 3px; } .logRow-info { background-image: url(infoIcon.png) !important; background-image: url(infoIcon.gif); } .logRow-warn { background-color: cyan; background-image: url(warningIcon.png) !important; background-image: url(warningIcon.gif); } .logRow-error { background-color: LightYellow; background-image: url(errorIcon.png) !important; background-image: url(errorIcon.gif); color: #f00; } .errorMessage { vertical-align: top; color: #f00; } .objectBox-sourceLink { position: absolute; right: 4px; top: 2px; padding-left: 8px; font-family: Lucida Grande, sans-serif; font-weight: bold; color: #0000FF; } /************************************************************************************************/ /* //TODO: remove this when console2 is finished */ /* .logRow-group { background: #EEEEEE; border-bottom: none; } .logGroup { background: #EEEEEE; } .logGroupBox { margin-left: 24px; border-top: 1px solid #D7D7D7; border-left: 1px solid #D7D7D7; }/**/ /************************************************************************************************/ .selectorTag,.selectorId,.selectorClass { font-family: Monaco, monospace; font-weight: normal; } .selectorTag { color: #0000FF; } .selectorId { color: DarkBlue; } .selectorClass { color: red; } /************************************************************************************************/ .objectBox-element { font-family: Monaco, monospace; color: #000088; } .nodeChildren { padding-left: 26px; } .nodeTag { color: blue; cursor: pointer; } .nodeValue { color: #FF0000; font-weight: normal; } .nodeText,.nodeComment { margin: 0 2px; vertical-align: top; } .nodeText { color: #333333; font-family: Monaco, monospace; } .nodeComment { color: DarkGreen; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .nodeHidden, .nodeHidden * { color: #888888; } .nodeHidden .nodeTag { color: #5F82D9; } .nodeHidden .nodeValue { color: #D86060; } .selectedElement .nodeHidden, .selectedElement .nodeHidden * { color: SkyBlue !important; } /************************************************************************************************/ .log-object { /* _position: relative; _height: 100%; /**/ } .property { position: relative; clear: both; height: 15px; } .propertyNameCell { vertical-align: top; float: left; width: 28%; position: absolute; left: 0; z-index: 0; } .propertyValueCell { float: right; width: 68%; background: #fff; position: absolute; padding-left: 5px; display: table-cell; right: 0; z-index: 1; /* _position: relative; /**/ } .propertyName { font-weight: bold; } .FirebugPopup { height: 100% !important; } .FirebugPopup #fbWindowButtons { display: none !important; } .FirebugPopup #fbHSplitter { display: none !important; } lodash-2.4.1/vendor/firebug-lite/skin/xp/detach.png0000755000175000017500000000121712247303154020343 0ustar ovdovdPNG  IHDRasBIT|d pHYs  ~tEXtCreation Time05/29/092ltEXtSoftwareAdobe FireworksONIDAT8ӿAo6b$r?"D8JVK5].NRlQ+j%V"p̳5aPiͰfD3g!p]𺽴j9N~zX35S!o= ^DJ9t< UM>ݽO9ǗkD"X9 IR`Λ6UJkef@k$MHȥ jI< Gmi ~@o~81U09;}N MG!¯dg2/*o^7s.dp=H3 [tꞽ4 |cwnM̞u9 B  ?j6vDkeΞ ǡb@{C/mrZߌnaГ]8Ä(! [Vw/o<[ƄtSIENDB`lodash-2.4.1/vendor/firebug-lite/skin/xp/up.png0000755000175000017500000000115312247303154017536 0ustar ovdovdPNG  IHDRasBIT|d pHYs  ~tEXtCreation Time05/29/092ltEXtSoftwareAdobe FireworksONIDAT8=A;sB8𚃤9N Q; ;!&NlP;Xz!(*HBpd\M U3ӔRO \:E=p4_l v+g2¶*ex pO>{1"%E_xHiyB%R 3]ZkJ!4JҙBcs4)$YF~c ]/R( Eq死s$T9@L6!ڬka vkQmIޯ wO*3j:{_vm O ӄWGB`Df$Rr:aDJ*Gd{TbPEo;=TƲ?^ hPEw+bkSq{ [aDY]smlԽU ٤?]=IENDB`lodash-2.4.1/vendor/firebug-lite/skin/xp/tree_close.gif0000755000175000017500000000045412247303154021222 0ustar ovdovdGIF89a ³ôõĶȺ!Created with GIMP! ?, 4C`H A$hi6=aé^&C ` 3Kq %c@ ` (C;lodash-2.4.1/vendor/firebug-lite/skin/xp/infoIcon.png0000755000175000017500000000101412247303154020652 0ustar ovdovdPNG  IHDRH-gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxڌRM(DQ}Ϙ13addegkA6h,FFebeR"" a!57?{fo$ιC)`i^'# ׷l!(\fHw=(')Dz%*%$Ѩ2*fE~`y,_yxx| `$$ Q7 v>C`w;‡· ʨz0ې0d>B;QDW]-y4pǐLF^~6u`n|&4Br, EerBc#薻x;?f/rξۚs(K43 aDI\% Biuz8v 6& {s?XV0aIENDB`lodash-2.4.1/vendor/firebug-lite/skin/xp/pixel_transparent.gif0000755000175000017500000000005312247303154022633 0ustar ovdovdGIF89a e!,D;lodash-2.4.1/vendor/firebug-lite/skin/xp/down.png0000755000175000017500000000117512247303154020065 0ustar ovdovdPNG  IHDRasBIT|d pHYs  ~tEXtCreation Time05/29/092ltEXtSoftwareAdobe FireworksONIDAT8?hSQIɐЎ"AA]LXl8]mtP'P =/K z{/{?a^w/.uʵueK9seG*W/ўW?ن ..a*-.qSR/-p"Bx"(ASH' o+0 C0Ʌع{%1f&e$5j5HOÈU*DSH4fU0"zH?zA `)Nu[TW(g-Z0]f }ũi0Q-XfƌK9B(fN=sD~R`Y]0ĞI[: Bȼ22GZ&~7bYت\\jL6L1O b:IENDB`lodash-2.4.1/vendor/firebug-lite/skin/xp/blank.gif0000755000175000017500000000005312247303154020160 0ustar ovdovdGIF89a e!,D;lodash-2.4.1/vendor/firebug-lite/skin/xp/roundCorner.svg0000755000175000017500000000034412247303154021426 0ustar ovdovd lodash-2.4.1/vendor/firebug-lite/skin/xp/downHover.png0000755000175000017500000000101612247303154021063 0ustar ovdovdPNG  IHDRasBIT|d pHYs  ~tEXtCreation Time05/29/092ltEXtSoftwareAdobe FireworksONjIDAT8?KBQ^^5*4c M7 ZZ!M!htj{ Ko.%=p~ρU?MKv/L+4D*[̭l' |U<,˅b]9?mN[䲶CLd$@F(C fd   C  IBIA;lodash-2.4.1/vendor/qunit/0000755000175000017500000000000012247303154013570 5ustar ovdovdlodash-2.4.1/vendor/qunit/qunit/0000755000175000017500000000000012247303154014730 5ustar ovdovdlodash-2.4.1/vendor/qunit/qunit/qunit.js0000644000175000017500000015711412247303154016437 0ustar ovdovd/** * QUnit v1.11.0 - A JavaScript Unit Testing Framework * * http://qunitjs.com * * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license */ (function( window ) { var QUnit, assert, config, onErrorFnPrev, testId = 0, fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, // Keep a local reference to Date (GH-283) Date = window.Date, defined = { setTimeout: typeof window.setTimeout !== "undefined", sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; } catch( e ) { return false; } }()) }, /** * Provides a normalized error string, correcting an issue * with IE 7 (and prior) where Error.prototype.toString is * not properly implemented * * Based on http://es5.github.com/#x15.11.4.4 * * @param {String|Error} error * @return {String} error message */ errorString = function( error ) { var name, message, errorString = error.toString(); if ( errorString.substring( 0, 7 ) === "[object" ) { name = error.name ? error.name.toString() : "Error"; message = error.message ? error.message.toString() : ""; if ( name && message ) { return name + ": " + message; } else if ( name ) { return name; } else if ( message ) { return message; } else { return "Error"; } } else { return errorString; } }, /** * Makes a clone of an object using only Array or Object as base, * and copies over the own enumerable properties. * * @param {Object} obj * @return {Object} New object with only the own properties (recursively). */ objectValues = function( obj ) { // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392. /*jshint newcap: false */ var key, val, vals = QUnit.is( "array", obj ) ? [] : {}; for ( key in obj ) { if ( hasOwn.call( obj, key ) ) { val = obj[key]; vals[key] = val === Object(val) ? objectValues(val) : val; } } return vals; }; function Test( settings ) { extend( this, settings ); this.assertions = []; this.testNumber = ++Test.count; } Test.count = 0; Test.prototype = { init: function() { var a, b, li, tests = id( "qunit-tests" ); if ( tests ) { b = document.createElement( "strong" ); b.innerHTML = this.nameHtml; // `a` initialized at top of scope a = document.createElement( "a" ); a.innerHTML = "Rerun"; a.href = QUnit.url({ testNumber: this.testNumber }); li = document.createElement( "li" ); li.appendChild( b ); li.appendChild( a ); li.className = "running"; li.id = this.id = "qunit-test-output" + testId++; tests.appendChild( li ); } }, setup: function() { if ( this.module !== config.previousModule ) { if ( config.previousModule ) { runLoggingCallbacks( "moduleDone", QUnit, { name: config.previousModule, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all }); } config.previousModule = this.module; config.moduleStats = { all: 0, bad: 0 }; runLoggingCallbacks( "moduleStart", QUnit, { name: this.module }); } else if ( config.autorun ) { runLoggingCallbacks( "moduleStart", QUnit, { name: this.module }); } config.current = this; this.testEnvironment = extend({ setup: function() {}, teardown: function() {} }, this.moduleTestEnvironment ); this.started = +new Date(); runLoggingCallbacks( "testStart", QUnit, { name: this.testName, module: this.module }); // allow utility functions to access the current test environment // TODO why?? QUnit.current_testEnvironment = this.testEnvironment; if ( !config.pollution ) { saveGlobal(); } if ( config.notrycatch ) { this.testEnvironment.setup.call( this.testEnvironment ); return; } try { this.testEnvironment.setup.call( this.testEnvironment ); } catch( e ) { QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); } }, run: function() { config.current = this; var running = id( "qunit-testresult" ); if ( running ) { running.innerHTML = "Running:
" + this.nameHtml; } if ( this.async ) { QUnit.stop(); } this.callbackStarted = +new Date(); if ( config.notrycatch ) { this.callback.call( this.testEnvironment, QUnit.assert ); this.callbackRuntime = +new Date() - this.callbackStarted; return; } try { this.callback.call( this.testEnvironment, QUnit.assert ); this.callbackRuntime = +new Date() - this.callbackStarted; } catch( e ) { this.callbackRuntime = +new Date() - this.callbackStarted; QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); // else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if ( config.blocking ) { QUnit.start(); } } }, teardown: function() { config.current = this; if ( config.notrycatch ) { if ( typeof this.callbackRuntime === "undefined" ) { this.callbackRuntime = +new Date() - this.callbackStarted; } this.testEnvironment.teardown.call( this.testEnvironment ); return; } else { try { this.testEnvironment.teardown.call( this.testEnvironment ); } catch( e ) { QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); } } checkPollution(); }, finish: function() { config.current = this; if ( config.requireExpects && this.expected === null ) { QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); } else if ( this.expected !== null && this.expected !== this.assertions.length ) { QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); } else if ( this.expected === null && !this.assertions.length ) { QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); } var i, assertion, a, b, time, li, ol, test = this, good = 0, bad = 0, tests = id( "qunit-tests" ); this.runtime = +new Date() - this.started; config.stats.all += this.assertions.length; config.moduleStats.all += this.assertions.length; if ( tests ) { ol = document.createElement( "ol" ); ol.className = "qunit-assert-list"; for ( i = 0; i < this.assertions.length; i++ ) { assertion = this.assertions[i]; li = document.createElement( "li" ); li.className = assertion.result ? "pass" : "fail"; li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); ol.appendChild( li ); if ( assertion.result ) { good++; } else { bad++; config.stats.bad++; config.moduleStats.bad++; } } // store result when possible if ( QUnit.config.reorder && defined.sessionStorage ) { if ( bad ) { sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); } else { sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); } } if ( bad === 0 ) { addClass( ol, "qunit-collapsed" ); } // `b` initialized at top of scope b = document.createElement( "strong" ); b.innerHTML = this.nameHtml + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; addEvent(b, "click", function() { var next = b.parentNode.lastChild, collapsed = hasClass( next, "qunit-collapsed" ); ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" ); }); addEvent(b, "dblclick", function( e ) { var target = e && e.target ? e.target : window.event.srcElement; if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { target = target.parentNode; } if ( window.location && target.nodeName.toLowerCase() === "strong" ) { window.location = QUnit.url({ testNumber: test.testNumber }); } }); // `time` initialized at top of scope time = document.createElement( "span" ); time.className = "runtime"; time.innerHTML = this.runtime + " ms"; // `li` initialized at top of scope li = id( this.id ); li.className = bad ? "fail" : "pass"; li.removeChild( li.firstChild ); a = li.firstChild; li.appendChild( b ); li.appendChild( a ); li.appendChild( time ); li.appendChild( ol ); } else { for ( i = 0; i < this.assertions.length; i++ ) { if ( !this.assertions[i].result ) { bad++; config.stats.bad++; config.moduleStats.bad++; } } } runLoggingCallbacks( "testDone", QUnit, { name: this.testName, module: this.module, failed: bad, passed: this.assertions.length - bad, total: this.assertions.length, duration: this.runtime }); QUnit.reset(); config.current = undefined; }, queue: function() { var bad, test = this; synchronize(function() { test.init(); }); function run() { // each of these can by async synchronize(function() { test.setup(); }); synchronize(function() { test.run(); }); synchronize(function() { test.teardown(); }); synchronize(function() { test.finish(); }); } // `bad` initialized at top of scope // defer when previous test run passed, if storage is available bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); if ( bad ) { run(); } else { synchronize( run, true ); } } }; // Root QUnit object. // `QUnit` initialized at top of scope QUnit = { // call on start of module test to prepend name to all tests module: function( name, testEnvironment ) { config.currentModule = name; config.currentModuleTestEnvironment = testEnvironment; config.modules[name] = true; }, asyncTest: function( testName, expected, callback ) { if ( arguments.length === 2 ) { callback = expected; expected = null; } QUnit.test( testName, expected, callback, true ); }, test: function( testName, expected, callback, async ) { var test, nameHtml = "" + escapeText( testName ) + ""; if ( arguments.length === 2 ) { callback = expected; expected = null; } if ( config.currentModule ) { nameHtml = "" + escapeText( config.currentModule ) + ": " + nameHtml; } test = new Test({ nameHtml: nameHtml, testName: testName, expected: expected, async: async, callback: callback, module: config.currentModule, moduleTestEnvironment: config.currentModuleTestEnvironment, stack: sourceFromStacktrace( 2 ) }); if ( !validTest( test ) ) { return; } test.queue(); }, // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. expect: function( asserts ) { if (arguments.length === 1) { config.current.expected = asserts; } else { return config.current.expected; } }, start: function( count ) { // QUnit hasn't been initialized yet. // Note: RequireJS (et al) may delay onLoad if ( config.semaphore === undefined ) { QUnit.begin(function() { // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first setTimeout(function() { QUnit.start( count ); }); }); return; } config.semaphore -= count || 1; // don't start until equal number of stop-calls if ( config.semaphore > 0 ) { return; } // ignore if start is called more often then stop if ( config.semaphore < 0 ) { config.semaphore = 0; QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) ); return; } // A slight delay, to avoid any current callbacks if ( defined.setTimeout ) { window.setTimeout(function() { if ( config.semaphore > 0 ) { return; } if ( config.timeout ) { clearTimeout( config.timeout ); } config.blocking = false; process( true ); }, 13); } else { config.blocking = false; process( true ); } }, stop: function( count ) { config.semaphore += count || 1; config.blocking = true; if ( config.testTimeout && defined.setTimeout ) { clearTimeout( config.timeout ); config.timeout = window.setTimeout(function() { QUnit.ok( false, "Test timed out" ); config.semaphore = 1; QUnit.start(); }, config.testTimeout ); } } }; // `assert` initialized at top of scope // Asssert helpers // All of these must either call QUnit.push() or manually do: // - runLoggingCallbacks( "log", .. ); // - config.current.assertions.push({ .. }); // We attach it to the QUnit object *after* we expose the public API, // otherwise `assert` will become a global variable in browsers (#341). assert = { /** * Asserts rough true-ish result. * @name ok * @function * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); */ ok: function( result, msg ) { if ( !config.current ) { throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); } result = !!result; var source, details = { module: config.current.module, name: config.current.testName, result: result, message: msg }; msg = escapeText( msg || (result ? "okay" : "failed" ) ); msg = "" + msg + ""; if ( !result ) { source = sourceFromStacktrace( 2 ); if ( source ) { details.source = source; msg += "
Source:
" + escapeText( source ) + "
"; } } runLoggingCallbacks( "log", QUnit, details ); config.current.assertions.push({ result: result, message: msg }); }, /** * Assert that the first two arguments are equal, with an optional message. * Prints out both actual and expected values. * @name equal * @function * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); */ equal: function( actual, expected, message ) { /*jshint eqeqeq:false */ QUnit.push( expected == actual, actual, expected, message ); }, /** * @name notEqual * @function */ notEqual: function( actual, expected, message ) { /*jshint eqeqeq:false */ QUnit.push( expected != actual, actual, expected, message ); }, /** * @name propEqual * @function */ propEqual: function( actual, expected, message ) { actual = objectValues(actual); expected = objectValues(expected); QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name notPropEqual * @function */ notPropEqual: function( actual, expected, message ) { actual = objectValues(actual); expected = objectValues(expected); QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name deepEqual * @function */ deepEqual: function( actual, expected, message ) { QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name notDeepEqual * @function */ notDeepEqual: function( actual, expected, message ) { QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name strictEqual * @function */ strictEqual: function( actual, expected, message ) { QUnit.push( expected === actual, actual, expected, message ); }, /** * @name notStrictEqual * @function */ notStrictEqual: function( actual, expected, message ) { QUnit.push( expected !== actual, actual, expected, message ); }, "throws": function( block, expected, message ) { var actual, expectedOutput = expected, ok = false; // 'expected' is optional if ( typeof expected === "string" ) { message = expected; expected = null; } config.current.ignoreGlobalErrors = true; try { block.call( config.current.testEnvironment ); } catch (e) { actual = e; } config.current.ignoreGlobalErrors = false; if ( actual ) { // we don't want to validate thrown error if ( !expected ) { ok = true; expectedOutput = null; // expected is a regexp } else if ( QUnit.objectType( expected ) === "regexp" ) { ok = expected.test( errorString( actual ) ); // expected is a constructor } else if ( actual instanceof expected ) { ok = true; // expected is a validation function which returns true is validation passed } else if ( expected.call( {}, actual ) === true ) { expectedOutput = null; ok = true; } QUnit.push( ok, actual, expectedOutput, message ); } else { QUnit.pushFailure( message, null, 'No exception was thrown.' ); } } }; /** * @deprecate since 1.8.0 * Kept assertion helpers in root for backwards compatibility. */ extend( QUnit, assert ); /** * @deprecated since 1.9.0 * Kept root "raises()" for backwards compatibility. * (Note that we don't introduce assert.raises). */ QUnit.raises = assert[ "throws" ]; /** * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 * Kept to avoid TypeErrors for undefined methods. */ QUnit.equals = function() { QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); }; QUnit.same = function() { QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); }; // We want access to the constructor's prototype (function() { function F() {} F.prototype = QUnit; QUnit = new F(); // Make F QUnit's constructor so that we can add to the prototype later QUnit.constructor = F; }()); /** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */ config = { // The queue of tests to run queue: [], // block until document ready blocking: true, // when enabled, show only failing tests // gets persisted through sessionStorage and can be changed in UI via checkbox hidepassed: false, // by default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, // by default, modify document.title when suite is done altertitle: true, // when enabled, all tests must call expect() requireExpects: false, // add checkboxes that are persisted in the query-string // when enabled, the id is set to `true` as a `QUnit.config` property urlConfig: [ { id: "noglobals", label: "Check for Globals", tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings." }, { id: "notrycatch", label: "No try-catch", tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings." } ], // Set of all modules. modules: {}, // logging callback queues begin: [], done: [], log: [], testStart: [], testDone: [], moduleStart: [], moduleDone: [] }; // Export global variables, unless an 'exports' object exists, // in that case we assume we're in CommonJS (dealt with on the bottom of the script) if ( typeof exports === "undefined" ) { extend( window, QUnit ); // Expose QUnit object window.QUnit = QUnit; } // Initialize more QUnit.config and QUnit.urlParams (function() { var i, location = window.location || { search: "", protocol: "file:" }, params = location.search.slice( 1 ).split( "&" ), length = params.length, urlParams = {}, current; if ( params[ 0 ] ) { for ( i = 0; i < length; i++ ) { current = params[ i ].split( "=" ); current[ 0 ] = decodeURIComponent( current[ 0 ] ); // allow just a key to turn on a flag, e.g., test.html?noglobals current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; urlParams[ current[ 0 ] ] = current[ 1 ]; } } QUnit.urlParams = urlParams; // String search anywhere in moduleName+testName config.filter = urlParams.filter; // Exact match of the module name config.module = urlParams.module; config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; // Figure out if we're running the tests from a server or not QUnit.isLocal = location.protocol === "file:"; }()); // Extend QUnit object, // these after set here because they should not be exposed as global functions extend( QUnit, { assert: assert, config: config, // Initialize the configuration options init: function() { extend( config, { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: +new Date(), updateRate: 1000, blocking: false, autostart: true, autorun: false, filter: "", queue: [], semaphore: 1 }); var tests, banner, result, qunit = id( "qunit" ); if ( qunit ) { qunit.innerHTML = "

" + escapeText( document.title ) + "

" + "

" + "
" + "

" + "
    "; } tests = id( "qunit-tests" ); banner = id( "qunit-banner" ); result = id( "qunit-testresult" ); if ( tests ) { tests.innerHTML = ""; } if ( banner ) { banner.className = ""; } if ( result ) { result.parentNode.removeChild( result ); } if ( tests ) { result = document.createElement( "p" ); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests ); result.innerHTML = "Running...
     "; } }, // Resets the test setup. Useful for tests that modify the DOM. reset: function() { var fixture = id( "qunit-fixture" ); if ( fixture ) { fixture.innerHTML = config.fixture; } }, // Trigger an event on an element. // @example triggerEvent( document.body, "click" ); triggerEvent: function( elem, type, event ) { if ( document.createEvent ) { event = document.createEvent( "MouseEvents" ); event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); elem.dispatchEvent( event ); } else if ( elem.fireEvent ) { elem.fireEvent( "on" + type ); } }, // Safe object type checking is: function( type, obj ) { return QUnit.objectType( obj ) === type; }, objectType: function( obj ) { if ( typeof obj === "undefined" ) { return "undefined"; // consider: typeof null === object } if ( obj === null ) { return "null"; } var match = toString.call( obj ).match(/^\[object\s(.*)\]$/), type = match && match[1] || ""; switch ( type ) { case "Number": if ( isNaN(obj) ) { return "nan"; } return "number"; case "String": case "Boolean": case "Array": case "Date": case "RegExp": case "Function": return type.toLowerCase(); } if ( typeof obj === "object" ) { return "object"; } return undefined; }, push: function( result, actual, expected, message ) { if ( !config.current ) { throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); } var output, source, details = { module: config.current.module, name: config.current.testName, result: result, message: message, actual: actual, expected: expected }; message = escapeText( message ) || ( result ? "okay" : "failed" ); message = "" + message + ""; output = message; if ( !result ) { expected = escapeText( QUnit.jsDump.parse(expected) ); actual = escapeText( QUnit.jsDump.parse(actual) ); output += ""; if ( actual !== expected ) { output += ""; output += ""; } source = sourceFromStacktrace(); if ( source ) { details.source = source; output += ""; } output += "
    Expected:
    " + expected + "
    Result:
    " + actual + "
    Diff:
    " + QUnit.diff( expected, actual ) + "
    Source:
    " + escapeText( source ) + "
    "; } runLoggingCallbacks( "log", QUnit, details ); config.current.assertions.push({ result: !!result, message: output }); }, pushFailure: function( message, source, actual ) { if ( !config.current ) { throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); } var output, details = { module: config.current.module, name: config.current.testName, result: false, message: message }; message = escapeText( message ) || "error"; message = "" + message + ""; output = message; output += ""; if ( actual ) { output += ""; } if ( source ) { details.source = source; output += ""; } output += "
    Result:
    " + escapeText( actual ) + "
    Source:
    " + escapeText( source ) + "
    "; runLoggingCallbacks( "log", QUnit, details ); config.current.assertions.push({ result: false, message: output }); }, url: function( params ) { params = extend( extend( {}, QUnit.urlParams ), params ); var key, querystring = "?"; for ( key in params ) { if ( !hasOwn.call( params, key ) ) { continue; } querystring += encodeURIComponent( key ) + "=" + encodeURIComponent( params[ key ] ) + "&"; } return window.location.protocol + "//" + window.location.host + window.location.pathname + querystring.slice( 0, -1 ); }, extend: extend, id: id, addEvent: addEvent // load, equiv, jsDump, diff: Attached later }); /** * @deprecated: Created for backwards compatibility with test runner that set the hook function * into QUnit.{hook}, instead of invoking it and passing the hook function. * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. * Doing this allows us to tell if the following methods have been overwritten on the actual * QUnit object. */ extend( QUnit.constructor.prototype, { // Logging callbacks; all receive a single argument with the listed properties // run test/logs.html for any related changes begin: registerLoggingCallback( "begin" ), // done: { failed, passed, total, runtime } done: registerLoggingCallback( "done" ), // log: { result, actual, expected, message } log: registerLoggingCallback( "log" ), // testStart: { name } testStart: registerLoggingCallback( "testStart" ), // testDone: { name, failed, passed, total, duration } testDone: registerLoggingCallback( "testDone" ), // moduleStart: { name } moduleStart: registerLoggingCallback( "moduleStart" ), // moduleDone: { name, failed, passed, total } moduleDone: registerLoggingCallback( "moduleDone" ) }); if ( typeof document === "undefined" || document.readyState === "complete" ) { config.autorun = true; } QUnit.load = function() { runLoggingCallbacks( "begin", QUnit, {} ); // Initialize the config, saving the execution queue var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter, numModules = 0, moduleFilterHtml = "", urlConfigHtml = "", oldconfig = extend( {}, config ); QUnit.init(); extend(config, oldconfig); config.blocking = false; len = config.urlConfig.length; for ( i = 0; i < len; i++ ) { val = config.urlConfig[i]; if ( typeof val === "string" ) { val = { id: val, label: val, tooltip: "[no tooltip available]" }; } config[ val.id ] = QUnit.urlParams[ val.id ]; urlConfigHtml += ""; } moduleFilterHtml += ""; // `userAgent` initialized at top of scope userAgent = id( "qunit-userAgent" ); if ( userAgent ) { userAgent.innerHTML = navigator.userAgent; } // `banner` initialized at top of scope banner = id( "qunit-header" ); if ( banner ) { banner.innerHTML = "" + banner.innerHTML + " "; } // `toolbar` initialized at top of scope toolbar = id( "qunit-testrunner-toolbar" ); if ( toolbar ) { // `filter` initialized at top of scope filter = document.createElement( "input" ); filter.type = "checkbox"; filter.id = "qunit-filter-pass"; addEvent( filter, "click", function() { var tmp, ol = document.getElementById( "qunit-tests" ); if ( filter.checked ) { ol.className = ol.className + " hidepass"; } else { tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; ol.className = tmp.replace( / hidepass /, " " ); } if ( defined.sessionStorage ) { if (filter.checked) { sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); } else { sessionStorage.removeItem( "qunit-filter-passed-tests" ); } } }); if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { filter.checked = true; // `ol` initialized at top of scope ol = document.getElementById( "qunit-tests" ); ol.className = ol.className + " hidepass"; } toolbar.appendChild( filter ); // `label` initialized at top of scope label = document.createElement( "label" ); label.setAttribute( "for", "qunit-filter-pass" ); label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." ); label.innerHTML = "Hide passed tests"; toolbar.appendChild( label ); urlConfigCheckboxesContainer = document.createElement("span"); urlConfigCheckboxesContainer.innerHTML = urlConfigHtml; urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input"); // For oldIE support: // * Add handlers to the individual elements instead of the container // * Use "click" instead of "change" // * Fallback from event.target to event.srcElement addEvents( urlConfigCheckboxes, "click", function( event ) { var params = {}, target = event.target || event.srcElement; params[ target.name ] = target.checked ? true : undefined; window.location = QUnit.url( params ); }); toolbar.appendChild( urlConfigCheckboxesContainer ); if (numModules > 1) { moduleFilter = document.createElement( 'span' ); moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' ); moduleFilter.innerHTML = moduleFilterHtml; addEvent( moduleFilter.lastChild, "change", function() { var selectBox = moduleFilter.getElementsByTagName("select")[0], selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } ); }); toolbar.appendChild(moduleFilter); } } // `main` initialized at top of scope main = id( "qunit-fixture" ); if ( main ) { config.fixture = main.innerHTML; } if ( config.autostart ) { QUnit.start(); } }; addEvent( window, "load", QUnit.load ); // `onErrorFnPrev` initialized at top of scope // Preserve other handlers onErrorFnPrev = window.onerror; // Cover uncaught exceptions // Returning true will surpress the default browser handler, // returning false will let it run. window.onerror = function ( error, filePath, linerNr ) { var ret = false; if ( onErrorFnPrev ) { ret = onErrorFnPrev( error, filePath, linerNr ); } // Treat return value as window.onerror itself does, // Only do our handling if not surpressed. if ( ret !== true ) { if ( QUnit.config.current ) { if ( QUnit.config.current.ignoreGlobalErrors ) { return true; } QUnit.pushFailure( error, filePath + ":" + linerNr ); } else { QUnit.test( "global failure", extend( function() { QUnit.pushFailure( error, filePath + ":" + linerNr ); }, { validTest: validTest } ) ); } return false; } return ret; }; function done() { config.autorun = true; // Log the last module results if ( config.currentModule ) { runLoggingCallbacks( "moduleDone", QUnit, { name: config.currentModule, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all }); } var i, key, banner = id( "qunit-banner" ), tests = id( "qunit-tests" ), runtime = +new Date() - config.started, passed = config.stats.all - config.stats.bad, html = [ "Tests completed in ", runtime, " milliseconds.
    ", "", passed, " assertions of ", config.stats.all, " passed, ", config.stats.bad, " failed." ].join( "" ); if ( banner ) { banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); } if ( tests ) { id( "qunit-testresult" ).innerHTML = html; } if ( config.altertitle && typeof document !== "undefined" && document.title ) { // show ✖ for good, ✔ for bad suite result in title // use escape sequences in case file gets loaded with non-utf-8-charset document.title = [ ( config.stats.bad ? "\u2716" : "\u2714" ), document.title.replace( /^[\u2714\u2716] /i, "" ) ].join( " " ); } // clear own sessionStorage items if all tests passed if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { // `key` & `i` initialized at top of scope for ( i = 0; i < sessionStorage.length; i++ ) { key = sessionStorage.key( i++ ); if ( key.indexOf( "qunit-test-" ) === 0 ) { sessionStorage.removeItem( key ); } } } // scroll back to top to show results if ( window.scrollTo ) { window.scrollTo(0, 0); } runLoggingCallbacks( "done", QUnit, { failed: config.stats.bad, passed: passed, total: config.stats.all, runtime: runtime }); } /** @return Boolean: true if this test should be ran */ function validTest( test ) { var include, filter = config.filter && config.filter.toLowerCase(), module = config.module && config.module.toLowerCase(), fullName = (test.module + ": " + test.testName).toLowerCase(); // Internally-generated tests are always valid if ( test.callback && test.callback.validTest === validTest ) { delete test.callback.validTest; return true; } if ( config.testNumber ) { return test.testNumber === config.testNumber; } if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) { return false; } if ( !filter ) { return true; } include = filter.charAt( 0 ) !== "!"; if ( !include ) { filter = filter.slice( 1 ); } // If the filter matches, we need to honour include if ( fullName.indexOf( filter ) !== -1 ) { return include; } // Otherwise, do the opposite return !include; } // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) // Later Safari and IE10 are supposed to support error.stack as well // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { offset = offset === undefined ? 3 : offset; var stack, include, i; if ( e.stacktrace ) { // Opera return e.stacktrace.split( "\n" )[ offset + 3 ]; } else if ( e.stack ) { // Firefox, Chrome stack = e.stack.split( "\n" ); if (/^error$/i.test( stack[0] ) ) { stack.shift(); } if ( fileName ) { include = []; for ( i = offset; i < stack.length; i++ ) { if ( stack[ i ].indexOf( fileName ) !== -1 ) { break; } include.push( stack[ i ] ); } if ( include.length ) { return include.join( "\n" ); } } return stack[ offset ]; } else if ( e.sourceURL ) { // Safari, PhantomJS // hopefully one day Safari provides actual stacktraces // exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; } // for actual exceptions, this is useful return e.sourceURL + ":" + e.line; } } function sourceFromStacktrace( offset ) { try { throw new Error(); } catch ( e ) { return extractStacktrace( e, offset ); } } /** * Escape text for attribute or text content. */ function escapeText( s ) { if ( !s ) { return ""; } s = s + ""; // Both single quotes and double quotes (for attributes) return s.replace( /['"<>&]/g, function( s ) { switch( s ) { case '\'': return '''; case '"': return '"'; case '<': return '<'; case '>': return '>'; case '&': return '&'; } }); } function synchronize( callback, last ) { config.queue.push( callback ); if ( config.autorun && !config.blocking ) { process( last ); } } function process( last ) { function next() { process( last ); } var start = new Date().getTime(); config.depth = config.depth ? config.depth + 1 : 1; while ( config.queue.length && !config.blocking ) { if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { config.queue.shift()(); } else { window.setTimeout( next, 13 ); break; } } config.depth--; if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { done(); } } function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in window ) { // in Opera sometimes DOM element ids show up here, ignore them if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) { continue; } config.pollution.push( key ); } } } function checkPollution() { var newGlobals, deletedGlobals, old = config.pollution; saveGlobal(); newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); } deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); } } // returns a new Array with the elements that are in a but not in b function diff( a, b ) { var i, j, result = a.slice(); for ( i = 0; i < result.length; i++ ) { for ( j = 0; j < b.length; j++ ) { if ( result[i] === b[j] ) { result.splice( i, 1 ); i--; break; } } } return result; } function extend( a, b ) { for ( var prop in b ) { if ( b[ prop ] === undefined ) { delete a[ prop ]; // Avoid "Member not found" error in IE8 caused by setting window.constructor } else if ( prop !== "constructor" || a !== window ) { a[ prop ] = b[ prop ]; } } return a; } /** * @param {HTMLElement} elem * @param {string} type * @param {Function} fn */ function addEvent( elem, type, fn ) { // Standards-based browsers if ( elem.addEventListener ) { elem.addEventListener( type, fn, false ); // IE } else { elem.attachEvent( "on" + type, fn ); } } /** * @param {Array|NodeList} elems * @param {string} type * @param {Function} fn */ function addEvents( elems, type, fn ) { var i = elems.length; while ( i-- ) { addEvent( elems[i], type, fn ); } } function hasClass( elem, name ) { return (" " + elem.className + " ").indexOf(" " + name + " ") > -1; } function addClass( elem, name ) { if ( !hasClass( elem, name ) ) { elem.className += (elem.className ? " " : "") + name; } } function removeClass( elem, name ) { var set = " " + elem.className + " "; // Class name may appear multiple times while ( set.indexOf(" " + name + " ") > -1 ) { set = set.replace(" " + name + " " , " "); } // If possible, trim it for prettiness, but not neccecarily elem.className = window.jQuery ? jQuery.trim( set ) : ( set.trim ? set.trim() : set ); } function id( name ) { return !!( typeof document !== "undefined" && document && document.getElementById ) && document.getElementById( name ); } function registerLoggingCallback( key ) { return function( callback ) { config[key].push( callback ); }; } // Supports deprecated method of completely overwriting logging callbacks function runLoggingCallbacks( key, scope, args ) { var i, callbacks; if ( QUnit.hasOwnProperty( key ) ) { QUnit[ key ].call(scope, args ); } else { callbacks = config[ key ]; for ( i = 0; i < callbacks.length; i++ ) { callbacks[ i ].call( scope, args ); } } } // Test for equality any JavaScript type. // Author: Philippe Rathé QUnit.equiv = (function() { // Call the o related callback with the given arguments. function bindCallbacks( o, callbacks, args ) { var prop = QUnit.objectType( o ); if ( prop ) { if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { return callbacks[ prop ].apply( callbacks, args ); } else { return callbacks[ prop ]; // or undefined } } } // the real equiv function var innerEquiv, // stack to decide between skip/abort functions callers = [], // stack to avoiding loops from circular referencing parents = [], getProto = Object.getPrototypeOf || function ( obj ) { return obj.__proto__; }, callbacks = (function () { // for string, boolean, number and null function useStrictEquality( b, a ) { /*jshint eqeqeq:false */ if ( b instanceof a.constructor || a instanceof b.constructor ) { // to catch short annotaion VS 'new' annotation of a // declaration // e.g. var i = 1; // var j = new Number(1); return a == b; } else { return a === b; } } return { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "nan": function( b ) { return isNaN( b ); }, "date": function( b, a ) { return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); }, "regexp": function( b, a ) { return QUnit.objectType( b ) === "regexp" && // the regex itself a.source === b.source && // and its modifers a.global === b.global && // (gmi) ... a.ignoreCase === b.ignoreCase && a.multiline === b.multiline && a.sticky === b.sticky; }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function() { var caller = callers[callers.length - 1]; return caller !== Object && typeof caller !== "undefined"; }, "array": function( b, a ) { var i, j, len, loop; // b could be an object literal here if ( QUnit.objectType( b ) !== "array" ) { return false; } len = a.length; if ( len !== b.length ) { // safe and faster return false; } // track reference to avoid circular references parents.push( a ); for ( i = 0; i < len; i++ ) { loop = false; for ( j = 0; j < parents.length; j++ ) { if ( parents[j] === a[i] ) { loop = true;// dont rewalk array } } if ( !loop && !innerEquiv(a[i], b[i]) ) { parents.pop(); return false; } } parents.pop(); return true; }, "object": function( b, a ) { var i, j, loop, // Default to true eq = true, aProperties = [], bProperties = []; // comparing constructors is more strict than using // instanceof if ( a.constructor !== b.constructor ) { // Allow objects with no prototype to be equivalent to // objects with Object as their constructor. if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { return false; } } // stack constructor before traversing properties callers.push( a.constructor ); // track reference to avoid circular references parents.push( a ); for ( i in a ) { // be strict: don't ensures hasOwnProperty // and go deep loop = false; for ( j = 0; j < parents.length; j++ ) { if ( parents[j] === a[i] ) { // don't go down the same path twice loop = true; } } aProperties.push(i); // collect a's properties if (!loop && !innerEquiv( a[i], b[i] ) ) { eq = false; break; } } callers.pop(); // unstack, we are done parents.pop(); for ( i in b ) { bProperties.push( i ); // collect b's properties } // Ensures identical properties name return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); } }; }()); innerEquiv = function() { // can take multiple arguments var args = [].slice.apply( arguments ); if ( args.length < 2 ) { return true; // end transition } return (function( a, b ) { if ( a === b ) { return true; // catch the most you can } else if ( a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b) ) { return false; // don't lose time with error prone cases } else { return bindCallbacks(a, callbacks, [ b, a ]); } // apply transition with (1..n) arguments }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) ); }; return innerEquiv; }()); /** * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | * http://flesler.blogspot.com Licensed under BSD * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 * * @projectDescription Advanced and extensible data dumping for Javascript. * @version 1.0.0 * @author Ariel Flesler * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} */ QUnit.jsDump = (function() { function quote( str ) { return '"' + str.toString().replace( /"/g, '\\"' ) + '"'; } function literal( o ) { return o + ""; } function join( pre, arr, post ) { var s = jsDump.separator(), base = jsDump.indent(), inner = jsDump.indent(1); if ( arr.join ) { arr = arr.join( "," + s + inner ); } if ( !arr ) { return pre + post; } return [ pre, inner + arr, base + post ].join(s); } function array( arr, stack ) { var i = arr.length, ret = new Array(i); this.up(); while ( i-- ) { ret[i] = this.parse( arr[i] , undefined , stack); } this.down(); return join( "[", ret, "]" ); } var reName = /^function (\w+)/, jsDump = { // type is used mostly internally, you can fix a (custom)type in advance parse: function( obj, type, stack ) { stack = stack || [ ]; var inStack, res, parser = this.parsers[ type || this.typeOf(obj) ]; type = typeof parser; inStack = inArray( obj, stack ); if ( inStack !== -1 ) { return "recursion(" + (inStack - stack.length) + ")"; } if ( type === "function" ) { stack.push( obj ); res = parser.call( this, obj, stack ); stack.pop(); return res; } return ( type === "string" ) ? parser : this.parsers.error; }, typeOf: function( obj ) { var type; if ( obj === null ) { type = "null"; } else if ( typeof obj === "undefined" ) { type = "undefined"; } else if ( QUnit.is( "regexp", obj) ) { type = "regexp"; } else if ( QUnit.is( "date", obj) ) { type = "date"; } else if ( QUnit.is( "function", obj) ) { type = "function"; } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { type = "window"; } else if ( obj.nodeType === 9 ) { type = "document"; } else if ( obj.nodeType ) { type = "node"; } else if ( // native arrays toString.call( obj ) === "[object Array]" || // NodeList objects ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) ) { type = "array"; } else if ( obj.constructor === Error.prototype.constructor ) { type = "error"; } else { type = typeof obj; } return type; }, separator: function() { return this.multiline ? this.HTML ? "
    " : "\n" : this.HTML ? " " : " "; }, // extra can be a number, shortcut for increasing-calling-decreasing indent: function( extra ) { if ( !this.multiline ) { return ""; } var chr = this.indentChar; if ( this.HTML ) { chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); } return new Array( this._depth_ + (extra||0) ).join(chr); }, up: function( a ) { this._depth_ += a || 1; }, down: function( a ) { this._depth_ -= a || 1; }, setParser: function( name, parser ) { this.parsers[name] = parser; }, // The next 3 are exposed so you can use them quote: quote, literal: literal, join: join, // _depth_: 1, // This is the list of parsers, to modify them, use jsDump.setParser parsers: { window: "[Window]", document: "[Document]", error: function(error) { return "Error(\"" + error.message + "\")"; }, unknown: "[Unknown]", "null": "null", "undefined": "undefined", "function": function( fn ) { var ret = "function", // functions never have name in IE name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; if ( name ) { ret += " " + name; } ret += "( "; ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); }, array: array, nodelist: array, "arguments": array, object: function( map, stack ) { var ret = [ ], keys, key, val, i; QUnit.jsDump.up(); keys = []; for ( key in map ) { keys.push( key ); } keys.sort(); for ( i = 0; i < keys.length; i++ ) { key = keys[ i ]; val = map[ key ]; ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); } QUnit.jsDump.down(); return join( "{", ret, "}" ); }, node: function( node ) { var len, i, val, open = QUnit.jsDump.HTML ? "<" : "<", close = QUnit.jsDump.HTML ? ">" : ">", tag = node.nodeName.toLowerCase(), ret = open + tag, attrs = node.attributes; if ( attrs ) { for ( i = 0, len = attrs.length; i < len; i++ ) { val = attrs[i].nodeValue; // IE6 includes all attributes in .attributes, even ones not explicitly set. // Those have values like undefined, null, 0, false, "" or "inherit". if ( val && val !== "inherit" ) { ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" ); } } } ret += close; // Show content of TextNode or CDATASection if ( node.nodeType === 3 || node.nodeType === 4 ) { ret += node.nodeValue; } return ret + open + "/" + tag + close; }, // function calls it internally, it's the arguments part of the function functionArgs: function( fn ) { var args, l = fn.length; if ( !l ) { return ""; } args = new Array(l); while ( l-- ) { // 97 is 'a' args[l] = String.fromCharCode(97+l); } return " " + args.join( ", " ) + " "; }, // object calls it internally, the key part of an item in a map key: quote, // function calls it internally, it's the content of the function functionCode: "[code]", // node calls it internally, it's an html attribute value attribute: quote, string: quote, date: quote, regexp: literal, number: literal, "boolean": literal }, // if true, entities are escaped ( <, >, \t, space and \n ) HTML: false, // indentation unit indentChar: " ", // if true, items in a collection, are separated by a \n, else just a space. multiline: true }; return jsDump; }()); // from jquery.js function inArray( elem, array ) { if ( array.indexOf ) { return array.indexOf( elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[ i ] === elem ) { return i; } } return -1; } /* * Javascript Diff Algorithm * By John Resig (http://ejohn.org/) * Modified by Chu Alan "sprite" * * Released under the MIT license. * * More Info: * http://ejohn.org/projects/javascript-diff-algorithm/ * * Usage: QUnit.diff(expected, actual) * * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" */ QUnit.diff = (function() { /*jshint eqeqeq:false, eqnull:true */ function diff( o, n ) { var i, ns = {}, os = {}; for ( i = 0; i < n.length; i++ ) { if ( !hasOwn.call( ns, n[i] ) ) { ns[ n[i] ] = { rows: [], o: null }; } ns[ n[i] ].rows.push( i ); } for ( i = 0; i < o.length; i++ ) { if ( !hasOwn.call( os, o[i] ) ) { os[ o[i] ] = { rows: [], n: null }; } os[ o[i] ].rows.push( i ); } for ( i in ns ) { if ( !hasOwn.call( ns, i ) ) { continue; } if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) { n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] }; o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] }; } } for ( i = 0; i < n.length - 1; i++ ) { if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && n[ i + 1 ] == o[ n[i].row + 1 ] ) { n[ i + 1 ] = { text: n[ i + 1 ], row: n[i].row + 1 }; o[ n[i].row + 1 ] = { text: o[ n[i].row + 1 ], row: i + 1 }; } } for ( i = n.length - 1; i > 0; i-- ) { if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && n[ i - 1 ] == o[ n[i].row - 1 ]) { n[ i - 1 ] = { text: n[ i - 1 ], row: n[i].row - 1 }; o[ n[i].row - 1 ] = { text: o[ n[i].row - 1 ], row: i - 1 }; } } return { o: o, n: n }; } return function( o, n ) { o = o.replace( /\s+$/, "" ); n = n.replace( /\s+$/, "" ); var i, pre, str = "", out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), oSpace = o.match(/\s+/g), nSpace = n.match(/\s+/g); if ( oSpace == null ) { oSpace = [ " " ]; } else { oSpace.push( " " ); } if ( nSpace == null ) { nSpace = [ " " ]; } else { nSpace.push( " " ); } if ( out.n.length === 0 ) { for ( i = 0; i < out.o.length; i++ ) { str += "" + out.o[i] + oSpace[i] + ""; } } else { if ( out.n[0].text == null ) { for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { str += "" + out.o[n] + oSpace[n] + ""; } } for ( i = 0; i < out.n.length; i++ ) { if (out.n[i].text == null) { str += "" + out.n[i] + nSpace[i] + ""; } else { // `pre` initialized at top of scope pre = ""; for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { pre += "" + out.o[n] + oSpace[n] + ""; } str += " " + out.n[i].text + nSpace[i] + pre; } } } return str; }; }()); // for CommonJS enviroments, export everything if ( typeof exports !== "undefined" ) { extend( exports, QUnit ); } // get at whatever the global object is, like window in browsers }( (function() {return this;}.call()) )); lodash-2.4.1/vendor/qunit/qunit/qunit.css0000644000175000017500000001107412247303154016605 0ustar ovdovd/** * QUnit v1.11.0 - A JavaScript Unit Testing Framework * * http://qunitjs.com * * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license */ /** Font Family and Sizes */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; } #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } #qunit-tests { font-size: smaller; } /** Resets */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { margin: 0; padding: 0; } /** Header */ #qunit-header { padding: 0.5em 0 0.5em 1em; color: #8699a4; background-color: #0d3349; font-size: 1.5em; line-height: 1em; font-weight: normal; border-radius: 5px 5px 0 0; -moz-border-radius: 5px 5px 0 0; -webkit-border-top-right-radius: 5px; -webkit-border-top-left-radius: 5px; } #qunit-header a { text-decoration: none; color: #c2ccd1; } #qunit-header a:hover, #qunit-header a:focus { color: #fff; } #qunit-testrunner-toolbar label { display: inline-block; padding: 0 .5em 0 .1em; } #qunit-banner { height: 5px; } #qunit-testrunner-toolbar { padding: 0.5em 0 0.5em 2em; color: #5E740B; background-color: #eee; overflow: hidden; } #qunit-userAgent { padding: 0.5em 0 0.5em 2.5em; background-color: #2b81af; color: #fff; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } #qunit-modulefilter-container { float: right; } /** Tests: Pass/Fail */ #qunit-tests { list-style-position: inside; } #qunit-tests li { padding: 0.4em 0.5em 0.4em 2.5em; border-bottom: 1px solid #fff; list-style-position: inside; } #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { display: none; } #qunit-tests li strong { cursor: pointer; } #qunit-tests li a { padding: 0.5em; color: #c2ccd1; text-decoration: none; } #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; } #qunit-tests li .runtime { float: right; font-size: smaller; } .qunit-assert-list { margin-top: 0.5em; padding: 0.5em; background-color: #fff; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; } .qunit-collapsed { display: none; } #qunit-tests table { border-collapse: collapse; margin-top: .2em; } #qunit-tests th { text-align: right; vertical-align: top; padding: 0 .5em 0 0; } #qunit-tests td { vertical-align: top; } #qunit-tests pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; } #qunit-tests del { background-color: #e0f2be; color: #374e0c; text-decoration: none; } #qunit-tests ins { background-color: #ffcaca; color: #500; text-decoration: none; } /*** Test Counts */ #qunit-tests b.counts { color: black; } #qunit-tests b.passed { color: #5E740B; } #qunit-tests b.failed { color: #710909; } #qunit-tests li li { padding: 5px; background-color: #fff; border-bottom: none; list-style-position: inside; } /*** Passing Styles */ #qunit-tests li li.pass { color: #3c510c; background-color: #fff; border-left: 10px solid #C6E746; } #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass .test-name { color: #366097; } #qunit-tests .pass .test-actual, #qunit-tests .pass .test-expected { color: #999999; } #qunit-banner.qunit-pass { background-color: #C6E746; } /*** Failing Styles */ #qunit-tests li li.fail { color: #710909; background-color: #fff; border-left: 10px solid #EE5757; white-space: pre; } #qunit-tests > li:last-child { border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; } #qunit-tests .fail { color: #000000; background-color: #EE5757; } #qunit-tests .fail .test-name, #qunit-tests .fail .module-name { color: #000000; } #qunit-tests .fail .test-actual { color: #EE5757; } #qunit-tests .fail .test-expected { color: green; } #qunit-banner.qunit-fail { background-color: #EE5757; } /** Result */ #qunit-testresult { padding: 0.5em 0.5em 0.5em 2.5em; color: #2b81af; background-color: #D2E0E6; border-bottom: 1px solid white; } #qunit-testresult .module-name { font-weight: bold; } /** Fixture */ #qunit-fixture { position: absolute; top: -10000px; left: -10000px; width: 1000px; height: 1000px; } lodash-2.4.1/vendor/qunit/MIT-LICENSE.txt0000644000175000017500000000211312247303154016037 0ustar ovdovdCopyright 2013 jQuery Foundation and other contributors http://jquery.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lodash-2.4.1/vendor/benchmark.js/0000755000175000017500000000000012247303154014775 5ustar ovdovdlodash-2.4.1/vendor/benchmark.js/benchmark.js0000644000175000017500000025101412247303154017270 0ustar ovdovd/*! * Benchmark.js v1.0.0 * Copyright 2010-2013 Mathias Bynens * Based on JSLitmus.js, copyright Robert Kieffer * Modified by John-David Dalton * Available under MIT license */ ;(function(root, undefined) { 'use strict'; /** Detect free variable `define` */ var freeDefine = typeof define == 'function' && typeof define.amd == 'object' && define.amd && define; /** Detect free variable `exports` */ var freeExports = typeof exports == 'object' && exports; /** Detect free variable `module` */ var freeModule = typeof module == 'object' && module && module.exports == freeExports && module; /** Detect free variable `require` */ var freeRequire = typeof require == 'function' && require; /** Detect free variable `global`, from Node.js or Browserified code, and use it as `root` */ var freeGlobal = typeof global == 'object' && global; if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { root = freeGlobal; } /** Used to assign each benchmark an incrimented id */ var counter = 0; /** Used to make every compiled test unique */ var uidCounter = 0; /** Used to detect primitive types */ var rePrimitive = /^(?:boolean|number|string|undefined)$/; /** Used to assign default `context` object properties */ var contextProps = [ 'Array', 'Date', 'Function', 'Math', 'Object', 'RegExp', 'String', '_', 'clearTimeout', 'chrome', 'chromium', 'document', 'java', 'navigator', 'performance', 'platform', 'process', 'runtime', 'setTimeout' ]; /** Used to avoid hz of Infinity */ var divisors = { '1': 4096, '2': 512, '3': 64, '4': 8, '5': 0 }; /** * T-Distribution two-tailed critical values for 95% confidence * http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm */ var tTable = { '1': 12.706,'2': 4.303, '3': 3.182, '4': 2.776, '5': 2.571, '6': 2.447, '7': 2.365, '8': 2.306, '9': 2.262, '10': 2.228, '11': 2.201, '12': 2.179, '13': 2.16, '14': 2.145, '15': 2.131, '16': 2.12, '17': 2.11, '18': 2.101, '19': 2.093, '20': 2.086, '21': 2.08, '22': 2.074, '23': 2.069, '24': 2.064, '25': 2.06, '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042, 'infinity': 1.96 }; /** * Critical Mann-Whitney U-values for 95% confidence * http://www.saburchill.com/IBbiology/stats/003.html */ var uTable = { '5': [0, 1, 2], '6': [1, 2, 3, 5], '7': [1, 3, 5, 6, 8], '8': [2, 4, 6, 8, 10, 13], '9': [2, 4, 7, 10, 12, 15, 17], '10': [3, 5, 8, 11, 14, 17, 20, 23], '11': [3, 6, 9, 13, 16, 19, 23, 26, 30], '12': [4, 7, 11, 14, 18, 22, 26, 29, 33, 37], '13': [4, 8, 12, 16, 20, 24, 28, 33, 37, 41, 45], '14': [5, 9, 13, 17, 22, 26, 31, 36, 40, 45, 50, 55], '15': [5, 10, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64], '16': [6, 11, 15, 21, 26, 31, 37, 42, 47, 53, 59, 64, 70, 75], '17': [6, 11, 17, 22, 28, 34, 39, 45, 51, 57, 63, 67, 75, 81, 87], '18': [7, 12, 18, 24, 30, 36, 42, 48, 55, 61, 67, 74, 80, 86, 93, 99], '19': [7, 13, 19, 25, 32, 38, 45, 52, 58, 65, 72, 78, 85, 92, 99, 106, 113], '20': [8, 14, 20, 27, 34, 41, 48, 55, 62, 69, 76, 83, 90, 98, 105, 112, 119, 127], '21': [8, 15, 22, 29, 36, 43, 50, 58, 65, 73, 80, 88, 96, 103, 111, 119, 126, 134, 142], '22': [9, 16, 23, 30, 38, 45, 53, 61, 69, 77, 85, 93, 101, 109, 117, 125, 133, 141, 150, 158], '23': [9, 17, 24, 32, 40, 48, 56, 64, 73, 81, 89, 98, 106, 115, 123, 132, 140, 149, 157, 166, 175], '24': [10, 17, 25, 33, 42, 50, 59, 67, 76, 85, 94, 102, 111, 120, 129, 138, 147, 156, 165, 174, 183, 192], '25': [10, 18, 27, 35, 44, 53, 62, 71, 80, 89, 98, 107, 117, 126, 135, 145, 154, 163, 173, 182, 192, 201, 211], '26': [11, 19, 28, 37, 46, 55, 64, 74, 83, 93, 102, 112, 122, 132, 141, 151, 161, 171, 181, 191, 200, 210, 220, 230], '27': [11, 20, 29, 38, 48, 57, 67, 77, 87, 97, 107, 118, 125, 138, 147, 158, 168, 178, 188, 199, 209, 219, 230, 240, 250], '28': [12, 21, 30, 40, 50, 60, 70, 80, 90, 101, 111, 122, 132, 143, 154, 164, 175, 186, 196, 207, 218, 228, 239, 250, 261, 272], '29': [13, 22, 32, 42, 52, 62, 73, 83, 94, 105, 116, 127, 138, 149, 160, 171, 182, 193, 204, 215, 226, 238, 249, 260, 271, 282, 294], '30': [13, 23, 33, 43, 54, 65, 76, 87, 98, 109, 120, 131, 143, 154, 166, 177, 189, 200, 212, 223, 235, 247, 258, 270, 282, 293, 305, 317] }; /*--------------------------------------------------------------------------*/ /** * Create a new `Benchmark` function using the given `context` object. * * @static * @memberOf Benchmark * @param {Object} [context=root] The context object. * @returns {Function} Returns the `Benchmark` function. */ function runInContext(context) { // exit early if unable to acquire lodash var _ = context && context._ || req('lodash') || root._; if (!_) { Benchmark.runInContext = runInContext; return Benchmark; } // Avoid issues with some ES3 environments that attempt to use values, named // after built-in constructors like `Object`, for the creation of literals. // ES5 clears this up by stating that literals must use built-in constructors. // See http://es5.github.io/#x11.1.5. context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; /** Native constructor references */ var Array = context.Array, Date = context.Date, Function = context.Function, Math = context.Math, Object = context.Object, RegExp = context.RegExp, String = context.String; /** Used for `Array` and `Object` method references */ var arrayRef = [], objectProto = Object.prototype; /** Native method shortcuts */ var abs = Math.abs, clearTimeout = context.clearTimeout, floor = Math.floor, log = Math.log, max = Math.max, min = Math.min, pow = Math.pow, push = arrayRef.push, setTimeout = context.setTimeout, shift = arrayRef.shift, slice = arrayRef.slice, sqrt = Math.sqrt, toString = objectProto.toString; /** Detect DOM document object */ var doc = isHostType(context, 'document') && context.document; /** Used to access Wade Simmons' Node microtime module */ var microtimeObject = req('microtime'); /** Used to access the browser's high resolution timer */ var perfObject = isHostType(context, 'performance') && context.performance; /** Used to call the browser's high resolution timer */ var perfName = perfObject && ( perfObject.now && 'now' || perfObject.webkitNow && 'webkitNow' ); /** Used to access Node's high resolution timer */ var processObject = isHostType(context, 'process') && context.process; /** Used to prevent a `removeChild` memory leak in IE < 9 */ var trash = doc && doc.createElement('div'); /** Used to integrity check compiled tests */ var uid = 'uid' + (+new Date); /** Used to avoid infinite recursion when methods call each other */ var calledBy = {}; /** * An object used to flag environments/features. * * @static * @memberOf Benchmark * @type Object */ var support = {}; (function() { /** * Detect Adobe AIR. * * @memberOf Benchmark.support * @type boolean */ support.air = isClassOf(context.runtime, 'ScriptBridgingProxyObject'); /** * Detect if in a browser environment. * * @memberOf Benchmark.support * @type boolean */ support.browser = doc && isHostType(context, 'navigator') && !isHostType(context, 'phantom'); /** * Detect if Java is enabled/exposed. * * @memberOf Benchmark.support * @type boolean */ support.java = isClassOf(context.java, 'JavaPackage'); /** * Detect if the Timers API exists. * * @memberOf Benchmark.support * @type boolean */ support.timeout = isHostType(context, 'setTimeout') && isHostType(context, 'clearTimeout'); /** * Detect if functions support decompilation. * * @name decompilation * @memberOf Benchmark.support * @type boolean */ try { // Safari 2.x removes commas in object literals // from Function#toString results // http://webk.it/11609 // Firefox 3.6 and Opera 9.25 strip grouping // parentheses from Function#toString results // http://bugzil.la/559438 support.decompilation = Function( 'return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }) + ')' )()(0).x === '1'; } catch(e) { support.decompilation = false; } }()); /** * Timer object used by `clock()` and `Deferred#resolve`. * * @private * @type Object */ var timer = { /** * The timer namespace object or constructor. * * @private * @memberOf timer * @type {Function|Object} */ 'ns': Date, /** * Starts the deferred timer. * * @private * @memberOf timer * @param {Object} deferred The deferred instance. */ 'start': null, // lazy defined in `clock()` /** * Stops the deferred timer. * * @private * @memberOf timer * @param {Object} deferred The deferred instance. */ 'stop': null // lazy defined in `clock()` }; /*------------------------------------------------------------------------*/ /** * The Benchmark constructor. * * @constructor * @param {string} name A name to identify the benchmark. * @param {Function|string} fn The test to benchmark. * @param {Object} [options={}] Options object. * @example * * // basic usage (the `new` operator is optional) * var bench = new Benchmark(fn); * * // or using a name first * var bench = new Benchmark('foo', fn); * * // or with options * var bench = new Benchmark('foo', fn, { * * // displayed by Benchmark#toString if `name` is not available * 'id': 'xyz', * * // called when the benchmark starts running * 'onStart': onStart, * * // called after each run cycle * 'onCycle': onCycle, * * // called when aborted * 'onAbort': onAbort, * * // called when a test errors * 'onError': onError, * * // called when reset * 'onReset': onReset, * * // called when the benchmark completes running * 'onComplete': onComplete, * * // compiled/called before the test loop * 'setup': setup, * * // compiled/called after the test loop * 'teardown': teardown * }); * * // or name and options * var bench = new Benchmark('foo', { * * // a flag to indicate the benchmark is deferred * 'defer': true, * * // benchmark test function * 'fn': function(deferred) { * // call resolve() when the deferred test is finished * deferred.resolve(); * } * }); * * // or options only * var bench = new Benchmark({ * * // benchmark name * 'name': 'foo', * * // benchmark test as a string * 'fn': '[1,2,3,4].sort()' * }); * * // a test's `this` binding is set to the benchmark instance * var bench = new Benchmark('foo', function() { * 'My name is '.concat(this.name); // My name is foo * }); */ function Benchmark(name, fn, options) { var bench = this; // allow instance creation without the `new` operator if (bench == null || bench.constructor != Benchmark) { return new Benchmark(name, fn, options); } // juggle arguments if (_.isPlainObject(name)) { // 1 argument (options) options = name; } else if (_.isFunction(name)) { // 2 arguments (fn, options) options = fn; fn = name; } else if (_.isPlainObject(fn)) { // 2 arguments (name, options) options = fn; fn = null; bench.name = name; } else { // 3 arguments (name, fn [, options]) bench.name = name; } setOptions(bench, options); bench.id || (bench.id = ++counter); bench.fn == null && (bench.fn = fn); bench.stats = cloneDeep(bench.stats); bench.times = cloneDeep(bench.times); } /** * The Deferred constructor. * * @constructor * @memberOf Benchmark * @param {Object} clone The cloned benchmark instance. */ function Deferred(clone) { var deferred = this; if (deferred == null || deferred.constructor != Deferred) { return new Deferred(clone); } deferred.benchmark = clone; clock(deferred); } /** * The Event constructor. * * @constructor * @memberOf Benchmark * @param {Object|string} type The event type. */ function Event(type) { var event = this; return (event == null || event.constructor != Event) ? new Event(type) : (type instanceof Event ? type : _.extend(event, { 'timeStamp': +new Date }, typeof type == 'string' ? { 'type': type } : type) ); } /** * The Suite constructor. * * @constructor * @memberOf Benchmark * @param {string} name A name to identify the suite. * @param {Object} [options={}] Options object. * @example * * // basic usage (the `new` operator is optional) * var suite = new Benchmark.Suite; * * // or using a name first * var suite = new Benchmark.Suite('foo'); * * // or with options * var suite = new Benchmark.Suite('foo', { * * // called when the suite starts running * 'onStart': onStart, * * // called between running benchmarks * 'onCycle': onCycle, * * // called when aborted * 'onAbort': onAbort, * * // called when a test errors * 'onError': onError, * * // called when reset * 'onReset': onReset, * * // called when the suite completes running * 'onComplete': onComplete * }); */ function Suite(name, options) { var suite = this; // allow instance creation without the `new` operator if (suite == null || suite.constructor != Suite) { return new Suite(name, options); } // juggle arguments if (isClassOf(name, 'Object')) { // 1 argument (options) options = name; } else { // 2 arguments (name [, options]) suite.name = name; } setOptions(suite, options); } /*------------------------------------------------------------------------*/ /** * A deep clone utility. * * @private * @param {*} value The value to clone. * @returns {*} The cloned value. */ var cloneDeep = _.partialRight(_.cloneDeep, function(value) { // do not clone non-Object objects return (typeof value == 'object' && !_.isArray(value) && !_.isPlainObject(value)) ? value : undefined; }); /** * Creates a function from the given arguments string and body. * * @private * @param {string} args The comma separated function arguments. * @param {string} body The function body. * @returns {Function} The new function. */ function createFunction() { // lazy define createFunction = function(args, body) { var result, anchor = freeDefine ? freeDefine.amd : Benchmark, prop = uid + 'createFunction'; runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(' + args + '){' + body + '}'); result = anchor[prop]; delete anchor[prop]; return result; }; // fix JaegerMonkey bug // http://bugzil.la/639720 createFunction = support.browser && (createFunction('', 'return"' + uid + '"') || noop)() == uid ? createFunction : Function; return createFunction.apply(null, arguments); } /** * Delay the execution of a function based on the benchmark's `delay` property. * * @private * @param {Object} bench The benchmark instance. * @param {Object} fn The function to execute. */ function delay(bench, fn) { bench._timerId = _.delay(fn, bench.delay * 1e3); } /** * Destroys the given element. * * @private * @param {Element} element The element to destroy. */ function destroyElement(element) { trash.appendChild(element); trash.innerHTML = ''; } /** * Gets the name of the first argument from a function's source. * * @private * @param {Function} fn The function. * @returns {string} The argument name. */ function getFirstArgument(fn) { return (!_.has(fn, 'toString') && (/^[\s(]*function[^(]*\(([^\s,)]+)/.exec(fn) || 0)[1]) || ''; } /** * Computes the arithmetic mean of a sample. * * @private * @param {Array} sample The sample. * @returns {number} The mean. */ function getMean(sample) { return (_.reduce(sample, function(sum, x) { return sum + x; }) / sample.length) || 0; } /** * Gets the source code of a function. * * @private * @param {Function} fn The function. * @param {string} altSource A string used when a function's source code is unretrievable. * @returns {string} The function's source code. */ function getSource(fn, altSource) { var result = altSource; if (isStringable(fn)) { result = String(fn); } else if (support.decompilation) { // escape the `{` for Firefox 1 result = (/^[^{]+\{([\s\S]*)\}\s*$/.exec(fn) || 0)[1]; } // trim string result = (result || '').replace(/^\s+|\s+$/g, ''); // detect strings containing only the "use strict" directive return /^(?:\/\*+[\w|\W]*?\*\/|\/\/.*?[\n\r\u2028\u2029]|\s)*(["'])use strict\1;?$/.test(result) ? '' : result; } /** * Checks if an object is of the specified class. * * @private * @param {*} value The value to check. * @param {string} name The name of the class. * @returns {boolean} Returns `true` if the value is of the specified class, else `false`. */ function isClassOf(value, name) { return value != null && toString.call(value) == '[object ' + name + ']'; } /** * Host objects can return type values that are different from their actual * data type. The objects we are concerned with usually return non-primitive * types of "object", "function", or "unknown". * * @private * @param {*} object The owner of the property. * @param {string} property The property to check. * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`. */ function isHostType(object, property) { if (object == null) { return false; } var type = typeof object[property]; return !rePrimitive.test(type) && (type != 'object' || !!object[property]); } /** * Checks if a value can be safely coerced to a string. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if the value can be coerced, else `false`. */ function isStringable(value) { return _.has(value, 'toString') || isClassOf(value, 'String'); } /** * A no-operation function. * * @private */ function noop() { // no operation performed } /** * A wrapper around require() to suppress `module missing` errors. * * @private * @param {string} id The module id. * @returns {*} The exported module or `null`. */ function req(id) { try { var result = freeExports && freeRequire(id); } catch(e) { } return result || null; } /** * Runs a snippet of JavaScript via script injection. * * @private * @param {string} code The code to run. */ function runScript(code) { var anchor = freeDefine ? define.amd : Benchmark, script = doc.createElement('script'), sibling = doc.getElementsByTagName('script')[0], parent = sibling.parentNode, prop = uid + 'runScript', prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||function(){})();'; // Firefox 2.0.0.2 cannot use script injection as intended because it executes // asynchronously, but that's OK because script injection is only used to avoid // the previously commented JaegerMonkey bug. try { // remove the inserted script *before* running the code to avoid differences // in the expected script element count/order of the document. script.appendChild(doc.createTextNode(prefix + code)); anchor[prop] = function() { destroyElement(script); }; } catch(e) { parent = parent.cloneNode(false); sibling = null; script.text = code; } parent.insertBefore(script, sibling); delete anchor[prop]; } /** * A helper function for setting options/event handlers. * * @private * @param {Object} object The benchmark or suite instance. * @param {Object} [options={}] Options object. */ function setOptions(object, options) { options = _.extend({}, object.constructor.options, options); object.options = _.forOwn(options, function(value, key) { if (value != null) { // add event listeners if (/^on[A-Z]/.test(key)) { _.each(key.split(' '), function(key) { object.on(key.slice(2).toLowerCase(), value); }); } else if (!_.has(object, key)) { object[key] = cloneDeep(value); } } }); } /*------------------------------------------------------------------------*/ /** * Handles cycling/completing the deferred benchmark. * * @memberOf Benchmark.Deferred */ function resolve() { var deferred = this, clone = deferred.benchmark, bench = clone._original; if (bench.aborted) { // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete deferred.teardown(); clone.running = false; cycle(deferred); } else if (++deferred.cycles < clone.count) { clone.compiled.call(deferred, context, timer); } else { timer.stop(deferred); deferred.teardown(); delay(clone, function() { cycle(deferred); }); } } /*------------------------------------------------------------------------*/ /** * A generic `Array#filter` like method. * * @static * @memberOf Benchmark * @param {Array} array The array to iterate over. * @param {Function|string} callback The function/alias called per iteration. * @param {*} thisArg The `this` binding for the callback. * @returns {Array} A new array of values that passed callback filter. * @example * * // get odd numbers * Benchmark.filter([1, 2, 3, 4, 5], function(n) { * return n % 2; * }); // -> [1, 3, 5]; * * // get fastest benchmarks * Benchmark.filter(benches, 'fastest'); * * // get slowest benchmarks * Benchmark.filter(benches, 'slowest'); * * // get benchmarks that completed without erroring * Benchmark.filter(benches, 'successful'); */ function filter(array, callback, thisArg) { if (callback === 'successful') { // callback to exclude those that are errored, unrun, or have hz of Infinity callback = function(bench) { return bench.cycles && _.isFinite(bench.hz); }; } else if (callback === 'fastest' || callback === 'slowest') { // get successful, sort by period + margin of error, and filter fastest/slowest var result = filter(array, 'successful').sort(function(a, b) { a = a.stats; b = b.stats; return (a.mean + a.moe > b.mean + b.moe ? 1 : -1) * (callback === 'fastest' ? 1 : -1); }); return _.filter(result, function(bench) { return result[0].compare(bench) == 0; }); } return _.filter(array, callback, thisArg); } /** * Converts a number to a more readable comma-separated string representation. * * @static * @memberOf Benchmark * @param {number} number The number to convert. * @returns {string} The more readable string representation. */ function formatNumber(number) { number = String(number).split('.'); return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') + (number[1] ? '.' + number[1] : ''); } /** * Invokes a method on all items in an array. * * @static * @memberOf Benchmark * @param {Array} benches Array of benchmarks to iterate over. * @param {Object|string} name The name of the method to invoke OR options object. * @param {...*} [arg] Arguments to invoke the method with. * @returns {Array} A new array of values returned from each method invoked. * @example * * // invoke `reset` on all benchmarks * Benchmark.invoke(benches, 'reset'); * * // invoke `emit` with arguments * Benchmark.invoke(benches, 'emit', 'complete', listener); * * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks * Benchmark.invoke(benches, { * * // invoke the `run` method * 'name': 'run', * * // pass a single argument * 'args': true, * * // treat as queue, removing benchmarks from front of `benches` until empty * 'queued': true, * * // called before any benchmarks have been invoked. * 'onStart': onStart, * * // called between invoking benchmarks * 'onCycle': onCycle, * * // called after all benchmarks have been invoked. * 'onComplete': onComplete * }); */ function invoke(benches, name) { var args, bench, queued, index = -1, eventProps = { 'currentTarget': benches }, options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop }, result = _.toArray(benches); /** * Invokes the method of the current object and if synchronous, fetches the next. */ function execute() { var listeners, async = isAsync(bench); if (async) { // use `getNext` as the first listener bench.on('complete', getNext); listeners = bench.events.complete; listeners.splice(0, 0, listeners.pop()); } // execute method result[index] = _.isFunction(bench && bench[name]) ? bench[name].apply(bench, args) : undefined; // if synchronous return true until finished return !async && getNext(); } /** * Fetches the next bench or executes `onComplete` callback. */ function getNext(event) { var cycleEvent, last = bench, async = isAsync(last); if (async) { last.off('complete', getNext); last.emit('complete'); } // emit "cycle" event eventProps.type = 'cycle'; eventProps.target = last; cycleEvent = Event(eventProps); options.onCycle.call(benches, cycleEvent); // choose next benchmark if not exiting early if (!cycleEvent.aborted && raiseIndex() !== false) { bench = queued ? benches[0] : result[index]; if (isAsync(bench)) { delay(bench, execute); } else if (async) { // resume execution if previously asynchronous but now synchronous while (execute()) { } } else { // continue synchronous execution return true; } } else { // emit "complete" event eventProps.type = 'complete'; options.onComplete.call(benches, Event(eventProps)); } // When used as a listener `event.aborted = true` will cancel the rest of // the "complete" listeners because they were already called above and when // used as part of `getNext` the `return false` will exit the execution while-loop. if (event) { event.aborted = true; } else { return false; } } /** * Checks if invoking `Benchmark#run` with asynchronous cycles. */ function isAsync(object) { // avoid using `instanceof` here because of IE memory leak issues with host objects var async = args[0] && args[0].async; return Object(object).constructor == Benchmark && name == 'run' && ((async == null ? object.options.async : async) && support.timeout || object.defer); } /** * Raises `index` to the next defined index or returns `false`. */ function raiseIndex() { index++; // if queued remove the previous bench if (queued && index > 0) { shift.call(benches); } // if we reached the last index then return `false` return (queued ? benches.length : index < result.length) ? index : (index = false); } // juggle arguments if (_.isString(name)) { // 2 arguments (array, name) args = slice.call(arguments, 2); } else { // 2 arguments (array, options) options = _.extend(options, name); name = options.name; args = _.isArray(args = 'args' in options ? options.args : []) ? args : [args]; queued = options.queued; } // start iterating over the array if (raiseIndex() !== false) { // emit "start" event bench = result[index]; eventProps.type = 'start'; eventProps.target = bench; options.onStart.call(benches, Event(eventProps)); // end early if the suite was aborted in an "onStart" listener if (benches.aborted && benches.constructor == Suite && name == 'run') { // emit "cycle" event eventProps.type = 'cycle'; options.onCycle.call(benches, Event(eventProps)); // emit "complete" event eventProps.type = 'complete'; options.onComplete.call(benches, Event(eventProps)); } // else start else { if (isAsync(bench)) { delay(bench, execute); } else { while (execute()) { } } } } return result; } /** * Creates a string of joined array values or object key-value pairs. * * @static * @memberOf Benchmark * @param {Array|Object} object The object to operate on. * @param {string} [separator1=','] The separator used between key-value pairs. * @param {string} [separator2=': '] The separator used between keys and values. * @returns {string} The joined result. */ function join(object, separator1, separator2) { var result = [], length = (object = Object(object)).length, arrayLike = length === length >>> 0; separator2 || (separator2 = ': '); _.each(object, function(value, key) { result.push(arrayLike ? value : key + separator2 + value); }); return result.join(separator1 || ','); } /*------------------------------------------------------------------------*/ /** * Aborts all benchmarks in the suite. * * @name abort * @memberOf Benchmark.Suite * @returns {Object} The suite instance. */ function abortSuite() { var event, suite = this, resetting = calledBy.resetSuite; if (suite.running) { event = Event('abort'); suite.emit(event); if (!event.cancelled || resetting) { // avoid infinite recursion calledBy.abortSuite = true; suite.reset(); delete calledBy.abortSuite; if (!resetting) { suite.aborted = true; invoke(suite, 'abort'); } } } return suite; } /** * Adds a test to the benchmark suite. * * @memberOf Benchmark.Suite * @param {string} name A name to identify the benchmark. * @param {Function|string} fn The test to benchmark. * @param {Object} [options={}] Options object. * @returns {Object} The benchmark instance. * @example * * // basic usage * suite.add(fn); * * // or using a name first * suite.add('foo', fn); * * // or with options * suite.add('foo', fn, { * 'onCycle': onCycle, * 'onComplete': onComplete * }); * * // or name and options * suite.add('foo', { * 'fn': fn, * 'onCycle': onCycle, * 'onComplete': onComplete * }); * * // or options only * suite.add({ * 'name': 'foo', * 'fn': fn, * 'onCycle': onCycle, * 'onComplete': onComplete * }); */ function add(name, fn, options) { var suite = this, bench = new Benchmark(name, fn, options), event = Event({ 'type': 'add', 'target': bench }); if (suite.emit(event), !event.cancelled) { suite.push(bench); } return suite; } /** * Creates a new suite with cloned benchmarks. * * @name clone * @memberOf Benchmark.Suite * @param {Object} options Options object to overwrite cloned options. * @returns {Object} The new suite instance. */ function cloneSuite(options) { var suite = this, result = new suite.constructor(_.extend({}, suite.options, options)); // copy own properties _.forOwn(suite, function(value, key) { if (!_.has(result, key)) { result[key] = value && _.isFunction(value.clone) ? value.clone() : cloneDeep(value); } }); return result; } /** * An `Array#filter` like method. * * @name filter * @memberOf Benchmark.Suite * @param {Function|string} callback The function/alias called per iteration. * @returns {Object} A new suite of benchmarks that passed callback filter. */ function filterSuite(callback) { var suite = this, result = new suite.constructor; result.push.apply(result, filter(suite, callback)); return result; } /** * Resets all benchmarks in the suite. * * @name reset * @memberOf Benchmark.Suite * @returns {Object} The suite instance. */ function resetSuite() { var event, suite = this, aborting = calledBy.abortSuite; if (suite.running && !aborting) { // no worries, `resetSuite()` is called within `abortSuite()` calledBy.resetSuite = true; suite.abort(); delete calledBy.resetSuite; } // reset if the state has changed else if ((suite.aborted || suite.running) && (suite.emit(event = Event('reset')), !event.cancelled)) { suite.aborted = suite.running = false; if (!aborting) { invoke(suite, 'reset'); } } return suite; } /** * Runs the suite. * * @name run * @memberOf Benchmark.Suite * @param {Object} [options={}] Options object. * @returns {Object} The suite instance. * @example * * // basic usage * suite.run(); * * // or with options * suite.run({ 'async': true, 'queued': true }); */ function runSuite(options) { var suite = this; suite.reset(); suite.running = true; options || (options = {}); invoke(suite, { 'name': 'run', 'args': options, 'queued': options.queued, 'onStart': function(event) { suite.emit(event); }, 'onCycle': function(event) { var bench = event.target; if (bench.error) { suite.emit({ 'type': 'error', 'target': bench }); } suite.emit(event); event.aborted = suite.aborted; }, 'onComplete': function(event) { suite.running = false; suite.emit(event); } }); return suite; } /*------------------------------------------------------------------------*/ /** * Executes all registered listeners of the specified event type. * * @memberOf Benchmark, Benchmark.Suite * @param {Object|string} type The event type or object. * @returns {*} Returns the return value of the last listener executed. */ function emit(type) { var listeners, object = this, event = Event(type), events = object.events, args = (arguments[0] = event, arguments); event.currentTarget || (event.currentTarget = object); event.target || (event.target = object); delete event.result; if (events && (listeners = _.has(events, event.type) && events[event.type])) { _.each(listeners.slice(), function(listener) { if ((event.result = listener.apply(object, args)) === false) { event.cancelled = true; } return !event.aborted; }); } return event.result; } /** * Returns an array of event listeners for a given type that can be manipulated * to add or remove listeners. * * @memberOf Benchmark, Benchmark.Suite * @param {string} type The event type. * @returns {Array} The listeners array. */ function listeners(type) { var object = this, events = object.events || (object.events = {}); return _.has(events, type) ? events[type] : (events[type] = []); } /** * Unregisters a listener for the specified event type(s), * or unregisters all listeners for the specified event type(s), * or unregisters all listeners for all event types. * * @memberOf Benchmark, Benchmark.Suite * @param {string} [type] The event type. * @param {Function} [listener] The function to unregister. * @returns {Object} The benchmark instance. * @example * * // unregister a listener for an event type * bench.off('cycle', listener); * * // unregister a listener for multiple event types * bench.off('start cycle', listener); * * // unregister all listeners for an event type * bench.off('cycle'); * * // unregister all listeners for multiple event types * bench.off('start cycle complete'); * * // unregister all listeners for all event types * bench.off(); */ function off(type, listener) { var object = this, events = object.events; if (!events) { return object; } _.each(type ? type.split(' ') : events, function(listeners, type) { var index; if (typeof listeners == 'string') { type = listeners; listeners = _.has(events, type) && events[type]; } if (listeners) { if (listener) { index = _.indexOf(listeners, listener); if (index > -1) { listeners.splice(index, 1); } } else { listeners.length = 0; } } }); return object; } /** * Registers a listener for the specified event type(s). * * @memberOf Benchmark, Benchmark.Suite * @param {string} type The event type. * @param {Function} listener The function to register. * @returns {Object} The benchmark instance. * @example * * // register a listener for an event type * bench.on('cycle', listener); * * // register a listener for multiple event types * bench.on('start cycle', listener); */ function on(type, listener) { var object = this, events = object.events || (object.events = {}); _.each(type.split(' '), function(type) { (_.has(events, type) ? events[type] : (events[type] = []) ).push(listener); }); return object; } /*------------------------------------------------------------------------*/ /** * Aborts the benchmark without recording times. * * @memberOf Benchmark * @returns {Object} The benchmark instance. */ function abort() { var event, bench = this, resetting = calledBy.reset; if (bench.running) { event = Event('abort'); bench.emit(event); if (!event.cancelled || resetting) { // avoid infinite recursion calledBy.abort = true; bench.reset(); delete calledBy.abort; if (support.timeout) { clearTimeout(bench._timerId); delete bench._timerId; } if (!resetting) { bench.aborted = true; bench.running = false; } } } return bench; } /** * Creates a new benchmark using the same test and options. * * @memberOf Benchmark * @param {Object} options Options object to overwrite cloned options. * @returns {Object} The new benchmark instance. * @example * * var bizarro = bench.clone({ * 'name': 'doppelganger' * }); */ function clone(options) { var bench = this, sample = bench.stats.sample, result = new bench.constructor(_.extend({}, bench, options)); // correct the `options` object result.options = _.extend({}, bench.options, options); // copy own custom properties _.forOwn(bench, function(value, key) { if (!_.has(result, key)) { result[key] = cloneDeep(value); } }); return result; } /** * Determines if a benchmark is faster than another. * * @memberOf Benchmark * @param {Object} other The benchmark to compare. * @returns {number} Returns `-1` if slower, `1` if faster, and `0` if indeterminate. */ function compare(other) { var critical, zStat, bench = this, sample1 = bench.stats.sample, sample2 = other.stats.sample, size1 = sample1.length, size2 = sample2.length, maxSize = max(size1, size2), minSize = min(size1, size2), u1 = getU(sample1, sample2), u2 = getU(sample2, sample1), u = min(u1, u2); function getScore(xA, sampleB) { return _.reduce(sampleB, function(total, xB) { return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5); }, 0); } function getU(sampleA, sampleB) { return _.reduce(sampleA, function(total, xA) { return total + getScore(xA, sampleB); }, 0); } function getZ(u) { return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2 + 1)) / 12); } // exit early if comparing the same benchmark if (bench == other) { return 0; } // reject the null hyphothesis the two samples come from the // same population (i.e. have the same median) if... if (size1 + size2 > 30) { // ...the z-stat is greater than 1.96 or less than -1.96 // http://www.statisticslectures.com/topics/mannwhitneyu/ zStat = getZ(u); return abs(zStat) > 1.96 ? (u == u1 ? 1 : -1) : 0; } // ...the U value is less than or equal the critical U value critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3]; return u <= critical ? (u == u1 ? 1 : -1) : 0; } /** * Reset properties and abort if running. * * @memberOf Benchmark * @returns {Object} The benchmark instance. */ function reset() { var data, event, bench = this, index = 0, changes = { 'length': 0 }, queue = { 'length': 0 }; if (bench.running && !calledBy.abort) { // no worries, `reset()` is called within `abort()` calledBy.reset = true; bench.abort(); delete calledBy.reset; } else { // a non-recursive solution to check if properties have changed // http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4 data = { 'destination': bench, 'source': _.extend({}, bench.constructor.prototype, bench.options) }; do { _.forOwn(data.source, function(value, key) { var changed, destination = data.destination, currValue = destination[key]; // skip pseudo private properties like `_timerId` which could be a // Java object in environments like RingoJS if (key.charAt(0) == '_') { return; } if (value && typeof value == 'object') { if (_.isArray(value)) { // check if an array value has changed to a non-array value if (!_.isArray(currValue)) { changed = currValue = []; } // or has changed its length if (currValue.length != value.length) { changed = currValue = currValue.slice(0, value.length); currValue.length = value.length; } } // check if an object has changed to a non-object value else if (!currValue || typeof currValue != 'object') { changed = currValue = {}; } // register a changed object if (changed) { changes[changes.length++] = { 'destination': destination, 'key': key, 'value': currValue }; } queue[queue.length++] = { 'destination': currValue, 'source': value }; } // register a changed primitive else if (value !== currValue && !(value == null || _.isFunction(value))) { changes[changes.length++] = { 'destination': destination, 'key': key, 'value': value }; } }); } while ((data = queue[index++])); // if changed emit the `reset` event and if it isn't cancelled reset the benchmark if (changes.length && (bench.emit(event = Event('reset')), !event.cancelled)) { _.each(changes, function(data) { data.destination[data.key] = data.value; }); } } return bench; } /** * Displays relevant benchmark information when coerced to a string. * * @name toString * @memberOf Benchmark * @returns {string} A string representation of the benchmark instance. */ function toStringBench() { var bench = this, error = bench.error, hz = bench.hz, id = bench.id, stats = bench.stats, size = stats.sample.length, pm = support.java ? '+/-' : '\xb1', result = bench.name || (_.isNaN(id) ? id : ''); if (error) { result += ': ' + join(error); } else { result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm + stats.rme.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)'; } return result; } /*------------------------------------------------------------------------*/ /** * Clocks the time taken to execute a test per cycle (secs). * * @private * @param {Object} bench The benchmark instance. * @returns {number} The time taken. */ function clock() { var applet, options = Benchmark.options, templateData = {}, timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'ms' }]; // lazy define for hi-res timers clock = function(clone) { var deferred; if (clone instanceof Deferred) { deferred = clone; clone = deferred.benchmark; } var bench = clone._original, stringable = isStringable(bench.fn), count = bench.count = clone.count, decompilable = support.decompilation || stringable, id = bench.id, name = bench.name || (typeof id == 'number' ? '' : id), result = 0; // init `minTime` if needed clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime = options.minTime); // repair nanosecond timer // (some Chrome builds erase the `ns` variable after millions of executions) if (applet) { try { timer.ns.nanoTime(); } catch(e) { // use non-element to avoid issues with libs that augment them timer.ns = new applet.Packages.nano; } } // Compile in setup/teardown functions and the test loop. // Create a new compiled test, instead of using the cached `bench.compiled`, // to avoid potential engine optimizations enabled over the life of the test. var funcBody = deferred ? 'var d#=this,${fnArg}=d#,m#=d#.benchmark._original,f#=m#.fn,su#=m#.setup,td#=m#.teardown;' + // when `deferred.cycles` is `0` then... 'if(!d#.cycles){' + // set `deferred.fn` 'd#.fn=function(){var ${fnArg}=d#;if(typeof f#=="function"){try{${fn}\n}catch(e#){f#(d#)}}else{${fn}\n}};' + // set `deferred.teardown` 'd#.teardown=function(){d#.cycles=0;if(typeof td#=="function"){try{${teardown}\n}catch(e#){td#()}}else{${teardown}\n}};' + // execute the benchmark's `setup` 'if(typeof su#=="function"){try{${setup}\n}catch(e#){su#()}}else{${setup}\n};' + // start timer 't#.start(d#);' + // execute `deferred.fn` and return a dummy object '}d#.fn();return{uid:"${uid}"}' : 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count,n#=t#.ns;${setup}\n${begin};' + 'while(i#--){${fn}\n}${end};${teardown}\nreturn{elapsed:r#,uid:"${uid}"}'; var compiled = bench.compiled = clone.compiled = createCompiled(bench, deferred, funcBody), isEmpty = !(templateData.fn || stringable); try { if (isEmpty) { // Firefox may remove dead code from Function#toString results // http://bugzil.la/536085 throw new Error('The test "' + name + '" is empty. This may be the result of dead code removal.'); } else if (!deferred) { // pretest to determine if compiled code is exits early, usually by a // rogue `return` statement, by checking for a return object with the uid bench.count = 1; compiled = (compiled.call(bench, context, timer) || {}).uid == templateData.uid && compiled; bench.count = count; } } catch(e) { compiled = null; clone.error = e || new Error(String(e)); bench.count = count; } // fallback when a test exits early or errors during pretest if (decompilable && !compiled && !deferred && !isEmpty) { funcBody = ( clone.error && !stringable ? 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count' : 'function f#(){${fn}\n}var r#,s#,m#=this,i#=m#.count' ) + ',n#=t#.ns;${setup}\n${begin};m#.f#=f#;while(i#--){m#.f#()}${end};' + 'delete m#.f#;${teardown}\nreturn{elapsed:r#}'; compiled = createCompiled(bench, deferred, funcBody); try { // pretest one more time to check for errors bench.count = 1; compiled.call(bench, context, timer); bench.count = count; delete clone.error; } catch(e) { bench.count = count; if (!clone.error) { clone.error = e || new Error(String(e)); } } } // if no errors run the full test loop if (!clone.error) { compiled = bench.compiled = clone.compiled = createCompiled(bench, deferred, funcBody); result = compiled.call(deferred || bench, context, timer).elapsed; } return result; }; /*----------------------------------------------------------------------*/ /** * Creates a compiled function from the given function `body`. */ function createCompiled(bench, deferred, body) { var fn = bench.fn, fnArg = deferred ? getFirstArgument(fn) || 'deferred' : ''; templateData.uid = uid + uidCounter++; _.extend(templateData, { 'setup': getSource(bench.setup, interpolate('m#.setup()')), 'fn': getSource(fn, interpolate('m#.fn(' + fnArg + ')')), 'fnArg': fnArg, 'teardown': getSource(bench.teardown, interpolate('m#.teardown()')) }); // use API of chosen timer if (timer.unit == 'ns') { if (timer.ns.nanoTime) { _.extend(templateData, { 'begin': interpolate('s#=n#.nanoTime()'), 'end': interpolate('r#=(n#.nanoTime()-s#)/1e9') }); } else { _.extend(templateData, { 'begin': interpolate('s#=n#()'), 'end': interpolate('r#=n#(s#);r#=r#[0]+(r#[1]/1e9)') }); } } else if (timer.unit == 'us') { if (timer.ns.stop) { _.extend(templateData, { 'begin': interpolate('s#=n#.start()'), 'end': interpolate('r#=n#.microseconds()/1e6') }); } else if (perfName) { _.extend(templateData, { 'begin': interpolate('s#=n#.' + perfName + '()'), 'end': interpolate('r#=(n#.' + perfName + '()-s#)/1e3') }); } else { _.extend(templateData, { 'begin': interpolate('s#=n#()'), 'end': interpolate('r#=(n#()-s#)/1e6') }); } } else { _.extend(templateData, { 'begin': interpolate('s#=new n#'), 'end': interpolate('r#=(new n#-s#)/1e3') }); } // define `timer` methods timer.start = createFunction( interpolate('o#'), interpolate('var n#=this.ns,${begin};o#.elapsed=0;o#.timeStamp=s#') ); timer.stop = createFunction( interpolate('o#'), interpolate('var n#=this.ns,s#=o#.timeStamp,${end};o#.elapsed=r#') ); // create compiled test return createFunction( interpolate('window,t#'), 'var global = window, clearTimeout = global.clearTimeout, setTimeout = global.setTimeout;\n' + interpolate(body) ); } /** * Gets the current timer's minimum resolution (secs). */ function getRes(unit) { var measured, begin, count = 30, divisor = 1e3, ns = timer.ns, sample = []; // get average smallest measurable time while (count--) { if (unit == 'us') { divisor = 1e6; if (ns.stop) { ns.start(); while (!(measured = ns.microseconds())) { } } else if (ns[perfName]) { divisor = 1e3; measured = Function('n', 'var r,s=n.' + perfName + '();while(!(r=n.' + perfName + '()-s)){};return r')(ns); } else { begin = ns(); while (!(measured = ns() - begin)) { } } } else if (unit == 'ns') { divisor = 1e9; if (ns.nanoTime) { begin = ns.nanoTime(); while (!(measured = ns.nanoTime() - begin)) { } } else { begin = (begin = ns())[0] + (begin[1] / divisor); while (!(measured = ((measured = ns())[0] + (measured[1] / divisor)) - begin)) { } divisor = 1; } } else { begin = new ns; while (!(measured = new ns - begin)) { } } // check for broken timers (nanoTime may have issues) // http://alivebutsleepy.srnet.cz/unreliable-system-nanotime/ if (measured > 0) { sample.push(measured); } else { sample.push(Infinity); break; } } // convert to seconds return getMean(sample) / divisor; } /** * Interpolates a given template string. */ function interpolate(string) { // replaces all occurrences of `#` with a unique number and template tokens with content return _.template(string.replace(/\#/g, /\d+/.exec(templateData.uid)), templateData); } /*----------------------------------------------------------------------*/ // detect nanosecond support from a Java applet _.each(doc && doc.applets || [], function(element) { return !(timer.ns = applet = 'nanoTime' in element && element); }); // check type in case Safari returns an object instead of a number try { if (typeof timer.ns.nanoTime() == 'number') { timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' }); } } catch(e) { } // detect Chrome's microsecond timer: // enable benchmarking via the --enable-benchmarking command // line switch in at least Chrome 7 to use chrome.Interval try { if ((timer.ns = new (context.chrome || context.chromium).Interval)) { timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' }); } } catch(e) { } // detect `performance.now` microsecond resolution timer if ((timer.ns = perfName && perfObject)) { timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' }); } // detect Node's nanosecond resolution timer available in Node >= 0.8 if (processObject && typeof (timer.ns = processObject.hrtime) == 'function') { timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' }); } // detect Wade Simmons' Node microtime module if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function') { timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' }); } // pick timer with highest resolution timer = _.reduce(timers, function(timer, other) { return other.res < timer.res ? other : timer; }); // remove unused applet if (timer.unit != 'ns' && applet) { applet = destroyElement(applet); } // error if there are no working timers if (timer.res == Infinity) { throw new Error('Benchmark.js was unable to find a working timer.'); } // resolve time span required to achieve a percent uncertainty of at most 1% // http://spiff.rit.edu/classes/phys273/uncert/uncert.html options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05)); return clock.apply(null, arguments); } /*------------------------------------------------------------------------*/ /** * Computes stats on benchmark results. * * @private * @param {Object} bench The benchmark instance. * @param {Object} options The options object. */ function compute(bench, options) { options || (options = {}); var async = options.async, elapsed = 0, initCount = bench.initCount, minSamples = bench.minSamples, queue = [], sample = bench.stats.sample; /** * Adds a clone to the queue. */ function enqueue() { queue.push(bench.clone({ '_original': bench, 'events': { 'abort': [update], 'cycle': [update], 'error': [update], 'start': [update] } })); } /** * Updates the clone/original benchmarks to keep their data in sync. */ function update(event) { var clone = this, type = event.type; if (bench.running) { if (type == 'start') { // Note: `clone.minTime` prop is inited in `clock()` clone.count = bench.initCount; } else { if (type == 'error') { bench.error = clone.error; } if (type == 'abort') { bench.abort(); bench.emit('cycle'); } else { event.currentTarget = event.target = bench; bench.emit(event); } } } else if (bench.aborted) { // clear abort listeners to avoid triggering bench's abort/cycle again clone.events.abort.length = 0; clone.abort(); } } /** * Determines if more clones should be queued or if cycling should stop. */ function evaluate(event) { var critical, df, mean, moe, rme, sd, sem, variance, clone = event.target, done = bench.aborted, now = +new Date, size = sample.push(clone.times.period), maxedOut = size >= minSamples && (elapsed += now - clone.times.timeStamp) / 1e3 > bench.maxTime, times = bench.times, varOf = function(sum, x) { return sum + pow(x - mean, 2); }; // exit early for aborted or unclockable tests if (done || clone.hz == Infinity) { maxedOut = !(size = sample.length = queue.length = 0); } if (!done) { // sample mean (estimate of the population mean) mean = getMean(sample); // sample variance (estimate of the population variance) variance = _.reduce(sample, varOf, 0) / (size - 1) || 0; // sample standard deviation (estimate of the population standard deviation) sd = sqrt(variance); // standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean) sem = sd / sqrt(size); // degrees of freedom df = size - 1; // critical value critical = tTable[Math.round(df) || 1] || tTable.infinity; // margin of error moe = sem * critical; // relative margin of error rme = (moe / mean) * 100 || 0; _.extend(bench.stats, { 'deviation': sd, 'mean': mean, 'moe': moe, 'rme': rme, 'sem': sem, 'variance': variance }); // Abort the cycle loop when the minimum sample size has been collected // and the elapsed time exceeds the maximum time allowed per benchmark. // We don't count cycle delays toward the max time because delays may be // increased by browsers that clamp timeouts for inactive tabs. // https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs if (maxedOut) { // reset the `initCount` in case the benchmark is rerun bench.initCount = initCount; bench.running = false; done = true; times.elapsed = (now - times.timeStamp) / 1e3; } if (bench.hz != Infinity) { bench.hz = 1 / mean; times.cycle = mean * bench.count; times.period = mean; } } // if time permits, increase sample size to reduce the margin of error if (queue.length < 2 && !maxedOut) { enqueue(); } // abort the invoke cycle when done event.aborted = done; } // init queue and begin enqueue(); invoke(queue, { 'name': 'run', 'args': { 'async': async }, 'queued': true, 'onCycle': evaluate, 'onComplete': function() { bench.emit('complete'); } }); } /*------------------------------------------------------------------------*/ /** * Cycles a benchmark until a run `count` can be established. * * @private * @param {Object} clone The cloned benchmark instance. * @param {Object} options The options object. */ function cycle(clone, options) { options || (options = {}); var deferred; if (clone instanceof Deferred) { deferred = clone; clone = clone.benchmark; } var clocked, cycles, divisor, event, minTime, period, async = options.async, bench = clone._original, count = clone.count, times = clone.times; // continue, if not aborted between cycles if (clone.running) { // `minTime` is set to `Benchmark.options.minTime` in `clock()` cycles = ++clone.cycles; clocked = deferred ? deferred.elapsed : clock(clone); minTime = clone.minTime; if (cycles > bench.cycles) { bench.cycles = cycles; } if (clone.error) { event = Event('error'); event.message = clone.error; clone.emit(event); if (!event.cancelled) { clone.abort(); } } } // continue, if not errored if (clone.running) { // time taken to complete last test cycle bench.times.cycle = times.cycle = clocked; // seconds per operation period = bench.times.period = times.period = clocked / count; // ops per second bench.hz = clone.hz = 1 / period; // avoid working our way up to this next time bench.initCount = clone.initCount = count; // do we need to do another cycle? clone.running = clocked < minTime; if (clone.running) { // tests may clock at `0` when `initCount` is a small number, // to avoid that we set its count to something a bit higher if (!clocked && (divisor = divisors[clone.cycles]) != null) { count = floor(4e6 / divisor); } // calculate how many more iterations it will take to achive the `minTime` if (count <= clone.count) { count += Math.ceil((minTime - clocked) / period); } clone.running = count != Infinity; } } // should we exit early? event = Event('cycle'); clone.emit(event); if (event.aborted) { clone.abort(); } // figure out what to do next if (clone.running) { // start a new cycle clone.count = count; if (deferred) { clone.compiled.call(deferred, context, timer); } else if (async) { delay(clone, function() { cycle(clone, options); }); } else { cycle(clone); } } else { // fix TraceMonkey bug associated with clock fallbacks // http://bugzil.la/509069 if (support.browser) { runScript(uid + '=1;delete ' + uid); } // done clone.emit('complete'); } } /*------------------------------------------------------------------------*/ /** * Runs the benchmark. * * @memberOf Benchmark * @param {Object} [options={}] Options object. * @returns {Object} The benchmark instance. * @example * * // basic usage * bench.run(); * * // or with options * bench.run({ 'async': true }); */ function run(options) { var bench = this, event = Event('start'); // set `running` to `false` so `reset()` won't call `abort()` bench.running = false; bench.reset(); bench.running = true; bench.count = bench.initCount; bench.times.timeStamp = +new Date; bench.emit(event); if (!event.cancelled) { options = { 'async': ((options = options && options.async) == null ? bench.async : options) && support.timeout }; // for clones created within `compute()` if (bench._original) { if (bench.defer) { Deferred(bench); } else { cycle(bench, options); } } // for original benchmarks else { compute(bench, options); } } return bench; } /*------------------------------------------------------------------------*/ // Firefox 1 erroneously defines variable and argument names of functions on // the function itself as non-configurable properties with `undefined` values. // The bugginess continues as the `Benchmark` constructor has an argument // named `options` and Firefox 1 will not assign a value to `Benchmark.options`, // making it non-writable in the process, unless it is the first property // assigned by for-in loop of `_.extend()`. _.extend(Benchmark, { /** * The default options copied by benchmark instances. * * @static * @memberOf Benchmark * @type Object */ 'options': { /** * A flag to indicate that benchmark cycles will execute asynchronously * by default. * * @memberOf Benchmark.options * @type boolean */ 'async': false, /** * A flag to indicate that the benchmark clock is deferred. * * @memberOf Benchmark.options * @type boolean */ 'defer': false, /** * The delay between test cycles (secs). * @memberOf Benchmark.options * @type number */ 'delay': 0.005, /** * Displayed by Benchmark#toString when a `name` is not available * (auto-generated if absent). * * @memberOf Benchmark.options * @type string */ 'id': undefined, /** * The default number of times to execute a test on a benchmark's first cycle. * * @memberOf Benchmark.options * @type number */ 'initCount': 1, /** * The maximum time a benchmark is allowed to run before finishing (secs). * * Note: Cycle delays aren't counted toward the maximum time. * * @memberOf Benchmark.options * @type number */ 'maxTime': 5, /** * The minimum sample size required to perform statistical analysis. * * @memberOf Benchmark.options * @type number */ 'minSamples': 5, /** * The time needed to reduce the percent uncertainty of measurement to 1% (secs). * * @memberOf Benchmark.options * @type number */ 'minTime': 0, /** * The name of the benchmark. * * @memberOf Benchmark.options * @type string */ 'name': undefined, /** * An event listener called when the benchmark is aborted. * * @memberOf Benchmark.options * @type Function */ 'onAbort': undefined, /** * An event listener called when the benchmark completes running. * * @memberOf Benchmark.options * @type Function */ 'onComplete': undefined, /** * An event listener called after each run cycle. * * @memberOf Benchmark.options * @type Function */ 'onCycle': undefined, /** * An event listener called when a test errors. * * @memberOf Benchmark.options * @type Function */ 'onError': undefined, /** * An event listener called when the benchmark is reset. * * @memberOf Benchmark.options * @type Function */ 'onReset': undefined, /** * An event listener called when the benchmark starts running. * * @memberOf Benchmark.options * @type Function */ 'onStart': undefined }, /** * Platform object with properties describing things like browser name, * version, and operating system. * * @static * @memberOf Benchmark * @type Object */ 'platform': context.platform || req('platform') || ({ 'description': context.navigator && context.navigator.userAgent || null, 'layout': null, 'product': null, 'name': null, 'manufacturer': null, 'os': null, 'prerelease': null, 'version': null, 'toString': function() { return this.description || ''; } }), /** * The semantic version number. * * @static * @memberOf Benchmark * @type string */ 'version': '1.0.0' }); _.extend(Benchmark, { 'filter': filter, 'formatNumber': formatNumber, 'invoke': invoke, 'join': join, 'runInContext': runInContext, 'support': support }); // Add Lo-Dash methods to Benchmark _.each(['each', 'forEach', 'forOwn', 'has', 'indexOf', 'map', 'pluck', 'reduce'], function(methodName) { Benchmark[methodName] = _[methodName]; }); /*------------------------------------------------------------------------*/ _.extend(Benchmark.prototype, { /** * The number of times a test was executed. * * @memberOf Benchmark * @type number */ 'count': 0, /** * The number of cycles performed while benchmarking. * * @memberOf Benchmark * @type number */ 'cycles': 0, /** * The number of executions per second. * * @memberOf Benchmark * @type number */ 'hz': 0, /** * The compiled test function. * * @memberOf Benchmark * @type {Function|string} */ 'compiled': undefined, /** * The error object if the test failed. * * @memberOf Benchmark * @type Object */ 'error': undefined, /** * The test to benchmark. * * @memberOf Benchmark * @type {Function|string} */ 'fn': undefined, /** * A flag to indicate if the benchmark is aborted. * * @memberOf Benchmark * @type boolean */ 'aborted': false, /** * A flag to indicate if the benchmark is running. * * @memberOf Benchmark * @type boolean */ 'running': false, /** * Compiled into the test and executed immediately **before** the test loop. * * @memberOf Benchmark * @type {Function|string} * @example * * // basic usage * var bench = Benchmark({ * 'setup': function() { * var c = this.count, * element = document.getElementById('container'); * while (c--) { * element.appendChild(document.createElement('div')); * } * }, * 'fn': function() { * element.removeChild(element.lastChild); * } * }); * * // compiles to something like: * var c = this.count, * element = document.getElementById('container'); * while (c--) { * element.appendChild(document.createElement('div')); * } * var start = new Date; * while (count--) { * element.removeChild(element.lastChild); * } * var end = new Date - start; * * // or using strings * var bench = Benchmark({ * 'setup': '\ * var a = 0;\n\ * (function() {\n\ * (function() {\n\ * (function() {', * 'fn': 'a += 1;', * 'teardown': '\ * }())\n\ * }())\n\ * }())' * }); * * // compiles to something like: * var a = 0; * (function() { * (function() { * (function() { * var start = new Date; * while (count--) { * a += 1; * } * var end = new Date - start; * }()) * }()) * }()) */ 'setup': noop, /** * Compiled into the test and executed immediately **after** the test loop. * * @memberOf Benchmark * @type {Function|string} */ 'teardown': noop, /** * An object of stats including mean, margin or error, and standard deviation. * * @memberOf Benchmark * @type Object */ 'stats': { /** * The margin of error. * * @memberOf Benchmark#stats * @type number */ 'moe': 0, /** * The relative margin of error (expressed as a percentage of the mean). * * @memberOf Benchmark#stats * @type number */ 'rme': 0, /** * The standard error of the mean. * * @memberOf Benchmark#stats * @type number */ 'sem': 0, /** * The sample standard deviation. * * @memberOf Benchmark#stats * @type number */ 'deviation': 0, /** * The sample arithmetic mean (secs). * * @memberOf Benchmark#stats * @type number */ 'mean': 0, /** * The array of sampled periods. * * @memberOf Benchmark#stats * @type Array */ 'sample': [], /** * The sample variance. * * @memberOf Benchmark#stats * @type number */ 'variance': 0 }, /** * An object of timing data including cycle, elapsed, period, start, and stop. * * @memberOf Benchmark * @type Object */ 'times': { /** * The time taken to complete the last cycle (secs). * * @memberOf Benchmark#times * @type number */ 'cycle': 0, /** * The time taken to complete the benchmark (secs). * * @memberOf Benchmark#times * @type number */ 'elapsed': 0, /** * The time taken to execute the test once (secs). * * @memberOf Benchmark#times * @type number */ 'period': 0, /** * A timestamp of when the benchmark started (ms). * * @memberOf Benchmark#times * @type number */ 'timeStamp': 0 } }); _.extend(Benchmark.prototype, { 'abort': abort, 'clone': clone, 'compare': compare, 'emit': emit, 'listeners': listeners, 'off': off, 'on': on, 'reset': reset, 'run': run, 'toString': toStringBench }); /*------------------------------------------------------------------------*/ _.extend(Deferred.prototype, { /** * The deferred benchmark instance. * * @memberOf Benchmark.Deferred * @type Object */ 'benchmark': null, /** * The number of deferred cycles performed while benchmarking. * * @memberOf Benchmark.Deferred * @type number */ 'cycles': 0, /** * The time taken to complete the deferred benchmark (secs). * * @memberOf Benchmark.Deferred * @type number */ 'elapsed': 0, /** * A timestamp of when the deferred benchmark started (ms). * * @memberOf Benchmark.Deferred * @type number */ 'timeStamp': 0 }); _.extend(Deferred.prototype, { 'resolve': resolve }); /*------------------------------------------------------------------------*/ _.extend(Event.prototype, { /** * A flag to indicate if the emitters listener iteration is aborted. * * @memberOf Benchmark.Event * @type boolean */ 'aborted': false, /** * A flag to indicate if the default action is cancelled. * * @memberOf Benchmark.Event * @type boolean */ 'cancelled': false, /** * The object whose listeners are currently being processed. * * @memberOf Benchmark.Event * @type Object */ 'currentTarget': undefined, /** * The return value of the last executed listener. * * @memberOf Benchmark.Event * @type Mixed */ 'result': undefined, /** * The object to which the event was originally emitted. * * @memberOf Benchmark.Event * @type Object */ 'target': undefined, /** * A timestamp of when the event was created (ms). * * @memberOf Benchmark.Event * @type number */ 'timeStamp': 0, /** * The event type. * * @memberOf Benchmark.Event * @type string */ 'type': '' }); /*------------------------------------------------------------------------*/ /** * The default options copied by suite instances. * * @static * @memberOf Benchmark.Suite * @type Object */ Suite.options = { /** * The name of the suite. * * @memberOf Benchmark.Suite.options * @type string */ 'name': undefined }; /*------------------------------------------------------------------------*/ _.extend(Suite.prototype, { /** * The number of benchmarks in the suite. * * @memberOf Benchmark.Suite * @type number */ 'length': 0, /** * A flag to indicate if the suite is aborted. * * @memberOf Benchmark.Suite * @type boolean */ 'aborted': false, /** * A flag to indicate if the suite is running. * * @memberOf Benchmark.Suite * @type boolean */ 'running': false }); _.extend(Suite.prototype, { 'abort': abortSuite, 'add': add, 'clone': cloneSuite, 'emit': emit, 'filter': filterSuite, 'join': arrayRef.join, 'listeners': listeners, 'off': off, 'on': on, 'pop': arrayRef.pop, 'push': push, 'reset': resetSuite, 'run': runSuite, 'reverse': arrayRef.reverse, 'shift': shift, 'slice': arrayRef.slice, 'sort': arrayRef.sort, 'splice': arrayRef.splice, 'unshift': arrayRef.unshift }); /*------------------------------------------------------------------------*/ // expose Deferred, Event, and Suite _.extend(Benchmark, { 'Deferred': Deferred, 'Event': Event, 'Suite': Suite }); /*------------------------------------------------------------------------*/ // add Lo-Dash methods as Suite methods _.each(['each', 'forEach', 'indexOf', 'map', 'pluck', 'reduce'], function(methodName) { var func = _[methodName]; Suite.prototype[methodName] = function() { var args = [this]; push.apply(args, arguments); return func.apply(_, args); }; }); // avoid array-like object bugs with `Array#shift` and `Array#splice` // in Firefox < 10 and IE < 9 if (!_.support.spliceObjects) { _.each(['pop', 'shift', 'splice'], function(methodName) { var func = arrayRef[methodName]; Suite.prototype[methodName] = function() { var value = this, result = func.apply(value, arguments); if (value.length === 0) { delete value[0]; } return result; }; }); } // trigger clock's lazy define early to avoid a security error if (support.air) { clock({ '_original': { 'fn': noop, 'count': 1, 'options': {} } }); } return Benchmark; } /*--------------------------------------------------------------------------*/ // expose Benchmark // some AMD build optimizers, like r.js, check for condition patterns like the following: if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { // define as an anonymous module so, through path mapping, it can be aliased define(['lodash', 'platform'], function(_, platform) { return runInContext({ '_': _, 'platform': platform }); }); } else { var Benchmark = runInContext(); // check for `exports` after `define` in case a build optimizer adds an `exports` object if (freeExports && !freeExports.nodeType) { // in Node.js or RingoJS v0.8.0+ if (freeModule) { (freeModule.exports = Benchmark).Benchmark = Benchmark; } // in Narwhal or RingoJS v0.7.0- else { freeExports.Benchmark = Benchmark; } } // in a browser or Rhino else { root.Benchmark = Benchmark; } } }(this)); lodash-2.4.1/vendor/benchmark.js/LICENSE.txt0000644000175000017500000000227312247303154016624 0ustar ovdovdCopyright 2010-2013 Mathias Bynens Based on JSLitmus.js, copyright Robert Kieffer Modified by John-David Dalton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.lodash-2.4.1/vendor/benchmark.js/nano.jar0000644000175000017500000000044512247303154016431 0ustar ovdovdPK:> nano.class- @g5vjs؋ v9I.CYX)>%n1;32 "ȐaNg[{NƚvE6A'uQR5M[ ,=Vٚ8Ҙ cn&c6b:??}8u z"?PK:`UPK:>:`U nano.classPK8lodash-2.4.1/vendor/docdown/0000755000175000017500000000000012247303154014065 5ustar ovdovdlodash-2.4.1/vendor/docdown/src/0000755000175000017500000000000012247303154014654 5ustar ovdovdlodash-2.4.1/vendor/docdown/src/DocDown/0000755000175000017500000000000012247303154016211 5ustar ovdovdlodash-2.4.1/vendor/docdown/src/DocDown/Alias.php0000644000175000017500000001207512247303154017760 0ustar ovdovdowner = $owner; $this->_name = $name; $this->_call = $owner->getCall(); $this->_category = $owner->getCategory(); $this->_desc = $owner->getDesc(); $this->_example = $owner->getExample(); $this->_isCtor = $owner->isCtor(); $this->_isLicense = $owner->isLicense(); $this->_isPlugin = $owner->isPlugin(); $this->_isPrivate = $owner->isPrivate(); $this->_isStatic = $owner->isStatic(); $this->_lineNumber = $owner->getLineNumber(); $this->_members = $owner->getMembers(); $this->_params = $owner->getParams(); $this->_returns = $owner->getReturns(); $this->_type = $owner->getType(); } /*--------------------------------------------------------------------------*/ /** * Extracts the entry's `alias` objects. * * @memberOf Alias * @param {number} $index The index of the array value to return. * @returns {Array|string} The entry's `alias` objects. */ public function getAliases( $index = null ) { $result = array(); return $index !== null ? @$result[$index] : $result; } /** * Extracts the function call from the owner entry. * * @memberOf Alias * @returns {string} The function call. */ public function getCall() { return $this->_call; } /** * Extracts the owner entry's `category` data. * * @memberOf Alias * @returns {string} The owner entry's `category` data. */ public function getCategory() { return $this->_category; } /** * Extracts the owner entry's description. * * @memberOf Alias * @returns {string} The owner entry's description. */ public function getDesc() { return $this->_desc; } /** * Extracts the owner entry's `example` data. * * @memberOf Alias * @returns {string} The owner entry's `example` data. */ public function getExample() { return $this->_example; } /** * Checks if the entry is an alias. * * @memberOf Alias * @returns {boolean} Returns `true`. */ public function isAlias() { return true; } /** * Checks if the owner entry is a constructor. * * @memberOf Alias * @returns {boolean} Returns `true` if a constructor, else `false`. */ public function isCtor() { return $this->_isCtor; } /** * Checks if the owner entry is a license. * * @memberOf Alias * @returns {boolean} Returns `true` if a license, else `false`. */ public function isLicense() { return $this->_isLicense; } /** * Checks if the owner entry *is* assigned to a prototype. * * @memberOf Alias * @returns {boolean} Returns `true` if assigned to a prototype, else `false`. */ public function isPlugin() { return $this->_isPlugin; } /** * Checks if the owner entry is private. * * @memberOf Alias * @returns {boolean} Returns `true` if private, else `false`. */ public function isPrivate() { return $this->_isPrivate; } /** * Checks if the owner entry is *not* assigned to a prototype. * * @memberOf Alias * @returns {boolean} Returns `true` if not assigned to a prototype, else `false`. */ public function isStatic() { return $this->_isStatic; } /** * Resolves the owner entry's line number. * * @memberOf Alias * @returns {number} The owner entry's line number. */ public function getLineNumber() { return $this->_lineNumber; } /** * Extracts the owner entry's `member` data. * * @memberOf Alias * @param {number} $index The index of the array value to return. * @returns {Array|string} The owner entry's `member` data. */ public function getMembers( $index = null ) { return $index !== null ? @$this->_members[$index] : $this->_members; } /** * Extracts the owner entry's `name` data. * * @memberOf Alias * @returns {string} The owner entry's `name` data. */ public function getName() { return $this->_name; } /** * Extracts the owner entry's `param` data. * * @memberOf Alias * @param {number} $index The index of the array value to return. * @returns {Array} The owner entry's `param` data. */ public function getParams( $index = null ) { return $index !== null ? @$this->_params[$index] : $this->_params; } /** * Extracts the owner entry's `returns` data. * * @memberOf Alias * @returns {string} The owner entry's `returns` data. */ public function getReturns() { return $this->_returns; } /** * Extracts the owner entry's `type` data. * * @memberOf Alias * @returns {string} The owner entry's `type` data. */ public function getType() { return $this->_type; } } ?>lodash-2.4.1/vendor/docdown/src/DocDown/MarkdownGenerator.php0000644000175000017500000005020112247303154022351 0ustar ovdovd\n"; /** * An array of JSDoc entries. * * @memberOf MarkdownGenerator * @type Array */ public $entries = array(); /** * The HTML for the open tag. * * @memberOf MarkdownGenerator * @type string */ public $openTag = "\n\n"; /** * An options array used to configure the generator. * * @memberOf MarkdownGenerator * @type Array */ public $options = array(); /** * The file's source code. * * @memberOf MarkdownGenerator * @type string */ public $source = ''; /** * The array of code snippets that are tokenized by `escape`. * * @private * @memberOf MarkdownGenerator * @type Array */ private $snippets = array(); /*--------------------------------------------------------------------------*/ /** * The MarkdownGenerator constructor. * * @constructor * @param {string} $source The source code to parse. * @param {Array} $options The options array. */ public function __construct( $source, $options = array() ) { // juggle arguments if (is_array($source)) { $options = $source; } else { $options['source'] = $source; } if (isset($options['source']) && realpath($options['source'])) { $options['path'] = $options['source']; } if (isset($options['path'])) { preg_match('/(?<=\.)[a-z]+$/', $options['path'], $ext); $options['source'] = file_get_contents($options['path']); $ext = array_pop($ext); if (!isset($options['lang']) && $ext) { $options['lang'] = $ext; } if (!isset($options['title'])) { $options['title'] = ucfirst(basename($options['path'])) . ' API documentation'; } } if (!isset($options['lang'])) { $options['lang'] = 'js'; } if (!isset($options['toc'])) { $options['toc'] = 'properties'; } $this->options = $options; $this->source = str_replace(PHP_EOL, "\n", $options['source']); $this->entries = Entry::getEntries($this->source); foreach ($this->entries as $index => $value) { $this->entries[$index] = new Entry($value, $this->source, $options['lang']); } } /*--------------------------------------------------------------------------*/ /** * Performs common string formatting operations. * * @private * @static * @memberOf MarkdownGenerator * @param {string} $string The string to format. * @returns {string} The formatted string. */ private static function format( $string ) { $counter = 0; // tokenize inline code snippets preg_match_all('/`[^`]+`/', $string, $tokenized); $tokenized = $tokenized[0]; foreach ($tokenized as $snippet) { $string = str_replace($snippet, '__token' . ($counter++) .'__', $string); } // italicize parentheses $string = preg_replace('/(^|\s)(\([^)]+\))/', '$1*$2*', $string); // mark numbers as inline code $string = preg_replace('/[\t ](-?\d+(?:.\d+)?)(?!\.[^\n])/', ' `$1`', $string); // detokenize inline code snippets $counter = 0; foreach ($tokenized as $snippet) { $string = str_replace('__token' . ($counter++) . '__', $snippet, $string); } return trim($string); } /** * Modify a string by replacing named tokens with matching assoc. array values. * * @private * @static * @memberOf MarkdownGenerator * @param {string} $string The string to modify. * @param {Array|Object} $object The template object. * @returns {string} The modified string. */ private static function interpolate( $string, $object ) { preg_match_all('/#\{([^}]+)\}/', $string, $tokens); $tokens = array_unique(array_pop($tokens)); foreach ($tokens as $token) { $pattern = '/#\{' . preg_replace('/([.*+?^${}()|[\]\\\])/', '\\\$1', $token) . '\}/'; $replacement = ''; if (is_object($object)) { preg_match('/\(([^)]+?)\)$/', $token, $args); $args = preg_split('/,\s*/', array_pop($args)); $method = 'get' . ucfirst(preg_replace('/\([^)]+?\)$/', '', $token)); if (method_exists($object, $method)) { $replacement = (string) call_user_func_array(array($object, $method), $args); } else if (isset($object->{$token})) { $replacement = (string) $object->{$token}; } } else if (isset($object[$token])) { $replacement = (string) $object[$token]; } $string = preg_replace($pattern, trim($replacement), $string); } return MarkdownGenerator::format($string); } /*--------------------------------------------------------------------------*/ /** * Adds the given `$entries` to the `$result` array. * * @private * @memberOf MarkdownGenerator * @param {Array} $result The result array to modify. * @param {Array} $entries The entries to add to the `$result`. */ private function addEntries( &$result, $entries ) { foreach ($entries as $entry) { // skip aliases if ($entry->isAlias()) { continue; } // name and description array_push( $result, $this->openTag, MarkdownGenerator::interpolate("### `#{member}#{separator}#{call}`\n# [Ⓢ](#{href} \"View in source\") [Ⓣ][1]\n\n#{desc}", array( 'call' => $entry->getCall(), 'desc' => $this->escape($entry->getDesc()), 'hash' => $entry->hash, 'href' => $entry->href, 'member' => $entry->member, 'separator' => $entry->separator )) ); // @alias if (count($aliases = $entry->getAliases())) { array_push($result, '', '#### Aliases'); foreach ($aliases as $index => $alias) { $aliases[$index] = MarkdownGenerator::interpolate('#{member}#{separator}#{name}', $alias); } $result[] = '*' . implode(', ', $aliases) . '*'; } // @param if (count($params = $entry->getParams())) { array_push($result, '', '#### Arguments'); foreach ($params as $index => $param) { $result[] = MarkdownGenerator::interpolate('#{num}. `#{name}` (#{type}): #{desc}', array( 'desc' => $this->escape($param[2]), 'name' => $param[1], 'num' => $index + 1, 'type' => $this->escape($param[0]) )); } } // @returns if (count($returns = $entry->getReturns())) { array_push( $result, '', '#### Returns', MarkdownGenerator::interpolate('(#{type}): #{desc}', array( 'desc' => $this->escape($returns[1]), 'type' => $this->escape($returns[0]) )) ); } // @example if ($example = $entry->getExample()) { array_push($result, '', '#### Example', $example); } array_push($result, "\n* * *", $this->closeTag); } } /** * Escapes special Markdown characters. * * @private * @memberOf Entry * @param {string} $string The string to escape. * @returns {string} Returns the escaped string. */ private function escape( $string ) { $string = preg_replace_callback('/`.*?\`/', array($this, 'swapSnippetsToTokens'), $string); $string = preg_replace('/(?entries[$entry] : $entry; $member = !$member ? $entry->getMembers(0) : $member; $result = ($member ? $member . ($entry->isPlugin() ? 'prototype' : '') : '') . $entry->getCall(); $result = preg_replace('/\(\[|\[\]/', '', $result); $result = preg_replace('/[\t =|\'"{}.()\]]/', '', $result); $result = preg_replace('/[\[#,]+/', '-', $result); return strtolower($result); } /** * Resolves the entry's url for the specific line number. * * @private * @memberOf MarkdownGenerator * @param {number|Object} $entry The entry object. * @returns {string} The url. */ private function getLineUrl( $entry ) { $entry = is_numeric($entry) ? $this->entries($entry) : $entry; return $this->options['url'] . '#L' . $entry->getLineNumber(); } /** * Extracts the character used to separate the entry's name from its member. * * @private * @memberOf MarkdownGenerator * @param {number|Object} $entry The entry object. * @returns {string} The separator. */ private function getSeparator( $entry ) { $entry = is_numeric($entry) ? $this->entries($entry) : $entry; return $entry->isPlugin() ? '.prototype.' : '.'; } /** * Swaps code snippets with tokens as a `preg_replace_callback` callback * used by `escape`. * * @private * @memberOf Entry * @param {Array} $matches The array of regexp matches. * @returns {string} Returns the token. */ private function swapSnippetsToTokens( $matches ) { $this->snippets[] = $matches[0]; return '@@token@@'; } /** * Swaps tokens with code snippets as a `preg_replace_callback` callback * used by `escape`. * * @private * @memberOf Entry * @returns {string} Returns the code snippet. */ private function swapTokensToSnippets() { return array_shift($this->snippets); } /*--------------------------------------------------------------------------*/ /** * Generates Markdown from JSDoc entries. * * @memberOf MarkdownGenerator * @returns {string} The rendered Markdown. */ public function generate() { $api = array(); $byCategory = $this->options['toc'] == 'categories'; $categories = array(); $closeTag = $this->closeTag; $compiling = false; $openTag = $this->openTag; $result = array('# ' . $this->options['title']); $toc = 'toc'; // initialize $api array foreach ($this->entries as $entry) { // skip invalid or private entries $name = $entry->getName(); if (!$name || $entry->isPrivate()) { continue; } $members = $entry->getMembers(); $members = count($members) ? $members : array(''); foreach ($members as $member) { // create api category arrays if ($member && !isset($api[$member])) { // create temporary entry to be replaced later $api[$member] = new stdClass; $api[$member]->static = array(); $api[$member]->plugin = array(); } // append entry to api member if (!$member || $entry->isCtor() || ($entry->getType() == 'Object' && !preg_match('/[=:]\s*(?:null|undefined)\s*[,;]?$/', $entry->entry))) { // assign the real entry, replacing the temporary entry if it exist $member = ($member ? $member . ($entry->isPlugin() ? '#' : '.') : '') . $name; $entry->static = @$api[$member] ? $api[$member]->static : array(); $entry->plugin = @$api[$member] ? $api[$member]->plugin : array(); $api[$member] = $entry; foreach ($entry->getAliases() as $alias) { $api[$member]->static[] = $alias; } } else if ($entry->isStatic()) { $api[$member]->static[] = $entry; foreach ($entry->getAliases() as $alias) { $api[$member]->static[] = $alias; } } else if (!$entry->isCtor()) { $api[$member]->plugin[] = $entry; foreach ($entry->getAliases() as $alias) { $api[$member]->plugin[] = $alias; } } } } // add properties to each entry foreach ($api as $entry) { $entry->hash = $this->getHash($entry); $entry->href = $this->getLineUrl($entry); $entry->separator = ''; $member = $entry->getMembers(0); $member = ($member ? $member . $this->getSeparator($entry) : '') . $entry->getName(); $entry->member = preg_replace('/' . $entry->getName() . '$/', '', $member); // add properties to static and plugin sub-entries foreach (array('static', 'plugin') as $kind) { foreach ($entry->{$kind} as $subentry) { $subentry->hash = $this->getHash($subentry); $subentry->href = $this->getLineUrl($subentry); $subentry->member = $member; $subentry->separator = $this->getSeparator($subentry); } } } /*------------------------------------------------------------------------*/ // custom sort for root level entries // TODO: see how well it handles deeper namespace traversal function sortCompare($a, $b) { $score = array( 'a' => 0, 'b' => 0); foreach (array( 'a' => $a, 'b' => $b) as $key => $value) { // capitalized properties are last if (preg_match('/[#.][A-Z]/', $value)) { $score[$key] = 0; } // lowercase prototype properties are next to last else if (preg_match('/#[a-z]/', $value)) { $score[$key] = 1; } // lowercase static properties next to first else if (preg_match('/\.[a-z]/', $value)) { $score[$key] = 2; } // root properties are first else if (preg_match('/^[^#.]+$/', $value)) { $score[$key] = 3; } } $score = $score['b'] - $score['a']; return $score ? $score : strcasecmp($a, $b); } uksort($api, 'sortCompare'); // sort static and plugin sub-entries foreach ($api as $entry) { foreach (array('static', 'plugin') as $kind) { $sortBy = array( 'a' => array(), 'b' => array(), 'c' => array() ); foreach ($entry->{$kind} as $subentry) { $name = $subentry->getName(); // functions w/o ALL-CAPs names are last $sortBy['a'][] = $subentry->getType() == 'Function' && !preg_match('/^[A-Z_]+$/', $name); // ALL-CAPs properties first $sortBy['b'][] = preg_match('/^[A-Z_]+$/', $name); // lowercase alphanumeric sort $sortBy['c'][] = strtolower($name); } array_multisort($sortBy['a'], SORT_ASC, $sortBy['b'], SORT_DESC, $sortBy['c'], SORT_ASC, $entry->{$kind}); } } /*------------------------------------------------------------------------*/ // add categories foreach ($api as $entry) { $categories[$entry->getCategory()][] = $entry; foreach (array('static', 'plugin') as $kind) { foreach ($entry->{$kind} as $subentry) { $categories[$subentry->getCategory()][] = $subentry; } } } // sort categories ksort($categories); foreach(array('Methods', 'Properties') as $category) { if (isset($categories[$category])) { $entries = $categories[$category]; unset($categories[$category]); $categories[$category] = $entries; } } /*------------------------------------------------------------------------*/ // compile TOC $result[] = $openTag; // compile TOC by categories if ($byCategory) { foreach ($categories as $category => $entries) { if ($compiling) { $result[] = $closeTag; } else { $compiling = true; } // assign TOC hash if (count($result) == 2) { $toc = strtolower($category); } // add category array_push( $result, $openTag, '## ' . (count($result) == 2 ? '' : '') . '`' . $category . '`' ); // add entries foreach ($entries as $entry) { if ($entry->isAlias()) { $result[] = MarkdownGenerator::interpolate('* `#{member}#{separator}#{name}` -> `#{realName}`', array( 'hash' => $entry->hash, 'member' => $entry->member, 'name' => $entry->getName(), 'realName' => $entry->owner->getName(), 'separator' => $entry->separator )); } else { $result[] = MarkdownGenerator::interpolate('* `#{member}#{separator}#{name}`', $entry); } } } } // compile TOC by namespace else { foreach ($api as $entry) { if ($compiling) { $result[] = $closeTag; } else { $compiling = true; } $member = $entry->member . $entry->getName(); // assign TOC hash if (count($result) == 2) { $toc = $member; } // add root entry array_push( $result, $openTag, '## ' . (count($result) == 2 ? '' : '') . '`' . $member . '`', MarkdownGenerator::interpolate('* [`' . $member . '`](##{hash})', $entry) ); // add static and plugin sub-entries foreach (array('static', 'plugin') as $kind) { if ($kind == 'plugin' && count($entry->plugin)) { array_push( $result, $closeTag, $openTag, '## `' . $member . ($entry->isCtor() ? '.prototype`' : '`') ); } foreach ($entry->{$kind} as $subentry) { $subentry->member = $member; if ($subentry->isAlias()) { $result[] = MarkdownGenerator::interpolate('* `#{member}#{separator}#{name}` -> `#{realName}`', array( 'hash' => $subentry->hash, 'member' => $subentry->member, 'name' => $subentry->getName(), 'realName' => $subentry->owner->getName(), 'separator' => $subentry->separator )); } else { $result[] = MarkdownGenerator::interpolate('* `#{member}#{separator}#{name}`', $subentry); } } } } } array_push($result, $closeTag, $closeTag); /*------------------------------------------------------------------------*/ // compile content $compiling = false; $result[] = $openTag; if ($byCategory) { foreach ($categories as $category => $entries) { if ($compiling) { $result[] = $closeTag; } else { $compiling = true; } if ($category != 'Methods' && $category != 'Properties') { $category = '“' . $category . '” Methods'; } array_push($result, $openTag, '## `' . $category . '`'); $this->addEntries($result, $entries); } } else { foreach ($api as $entry) { // skip aliases if ($entry->isAlias()) { continue; } if ($compiling) { $result[] = $closeTag; } else { $compiling = true; } // add root entry name $member = $entry->member . $entry->getName(); array_push($result, $openTag, '## `' . $member . '`'); foreach (array($entry, 'static', 'plugin') as $kind) { $subentries = is_string($kind) ? $entry->{$kind} : array($kind); // add sub-entry name if ($kind != 'static' && $entry->getType() != 'Object' && count($subentries) && $subentries[0] != $kind) { if ($kind == 'plugin') { $result[] = $closeTag; } array_push( $result, $openTag, '## `' . $member . ($kind == 'plugin' ? '.prototype`' : '`') ); } $this->addEntries($result, $subentries); } } } // close tags add TOC link reference array_push($result, $closeTag, $closeTag, '', ' [1]: #' . $toc . ' "Jump back to the TOC."'); // cleanup whitespace return trim(preg_replace('/[\t ]+\n/', "\n", join($result, "\n"))); } } ?>lodash-2.4.1/vendor/docdown/src/DocDown/Entry.php0000644000175000017500000003046412247303154020032 0ustar ovdovdentry = $entry; $this->lang = $lang; $this->source = str_replace(PHP_EOL, "\n", $source); } /*--------------------------------------------------------------------------*/ /** * Extracts the documentation entries from source code. * * @static * @memberOf Entry * @param {string} $source The source code. * @returns {Array} The array of entries. */ public static function getEntries( $source ) { preg_match_all('#/\*\*(?![-!])[\s\S]*?\*/\s*.+#', $source, $result); return array_pop($result); } /*--------------------------------------------------------------------------*/ /** * Checks if the entry is a function reference. * * @private * @memberOf Entry * @returns {boolean} Returns `true` if the entry is a function reference, else `false`. */ private function isFunction() { if (!isset($this->_isFunction)) { $this->_isFunction = !!( $this->isCtor() || count($this->getParams()) || count($this->getReturns()) || preg_match('/\*[\t ]*@function\b/', $this->entry) || preg_match('#\*/\s*function #', $this->entry) ); } return $this->_isFunction; } /*--------------------------------------------------------------------------*/ /** * Extracts the entry's `alias` objects. * * @memberOf Entry * @param {number} $index The index of the array value to return. * @returns {Array|string} The entry's `alias` objects. */ public function getAliases( $index = null ) { if (!isset($this->_aliases)) { preg_match('#\*[\t ]*@alias\s+(.+)#', $this->entry, $result); if (count($result)) { $result = trim(preg_replace('/(?:^|\n)[\t ]*\*[\t ]?/', ' ', $result[1])); $result = preg_split('/,\s*/', $result); natsort($result); foreach ($result as $resultIndex => $value) { $result[$resultIndex] = new Alias($value, $this); } } $this->_aliases = $result; } return $index !== null ? @$this->_aliases[$index] : $this->_aliases; } /** * Extracts the function call from the entry. * * @memberOf Entry * @returns {string} The function call. */ public function getCall() { if (isset($this->_call)) { return $this->_call; } preg_match('#\*/\s*(?:function ([^(]*)|(.*?)(?=[:=,]|return\b))#', $this->entry, $result); if ($result = array_pop($result)) { $result = array_pop(explode('var ', trim(trim(array_pop(explode('.', $result))), "'"))); } // resolve name // avoid $this->getName() because it calls $this->getCall() preg_match('#\*[\t ]*@name\s+(.+)#', $this->entry, $name); if (count($name)) { $name = trim($name[1]); } else { $name = $result; } // compile function call syntax if ($this->isFunction()) { // compose parts $result = array($result); $params = $this->getParams(); foreach ($params as $param) { // skip params that are properties of other params (e.g. `options.leading`) if (!preg_match('/\w+\.[\w.]+\s*=/', $param[1])) { $result[] = $param[1]; } } // format $result = $name .'('. implode(array_slice($result, 1), ', ') .')'; } $this->_call = $result ? $result : $name; return $this->_call; } /** * Extracts the entry's `category` data. * * @memberOf Entry * @returns {string} The entry's `category` data. */ public function getCategory() { if (isset($this->_category)) { return $this->_category; } preg_match('#\*[\t ]*@category\s+(.+)#', $this->entry, $result); if (count($result)) { $result = trim(preg_replace('/(?:^|\n)[\t ]*\*[\t ]?/', ' ', $result[1])); } else { $result = $this->getType() == 'Function' ? 'Methods' : 'Properties'; } $this->_category = $result; return $result; } /** * Extracts the entry's description. * * @memberOf Entry * @returns {string} The entry's description. */ public function getDesc() { if (isset($this->_desc)) { return $this->_desc; } preg_match('#/\*\*(?:\s*\*)?([\s\S]*?)(?=\*\s\@[a-z]|\*/)#', $this->entry, $result); if (count($result)) { $type = $this->getType(); $result = preg_replace('/:\n[\t ]*\*[\t ]*/', ":
    \n", $result[1]); $result = preg_replace('/(?:^|\n)[\t ]*\*\n[\t ]*\*[\t ]*/', "\n\n", $result); $result = preg_replace('/(?:^|\n)[\t ]*\*[\t ]?/', ' ', $result); $result = trim($result); $result = ($type == 'Function' ? '' : '(' . str_replace('|', ', ', trim($type, '{}')) . '): ') . $result; } $this->_desc = $result; return $result; } /** * Extracts the entry's `example` data. * * @memberOf Entry * @returns {string} The entry's `example` data. */ public function getExample() { if (isset($this->_example)) { return $this->_example; } preg_match('#\*[\t ]*@example\s+([\s\S]*?)(?=\*\s\@[a-z]|\*/)#', $this->entry, $result); if (count($result)) { $result = trim(preg_replace('/(?:^|\n)[\t ]*\*[\t ]?/', "\n", $result[1])); $result = '```' . $this->lang . "\n" . $result . "\n```"; } $this->_example = $result; return $result; } /** * Checks if the entry is an alias. * * @memberOf Entry * @returns {boolean} Returns `false`. */ public function isAlias() { return false; } /** * Checks if the entry is a constructor. * * @memberOf Entry * @returns {boolean} Returns `true` if a constructor, else `false`. */ public function isCtor() { if (!isset($this->_isCtor)) { $this->_isCtor = !!preg_match('/\*[\t ]*@constructor\b/', $this->entry); } return $this->_isCtor; } /** * Checks if the entry is a license. * * @memberOf Entry * @returns {boolean} Returns `true` if a license, else `false`. */ public function isLicense() { if (!isset($this->_isLicense)) { $this->_isLicense = !!preg_match('/\*[\t ]*@license\b/', $this->entry); } return $this->_isLicense; } /** * Checks if the entry *is* assigned to a prototype. * * @memberOf Entry * @returns {boolean} Returns `true` if assigned to a prototype, else `false`. */ public function isPlugin() { if (!isset($this->_isPlugin)) { $this->_isPlugin = !$this->isCtor() && !$this->isPrivate() && !$this->isStatic(); } return $this->_isPlugin; } /** * Checks if the entry is private. * * @memberOf Entry * @returns {boolean} Returns `true` if private, else `false`. */ public function isPrivate() { if (!isset($this->_isPrivate)) { $this->_isPrivate = $this->isLicense() || !!preg_match('/\*[\t ]*@private\b/', $this->entry) || !preg_match('/\*[\t ]*@[a-z]+\b/', $this->entry); } return $this->_isPrivate; } /** * Checks if the entry is *not* assigned to a prototype. * * @memberOf Entry * @returns {boolean} Returns `true` if not assigned to a prototype, else `false`. */ public function isStatic() { if (isset($this->_isStatic)) { return $this->_isStatic; } $public = !$this->isPrivate(); $result = $public && !!preg_match('/\*[\t ]*@static\b/', $this->entry); // set in cases where it isn't explicitly stated if ($public && !$result) { if ($parent = array_pop(preg_split('/[#.]/', $this->getMembers(0)))) { foreach (Entry::getEntries($this->source) as $entry) { $entry = new Entry($entry, $this->source); if ($entry->getName() == $parent) { $result = !$entry->isCtor(); break; } } } else { $result = true; } } $this->_isStatic = $result; return $result; } /** * Resolves the entry's line number. * * @memberOf Entry * @returns {number} The entry's line number. */ public function getLineNumber() { if (!isset($this->_lineNumber)) { preg_match_all('/\n/', substr($this->source, 0, strrpos($this->source, $this->entry) + strlen($this->entry)), $lines); $this->_lineNumber = count(array_pop($lines)) + 1; } return $this->_lineNumber; } /** * Extracts the entry's `member` data. * * @memberOf Entry * @param {number} $index The index of the array value to return. * @returns {Array|string} The entry's `member` data. */ public function getMembers( $index = null ) { if (!isset($this->_members)) { preg_match('#\*[\t ]*@member(?:Of)?\s+(.+)#', $this->entry, $result); if (count($result)) { $result = trim(preg_replace('/(?:^|\n)[\t ]*\*[\t ]?/', ' ', $result[1])); $result = preg_split('/,\s*/', $result); natsort($result); } $this->_members = $result; } return $index !== null ? @$this->_members[$index] : $this->_members; } /** * Extracts the entry's `name` data. * * @memberOf Entry * @returns {string} The entry's `name` data. */ public function getName() { if (isset($this->_name)) { return $this->_name; } preg_match('#\*[\t ]*@name\s+(.+)#', $this->entry, $result); if (count($result)) { $result = trim(preg_replace('/(?:^|\n)[\t ]*\*[\t ]?/', ' ', $result[1])); } else { $result = array_shift(explode('(', $this->getCall())); } $this->_name = $result; return $result; } /** * Extracts the entry's `param` data. * * @memberOf Entry * @param {number} $index The index of the array value to return. * @returns {Array} The entry's `param` data. */ public function getParams( $index = null ) { if (!isset($this->_params)) { preg_match_all('#\*[\t ]*@param\s+\{\(?([^})]+)\)?\}\s+(\[.+\]|[$\w|]+(?:\[.+\])?)\s+([\s\S]*?)(?=\*\s\@[a-z]|\*/)#i', $this->entry, $matchTuples); $matchTuples = array_filter(array_slice($matchTuples, 1)); $result = array(); if (count($matchTuples)) { foreach ($matchTuples as $tupleKey => $tuple) { foreach ($tuple as $key => $value) { if (!isset($result[$key])) { $result[$key] = array(); } $result[$key][] = $tupleKey ? trim(preg_replace('/(?:^|\n)[\t ]*\*[\t ]*/', ' ', $value)) : trim($value); } } } $this->_params = $result; } return $index !== null ? @$this->_params[$index] : $this->_params; } /** * Extracts the entry's `returns` data. * * @memberOf Entry * @returns {string} The entry's `returns` data. */ public function getReturns() { if (isset($this->_returns)) { return $this->_returns; } preg_match('#\*[\t ]*@returns\s+\{([^}]+)\}\s+([\s\S]*?)(?=\*\s\@[a-z]|\*/)#', $this->entry, $result); if (count($result)) { $result = array_map('trim', array_slice($result, 1)); $result[0] = str_replace('|', ', ', $result[0]); $result[1] = preg_replace('/(?:^|\n)[\t ]*\*[\t ]?/', ' ', $result[1]); } $this->_returns = $result; return $result; } /** * Extracts the entry's `type` data. * * @memberOf Entry * @returns {string} The entry's `type` data. */ public function getType() { if (isset($this->_type)) { return $this->_type; } preg_match('#\*[\t ]*@type\s(?:\{\(?)?([^)}\n]+)#', $this->entry, $result); if (count($result)) { $result = trim($result[1]); if (preg_match('/^(?:array|function|object|regexp)$/', $result)) { $result = ucfirst($result); } } else { $result = $this->isFunction() ? 'Function' : 'unknown'; } $this->_type = $result; return $result; } } ?>lodash-2.4.1/vendor/docdown/docdown.php0000644000175000017500000000204712247303154016236 0ustar ovdovd * Available under MIT license */ require(dirname(__FILE__) . '/src/DocDown/MarkdownGenerator.php'); /** * Generates Markdown from JSDoc entries in a given file. * * @param {Array} [$options=array()] The options array. * @returns {string} The generated Markdown. * @example * * // specify a file path * $markdown = docdown(array( * // path to js file * 'path' => $filepath, * // url used to reference line numbers in code * 'url' => 'https://github.com/username/project/blob/master/my.js' * )); * * // or pass raw js * $markdown = docdown(array( * // raw JavaScript source * 'source' => $rawJS, * // documentation title * 'title' => 'My API Documentation', * // url used to reference line numbers in code * 'url' => 'https://github.com/username/project/blob/master/my.js' * )); */ function docdown( $options = array() ) { $gen = new MarkdownGenerator($options); return $gen->generate(); } ?>lodash-2.4.1/vendor/docdown/LICENSE.txt0000644000175000017500000000210112247303154015702 0ustar ovdovdCopyright 2011-2013 John-David Dalton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.lodash-2.4.1/vendor/curl/0000755000175000017500000000000012247303154013375 5ustar ovdovdlodash-2.4.1/vendor/curl/dist/0000755000175000017500000000000012247303154014340 5ustar ovdovdlodash-2.4.1/vendor/curl/dist/curl-kitchen-sink/0000755000175000017500000000000012321556674017704 5ustar ovdovdlodash-2.4.1/vendor/curl/dist/curl-kitchen-sink/curl.js0000644000175000017500000000000012321556674021175 0ustar ovdovdlodash-2.4.1/vendor/curl/LICENSE.txt0000644000175000017500000000222712247303154015223 0ustar ovdovdOpen Source Initiative OSI - The MIT License http://www.opensource.org/licenses/mit-license.php Copyright (c) 2010-2013 Brian Cavalier and John Hann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lodash-2.4.1/vendor/dojo/0000755000175000017500000000000012247303154013363 5ustar ovdovdlodash-2.4.1/vendor/dojo/dojo.js0000644000175000017500000020562412247303154014665 0ustar ovdovd(function( userConfig, defaultConfig ){ // summary: // This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with // any AMD-compliant loader via the package main module dojo/main. // description: // This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured // to operate in either synchronous or asynchronous modes. After the loader is defined, dojo is loaded // IAW the package main module dojo/main. In the event you wish to use a foreign loader, you may load dojo as a package // via the package main module dojo/main and this loader is not required; see dojo/package.json for details. // // In order to keep compatibility with the v1.x line, this loader includes additional machinery that enables // the dojo.provide, dojo.require et al API. This machinery is loaded by default, but may be dynamically removed // via the has.js API and statically removed via the build system. // // This loader includes sniffing machinery to determine the environment; the following environments are supported: // // - browser // - node.js // - rhino // // This is the so-called "source loader". As such, it includes many optional features that may be discarded by // building a customized version with the build system. // Design and Implementation Notes // // This is a dojo-specific adaption of bdLoad, donated to the dojo foundation by Altoviso LLC. // // This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition) // loader that can be configured to operate in either synchronous or asynchronous modes. // // Since this machinery implements a loader, it does not have the luxury of using a load system and/or // leveraging a utility library. This results in an unpleasantly long file; here is a road map of the contents: // // 1. Small library for use implementing the loader. // 2. Define the has.js API; this is used throughout the loader to bracket features. // 3. Define the node.js and rhino sniffs and sniff. // 4. Define the loader's data. // 5. Define the configuration machinery. // 6. Define the script element sniffing machinery and sniff for configuration data. // 7. Configure the loader IAW the provided user, default, and sniffing data. // 8. Define the global require function. // 9. Define the module resolution machinery. // 10. Define the module and plugin module definition machinery // 11. Define the script injection machinery. // 12. Define the window load detection. // 13. Define the logging API. // 14. Define the tracing API. // 16. Define the AMD define function. // 17. Define the dojo v1.x provide/require machinery--so called "legacy" modes. // 18. Publish global variables. // // Language and Acronyms and Idioms // // moduleId: a CJS module identifier, (used for public APIs) // mid: moduleId (used internally) // packageId: a package identifier (used for public APIs) // pid: packageId (used internally); the implied system or default package has pid==="" // pack: package is used internally to reference a package object (since javascript has reserved words including "package") // prid: plugin resource identifier // The integer constant 1 is used in place of true and 0 in place of false. // define a minimal library to help build the loader var noop = function(){ }, isEmpty = function(it){ for(var p in it){ return 0; } return 1; }, toString = {}.toString, isFunction = function(it){ return toString.call(it) == "[object Function]"; }, isString = function(it){ return toString.call(it) == "[object String]"; }, isArray = function(it){ return toString.call(it) == "[object Array]"; }, forEach = function(vector, callback){ if(vector){ for(var i = 0; vector[i];){ callback(vector[i++]); } } }, mix = function(dest, src){ for(var p in src){ dest[p] = src[p]; } return dest; }, makeError = function(error, info){ return mix(new Error(error), {src:"dojoLoader", info:info}); }, uidSeed = 1, uid = function(){ // Returns a unique identifier (within the lifetime of the document) of the form /_d+/. return "_" + uidSeed++; }, // FIXME: how to doc window.require() api // this will be the global require function; define it immediately so we can start hanging things off of it req = function( config, //(object, optional) hash of configuration properties dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before applying callback callback //(function, optional) lambda expression to apply to module values implied by dependencies ){ return contextRequire(config, dependencies, callback, 0, req); }, // the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout global = this, doc = global.document, element = doc && doc.createElement("DiV"), has = req.has = function(name){ return isFunction(hasCache[name]) ? (hasCache[name] = hasCache[name](global, doc, element)) : hasCache[name]; }, hasCache = has.cache = defaultConfig.hasCache; has.add = function(name, test, now, force){ (hasCache[name]===undefined || force) && (hasCache[name] = test); return now && has(name); }; has.add("host-node", userConfig.has && "host-node" in userConfig.has ? userConfig.has["host-node"] : (typeof process == "object" && process.versions && process.versions.node && process.versions.v8)); if(has("host-node")){ // fixup the default config for node.js environment require("./_base/configNode.js").config(defaultConfig); // remember node's require (with respect to baseUrl==dojo's root) defaultConfig.loaderPatch.nodeRequire = require; } has.add("host-rhino", userConfig.has && "host-rhino" in userConfig.has ? userConfig.has["host-rhino"] : (typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object"))); if(has("host-rhino")){ // owing to rhino's lame feature that hides the source of the script, give the user a way to specify the baseUrl... for(var baseUrl = userConfig.baseUrl || ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){ arg = (rhinoArgs[i++] + "").split("="); if(arg[0] == "baseUrl"){ baseUrl = arg[1]; break; } } load(baseUrl + "/_base/configRhino.js"); rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs); } // userConfig has tests override defaultConfig has tests; do this after the environment detection because // the environment detection usually sets some has feature values in the hasCache. for(var p in userConfig.has){ has.add(p, userConfig.has[p], 0, 1); } // // define the loader data // // the loader will use these like symbols if the loader has the traceApi; otherwise // define magic numbers so that modules can be provided as part of defaultConfig var requested = 1, arrived = 2, nonmodule = 3, executing = 4, executed = 5; if(has("dojo-trace-api")){ // these make debugging nice; but using strings for symbols is a gross rookie error; don't do it for production code requested = "requested"; arrived = "arrived"; nonmodule = "not-a-module"; executing = "executing"; executed = "executed"; } var legacyMode = 0, sync = "sync", xd = "xd", syncExecStack = [], dojoRequirePlugin = 0, checkDojoRequirePlugin = noop, transformToAmd = noop, getXhr; if(has("dojo-sync-loader")){ req.isXdUrl = noop; req.initSyncLoader = function(dojoRequirePlugin_, checkDojoRequirePlugin_, transformToAmd_){ // the first dojo/_base/loader loaded gets to define these variables; they are designed to work // in the presence of zero to many mapped dojo/_base/loaders if(!dojoRequirePlugin){ dojoRequirePlugin = dojoRequirePlugin_; checkDojoRequirePlugin = checkDojoRequirePlugin_; transformToAmd = transformToAmd_; } return { sync:sync, requested:requested, arrived:arrived, nonmodule:nonmodule, executing:executing, executed:executed, syncExecStack:syncExecStack, modules:modules, execQ:execQ, getModule:getModule, injectModule:injectModule, setArrived:setArrived, signal:signal, finishExec:finishExec, execModule:execModule, dojoRequirePlugin:dojoRequirePlugin, getLegacyMode:function(){return legacyMode;}, guardCheckComplete:guardCheckComplete }; }; if(has("dom")){ // in legacy sync mode, the loader needs a minimal XHR library var locationProtocol = location.protocol, locationHost = location.host; req.isXdUrl = function(url){ if(/^\./.test(url)){ // begins with a dot is always relative to page URL; therefore not xdomain return false; } if(/^\/\//.test(url)){ // for v1.6- backcompat, url starting with // indicates xdomain return true; } // get protocol and host // \/+ takes care of the typical file protocol that looks like file:///drive/path/to/file // locationHost is falsy if file protocol => if locationProtocol matches and is "file:", || will return false var match = url.match(/^([^\/\:]+\:)\/+([^\/]+)/); return match && (match[1] != locationProtocol || (locationHost && match[2] != locationHost)); }; // note: to get the file:// protocol to work in FF, you must set security.fileuri.strict_origin_policy to false in about:config has.add("dojo-xhr-factory", 1); has.add("dojo-force-activex-xhr", has("host-browser") && !doc.addEventListener && window.location.protocol == "file:"); has.add("native-xhr", typeof XMLHttpRequest != "undefined"); if(has("native-xhr") && !has("dojo-force-activex-xhr")){ getXhr = function(){ return new XMLHttpRequest(); }; }else{ // if in the browser an old IE; find an xhr for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){ try{ progid = XMLHTTP_PROGIDS[i++]; if(new ActiveXObject(progid)){ // this progid works; therefore, use it from now on break; } }catch(e){ // squelch; we're just trying to find a good ActiveX progid // if they all fail, then progid ends up as the last attempt and that will signal the error // the first time the client actually tries to exec an xhr } } getXhr = function(){ return new ActiveXObject(progid); }; } req.getXhr = getXhr; has.add("dojo-gettext-api", 1); req.getText = function(url, async, onLoad){ var xhr = getXhr(); xhr.open('GET', fixupUrl(url), false); xhr.send(null); if(xhr.status == 200 || (!location.host && !xhr.status)){ if(onLoad){ onLoad(xhr.responseText, async); } }else{ throw makeError("xhrFailed", xhr.status); } return xhr.responseText; }; } }else{ req.async = 1; } // // loader eval // var eval_ = // use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution new Function('return eval(arguments[0]);'); req.eval = function(text, hint){ return eval_(text + "\r\n////@ sourceURL=" + hint); }; // // loader micro events API // var listenerQueues = {}, error = "error", signal = req.signal = function(type, args){ var queue = listenerQueues[type]; // notice we run a copy of the queue; this allows listeners to add/remove // other listeners without affecting this particular signal forEach(queue && queue.slice(0), function(listener){ listener.apply(null, isArray(args) ? args : [args]); }); }, on = req.on = function(type, listener){ // notice a queue is not created until a client actually connects var queue = listenerQueues[type] || (listenerQueues[type] = []); queue.push(listener); return { remove:function(){ for(var i = 0; i (alias, actual) = [], paths // CommonJS paths = {}, pathsMapProg // list of (from-path, to-path, regex, length) derived from paths; // a "program" to apply paths; see computeMapProg = [], packs // a map from packageId to package configuration object; see fixupPackageInfo = {}, map = req.map // AMD map config variable; dojo/_base/kernel needs req.map to figure out the scope map = {}, mapProgs // vector of quads as described by computeMapProg; map-key is AMD map key, map-value is AMD map value = [], modules // A hash:(mid) --> (module-object) the module namespace // // pid: the package identifier to which the module belongs (e.g., "dojo"); "" indicates the system or default package // mid: the fully-resolved (i.e., mappings have been applied) module identifier without the package identifier (e.g., "dojo/io/script") // url: the URL from which the module was retrieved // pack: the package object of the package to which the module belongs // executed: 0 => not executed; executing => in the process of traversing deps and running factory; executed => factory has been executed // deps: the dependency vector for this module (vector of modules objects) // def: the factory for this module // result: the result of the running the factory for this module // injected: (0 | requested | arrived) the status of the module; nonmodule means the resource did not call define // load: plugin load function; applicable only for plugins // // Modules go through several phases in creation: // // 1. Requested: some other module's definition or a require application contained the requested module in // its dependency vector or executing code explicitly demands a module via req.require. // // 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL // // 3. Loaded: the resource injected in [2] has been evaluated. // // 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some // resources may just contain a bundle of code and never formally define a module via define // // 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result. = {}, cacheBust // query string to append to module URLs to bust browser cache = "", cache // hash:(mid | url)-->(function | string) // // A cache of resources. The resources arrive via a config.cache object, which is a hash from either mid --> function or // url --> string. The url key is distinguished from the mid key by always containing the prefix "url:". url keys as provided // by config.cache always have a string value that represents the contents of the resource at the given url. mid keys as provided // by configl.cache always have a function value that causes the same code to execute as if the module was script injected. // // Both kinds of key-value pairs are entered into cache via the function consumePendingCache, which may relocate keys as given // by any mappings *iff* the config.cache was received as part of a module resource request. // // Further, for mid keys, the implied url is computed and the value is entered into that key as well. This allows mapped modules // to retrieve cached items that may have arrived consequent to another namespace. // = {}, urlKeyPrefix // the prefix to prepend to a URL key in the cache. = "url:", pendingCacheInsert // hash:(mid)-->(function) // // Gives a set of cache modules pending entry into cache. When cached modules are published to the loader, they are // entered into pendingCacheInsert; modules are then pressed into cache upon (1) AMD define or (2) upon receiving another // independent set of cached modules. (1) is the usual case, and this case allows normalizing mids given in the pending // cache for the local configuration, possibly relocating modules. = {}, dojoSniffConfig // map of configuration variables // give the data-dojo-config as sniffed from the document (if any) = {}, insertPointSibling // the nodes used to locate where scripts are injected into the document = 0; if(has("dojo-config-api")){ var consumePendingCacheInsert = function(referenceModule){ var p, item, match, now, m; for(p in pendingCacheInsert){ item = pendingCacheInsert[p]; match = p.match(/^url\:(.+)/); if(match){ cache[urlKeyPrefix + toUrl(match[1], referenceModule)] = item; }else if(p=="*now"){ now = item; }else if(p!="*noref"){ m = getModuleInfo(p, referenceModule); cache[m.mid] = cache[urlKeyPrefix + m.url] = item; } } if(now){ now(createRequire(referenceModule)); } pendingCacheInsert = {}; }, escapeString = function(s){ return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(c){ return "\\" + c; }); }, computeMapProg = function(map, dest){ // This routine takes a map as represented by a JavaScript object and initializes dest, a vector of // quads of (map-key, map-value, refex-for-map-key, length-of-map-key), sorted decreasing by length- // of-map-key. The regex looks for the map-key followed by either "/" or end-of-string at the beginning // of a the search source. Notice the map-value is irrelevant to the algorithm dest.splice(0, dest.length); for(var p in map){ dest.push([ p, map[p], new RegExp("^" + escapeString(p) + "(\/|$)"), p.length]); } dest.sort(function(lhs, rhs){ return rhs[3] - lhs[3]; }); return dest; }, computeAliases = function(config, dest){ forEach(config, function(pair){ // take a fixed-up copy... dest.push([isString(pair[0]) ? new RegExp("^" + escapeString(pair[0]) + "$") : pair[0], pair[1]]); }); }, fixupPackageInfo = function(packageInfo){ // calculate the precise (name, location, main, mappings) for a package var name = packageInfo.name; if(!name){ // packageInfo must be a string that gives the name name = packageInfo; packageInfo = {name:name}; } packageInfo = mix({main:"main"}, packageInfo); packageInfo.location = packageInfo.location ? packageInfo.location : name; // packageMap is deprecated in favor of AMD map if(packageInfo.packageMap){ map[name] = packageInfo.packageMap; } if(!packageInfo.main.indexOf("./")){ packageInfo.main = packageInfo.main.substring(2); } // now that we've got a fully-resolved package object, push it into the configuration packs[name] = packageInfo; }, delayedModuleConfig // module config cannot be consumed until the loader is completely initialized; therefore, all // module config detected during booting is memorized and applied at the end of loader initialization // TODO: this is a bit of a kludge; all config should be moved to end of loader initialization, but // we'll delay this chore and do it with a final loader 1.x cleanup after the 2.x loader prototyping is complete = [], config = function(config, booting, referenceModule){ for(var p in config){ if(p=="waitSeconds"){ req.waitms = (config[p] || 0) * 1000; } if(p=="cacheBust"){ cacheBust = config[p] ? (isString(config[p]) ? config[p] : (new Date()).getTime() + "") : ""; } if(p=="baseUrl" || p=="combo"){ req[p] = config[p]; } if(has("dojo-sync-loader") && p=="async"){ // falsy or "sync" => legacy sync loader // "xd" => sync but loading xdomain tree and therefore loading asynchronously (not configurable, set automatically by the loader) // "legacyAsync" => permanently in "xd" by choice // "debugAtAllCosts" => trying to load everything via script injection (not implemented) // otherwise, must be truthy => AMD // legacyMode: sync | legacyAsync | xd | false var mode = config[p]; req.legacyMode = legacyMode = (isString(mode) && /sync|legacyAsync/.test(mode) ? mode : (!mode ? sync : false)); req.async = !legacyMode; } if(config[p]!==hasCache){ // accumulate raw config info for client apps which can use this to pass their own config req.rawConfig[p] = config[p]; p!="has" && has.add("config-"+p, config[p], 0, booting); } } // make sure baseUrl exists if(!req.baseUrl){ req.baseUrl = "./"; } // make sure baseUrl ends with a slash if(!/\/$/.test(req.baseUrl)){ req.baseUrl += "/"; } // now do the special work for has, packages, packagePaths, paths, aliases, and cache for(p in config.has){ has.add(p, config.has[p], 0, booting); } // for each package found in any packages config item, augment the packs map owned by the loader forEach(config.packages, fixupPackageInfo); // for each packagePath found in any packagePaths config item, augment the packageConfig // packagePaths is deprecated; remove in 2.0 for(baseUrl in config.packagePaths){ forEach(config.packagePaths[baseUrl], function(packageInfo){ var location = baseUrl + "/" + packageInfo; if(isString(packageInfo)){ packageInfo = {name:packageInfo}; } packageInfo.location = location; fixupPackageInfo(packageInfo); }); } // notice that computeMapProg treats the dest as a reference; therefore, if/when that variable // is published (see dojo-publish-privates), the published variable will always hold a valid value. // this must come after all package processing since package processing may mutate map computeMapProg(mix(map, config.map), mapProgs); forEach(mapProgs, function(item){ item[1] = computeMapProg(item[1], []); if(item[0]=="*"){ mapProgs.star = item; } }); // push in any paths and recompute the internal pathmap computeMapProg(mix(paths, config.paths), pathsMapProg); // aliases computeAliases(config.aliases, aliases); if(booting){ delayedModuleConfig.push({config:config.config}); }else{ for(p in config.config){ var module = getModule(p, referenceModule); module.config = mix(module.config || {}, config.config[p]); } } // push in any new cache values if(config.cache){ consumePendingCacheInsert(); pendingCacheInsert = config.cache; if(config.cache["*noref"]){ consumePendingCacheInsert(); } } signal("config", [config, req.rawConfig]); }; // // execute the various sniffs; userConfig can override and value // if(has("dojo-cdn") || has("dojo-sniff")){ // the sniff regex looks for a src attribute ending in dojo.js, optionally preceded with a path. // match[3] returns the path to dojo.js (if any) without the trailing slash. This is used for the // dojo location on CDN deployments and baseUrl when either/both of these are not provided // explicitly in the config data; this is the 1.6- behavior. var scripts = doc.getElementsByTagName("script"), i = 0, script, dojoDir, src, match; while(i < scripts.length){ script = scripts[i++]; if((src = script.getAttribute("src")) && (match = src.match(/(((.*)\/)|^)dojo\.js(\W|$)/i))){ // sniff dojoDir and baseUrl dojoDir = match[3] || ""; defaultConfig.baseUrl = defaultConfig.baseUrl || dojoDir; // remember an insertPointSibling insertPointSibling = script; } // sniff configuration on attribute in script element if((src = (script.getAttribute("data-dojo-config") || script.getAttribute("djConfig")))){ dojoSniffConfig = req.eval("({ " + src + " })", "data-dojo-config"); // remember an insertPointSibling insertPointSibling = script; } // sniff requirejs attribute if(has("dojo-requirejs-api")){ if((src = script.getAttribute("data-main"))){ dojoSniffConfig.deps = dojoSniffConfig.deps || [src]; } } } } if(has("dojo-test-sniff")){ // pass down doh.testConfig from parent as if it were a data-dojo-config try{ if(window.parent != window && window.parent.require){ var doh = window.parent.require("doh"); doh && mix(dojoSniffConfig, doh.testConfig); } }catch(e){} } // configure the loader; let the user override defaults req.rawConfig = {}; config(defaultConfig, 1); // do this before setting userConfig/sniffConfig to allow userConfig/sniff overrides if(has("dojo-cdn")){ packs.dojo.location = dojoDir; if(dojoDir){ dojoDir += "/"; } packs.dijit.location = dojoDir + "../dijit/"; packs.dojox.location = dojoDir + "../dojox/"; } config(userConfig, 1); config(dojoSniffConfig, 1); }else{ // no config API, assume defaultConfig has everything the loader needs...for the entire lifetime of the application paths = defaultConfig.paths; pathsMapProg = defaultConfig.pathsMapProg; packs = defaultConfig.packs; aliases = defaultConfig.aliases; mapProgs = defaultConfig.mapProgs; modules = defaultConfig.modules; cache = defaultConfig.cache; cacheBust = defaultConfig.cacheBust; // remember the default config for other processes (e.g., dojo/config) req.rawConfig = defaultConfig; } if(has("dojo-combo-api")){ req.combo = req.combo || {add:noop}; var comboPending = 0, combosPending = [], comboPendingTimer = null; } // build the loader machinery iaw configuration, including has feature tests var injectDependencies = function(module){ // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies guardCheckComplete(function(){ forEach(module.deps, injectModule); if(has("dojo-combo-api") && comboPending && !comboPendingTimer){ comboPendingTimer = setTimeout(function() { comboPending = 0; comboPendingTimer = null; req.combo.done(function(mids, url) { var onLoadCallback= function(){ // defQ is a vector of module definitions 1-to-1, onto mids runDefQ(0, mids); checkComplete(); }; combosPending.push(mids); injectingModule = mids; req.injectUrl(url, onLoadCallback, mids); injectingModule = 0; }, req); }, 0); } }); }, contextRequire = function(a1, a2, a3, referenceModule, contextRequire){ var module, syntheticMid; if(isString(a1)){ // signature is (moduleId) module = getModule(a1, referenceModule, true); if(module && module.executed){ return module.result; } throw makeError("undefinedModule", a1); } if(!isArray(a1)){ // a1 is a configuration config(a1, 0, referenceModule); // juggle args; (a2, a3) may be (dependencies, callback) a1 = a2; a2 = a3; } if(isArray(a1)){ // signature is (requestList [,callback]) if(!a1.length){ a2 && a2(); }else{ syntheticMid = "require*" + uid(); // resolve the request list with respect to the reference module for(var mid, deps = [], i = 0; i < a1.length;){ mid = a1[i++]; deps.push(getModule(mid, referenceModule)); } // construct a synthetic module to control execution of the requestList, and, optionally, callback module = mix(makeModuleInfo("", syntheticMid, 0, ""), { injected: arrived, deps: deps, def: a2 || noop, require: referenceModule ? referenceModule.require : req, gc: 1 //garbage collect }); modules[module.mid] = module; // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies injectDependencies(module); // try to immediately execute // if already traversing a factory tree, then strict causes circular dependency to abort the execution; maybe // it's possible to execute this require later after the current traversal completes and avoid the circular dependency. // ...but *always* insist on immediate in synch mode var strict = checkCompleteGuard && legacyMode!=sync; guardCheckComplete(function(){ execModule(module, strict); }); if(!module.executed){ // some deps weren't on board or circular dependency detected and strict; therefore, push into the execQ execQ.push(module); } checkComplete(); } } return contextRequire; }, createRequire = function(module){ if(!module){ return req; } var result = module.require; if(!result){ result = function(a1, a2, a3){ return contextRequire(a1, a2, a3, module, result); }; module.require = mix(result, req); result.module = module; result.toUrl = function(name){ return toUrl(name, module); }; result.toAbsMid = function(mid){ return toAbsMid(mid, module); }; if(has("dojo-undef-api")){ result.undef = function(mid){ req.undef(mid, module); }; } if(has("dojo-sync-loader")){ result.syncLoadNls = function(mid){ var nlsModuleInfo = getModuleInfo(mid, module), nlsModule = modules[nlsModuleInfo.mid]; if(!nlsModule || !nlsModule.executed){ cached = cache[nlsModuleInfo.mid] || cache[urlKeyPrefix + nlsModuleInfo.url]; if(cached){ evalModuleText(cached); nlsModule = modules[nlsModuleInfo.mid]; } } return nlsModule && nlsModule.executed && nlsModule.result; }; } } return result; }, execQ = // The list of modules that need to be evaluated. [], defQ = // The queue of define arguments sent to loader. [], waiting = // The set of modules upon which the loader is waiting for definition to arrive {}, setRequested = function(module){ module.injected = requested; waiting[module.mid] = 1; if(module.url){ waiting[module.url] = module.pack || 1; } startTimer(); }, setArrived = function(module){ module.injected = arrived; delete waiting[module.mid]; if(module.url){ delete waiting[module.url]; } if(isEmpty(waiting)){ clearTimer(); has("dojo-sync-loader") && legacyMode==xd && (legacyMode = sync); } }, execComplete = req.idle = // says the loader has completed (or not) its work function(){ return !defQ.length && isEmpty(waiting) && !execQ.length && !checkCompleteGuard; }, runMapProg = function(targetMid, map){ // search for targetMid in map; return the map item if found; falsy otherwise if(map){ for(var i = 0; i < map.length; i++){ if(map[i][2].test(targetMid)){ return map[i]; } } } return 0; }, compactPath = function(path){ var result = [], segment, lastSegment; path = path.replace(/\\/g, '/').split('/'); while(path.length){ segment = path.shift(); if(segment==".." && result.length && lastSegment!=".."){ result.pop(); lastSegment = result[result.length - 1]; }else if(segment!="."){ result.push(lastSegment= segment); } // else ignore "." } return result.join("/"); }, makeModuleInfo = function(pid, mid, pack, url){ if(has("dojo-sync-loader")){ var xd= req.isXdUrl(url); return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0, isXd:xd, isAmd:!!(xd || (packs[pid] && packs[pid].isAmd))}; }else{ return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0}; } }, getModuleInfo_ = function(mid, referenceModule, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate){ // arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g., the builder) // alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader var pid, pack, midInPackage, mapItem, url, result, isRelative, requestedMid; requestedMid = mid; isRelative = /^\./.test(mid); if(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){ // absolute path or protocol of .js filetype, or relative path but no reference module and therefore relative to page // whatever it is, it's not a module but just a URL of some sort // note: pid===0 indicates the routine is returning an unmodified mid return makeModuleInfo(0, mid, 0, mid); }else{ // relative module ids are relative to the referenceModule; get rid of any dots mid = compactPath(isRelative ? (referenceModule.mid + "/../" + mid) : mid); if(/^\./.test(mid)){ throw makeError("irrationalPath", mid); } // at this point, mid is an absolute mid // map the mid if(referenceModule){ mapItem = runMapProg(referenceModule.mid, mapProgs); } mapItem = mapItem || mapProgs.star; mapItem = mapItem && runMapProg(mid, mapItem[1]); if(mapItem){ mid = mapItem[1] + mid.substring(mapItem[3]); } match = mid.match(/^([^\/]+)(\/(.+))?$/); pid = match ? match[1] : ""; if((pack = packs[pid])){ mid = pid + "/" + (midInPackage = (match[3] || pack.main)); }else{ pid = ""; } // search aliases var candidateLength = 0, candidate = 0; forEach(aliases, function(pair){ var match = mid.match(pair[0]); if(match && match.length>candidateLength){ candidate = isFunction(pair[1]) ? mid.replace(pair[0], pair[1]) : pair[1]; } }); if(candidate){ return getModuleInfo_(candidate, 0, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate); } result = modules[mid]; if(result){ return alwaysCreate ? makeModuleInfo(result.pid, result.mid, result.pack, result.url) : modules[mid]; } } // get here iff the sought-after module does not yet exist; therefore, we need to compute the URL given the // fully resolved (i.e., all relative indicators and package mapping resolved) module id // note: pid!==0 indicates the routine is returning a url that has .js appended unmodified mid mapItem = runMapProg(mid, pathsMapProg); if(mapItem){ url = mapItem[1] + mid.substring(mapItem[3]); }else if(pid){ url = pack.location + "/" + midInPackage; }else if(has("config-tlmSiblingOfDojo")){ url = "../" + mid; }else{ url = mid; } // if result is not absolute, add baseUrl if(!(/(^\/)|(\:)/.test(url))){ url = baseUrl + url; } url += ".js"; return makeModuleInfo(pid, mid, pack, compactPath(url)); }, getModuleInfo = function(mid, referenceModule){ return getModuleInfo_(mid, referenceModule, packs, modules, req.baseUrl, mapProgs, pathsMapProg, aliases); }, resolvePluginResourceId = function(plugin, prid, referenceModule){ return plugin.normalize ? plugin.normalize(prid, function(mid){return toAbsMid(mid, referenceModule);}) : toAbsMid(prid, referenceModule); }, dynamicPluginUidGenerator = 0, getModule = function(mid, referenceModule, immediate){ // compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule var match, plugin, prid, result; match = mid.match(/^(.+?)\!(.*)$/); if(match){ // name was ! plugin = getModule(match[1], referenceModule, immediate); if(has("dojo-sync-loader") && legacyMode == sync && !plugin.executed){ injectModule(plugin); if(plugin.injected===arrived && !plugin.executed){ guardCheckComplete(function(){ execModule(plugin); }); } if(plugin.executed){ promoteModuleToPlugin(plugin); }else{ // we are in xdomain mode for some reason execQ.unshift(plugin); } } if(plugin.executed === executed && !plugin.load){ // executed the module not knowing it was a plugin promoteModuleToPlugin(plugin); } // if the plugin has not been loaded, then can't resolve the prid and must assume this plugin is dynamic until we find out otherwise if(plugin.load){ prid = resolvePluginResourceId(plugin, match[2], referenceModule); mid = (plugin.mid + "!" + (plugin.dynamic ? ++dynamicPluginUidGenerator + "!" : "") + prid); }else{ prid = match[2]; mid = plugin.mid + "!" + (++dynamicPluginUidGenerator) + "!waitingForPlugin"; } result = {plugin:plugin, mid:mid, req:createRequire(referenceModule), prid:prid}; }else{ result = getModuleInfo(mid, referenceModule); } return modules[result.mid] || (!immediate && (modules[result.mid] = result)); }, toAbsMid = req.toAbsMid = function(mid, referenceModule){ return getModuleInfo(mid, referenceModule).mid; }, toUrl = req.toUrl = function(name, referenceModule){ var moduleInfo = getModuleInfo(name+"/x", referenceModule), url= moduleInfo.url; return fixupUrl(moduleInfo.pid===0 ? // if pid===0, then name had a protocol or absolute path; either way, toUrl is the identify function in such cases name : // "/x.js" since getModuleInfo automatically appends ".js" and we appended "/x" to make name look like a module id url.substring(0, url.length-5) ); }, nonModuleProps = { injected: arrived, executed: executed, def: nonmodule, result: nonmodule }, makeCjs = function(mid){ return modules[mid] = mix({mid:mid}, nonModuleProps); }, cjsRequireModule = makeCjs("require"), cjsExportsModule = makeCjs("exports"), cjsModuleModule = makeCjs("module"), runFactory = function(module, args){ req.trace("loader-run-factory", [module.mid]); var factory = module.def, result; has("dojo-sync-loader") && syncExecStack.unshift(module); if(has("config-dojo-loader-catches")){ try{ result= isFunction(factory) ? factory.apply(null, args) : factory; }catch(e){ signal(error, module.result = makeError("factoryThrew", [module, e])); } }else{ result= isFunction(factory) ? factory.apply(null, args) : factory; } module.result = result===undefined && module.cjs ? module.cjs.exports : result; has("dojo-sync-loader") && syncExecStack.shift(module); }, abortExec = {}, defOrder = 0, promoteModuleToPlugin = function(pluginModule){ var plugin = pluginModule.result; pluginModule.dynamic = plugin.dynamic; pluginModule.normalize = plugin.normalize; pluginModule.load = plugin.load; return pluginModule; }, resolvePluginLoadQ = function(plugin){ // plugins is a newly executed module that has a loadQ waiting to run // step 1: traverse the loadQ and fixup the mid and prid; remember the map from original mid to new mid // recall the original mid was created before the plugin was on board and therefore it was impossible to // compute the final mid; accordingly, prid may or may not change, but the mid will definitely change var map = {}; forEach(plugin.loadQ, function(pseudoPluginResource){ // manufacture and insert the real module in modules var prid = resolvePluginResourceId(plugin, pseudoPluginResource.prid, pseudoPluginResource.req.module), mid = plugin.dynamic ? pseudoPluginResource.mid.replace(/waitingForPlugin$/, prid) : (plugin.mid + "!" + prid), pluginResource = mix(mix({}, pseudoPluginResource), {mid:mid, prid:prid, injected:0}); if(!modules[mid]){ // create a new (the real) plugin resource and inject it normally now that the plugin is on board injectPlugin(modules[mid] = pluginResource); } // else this was a duplicate request for the same (plugin, rid) for a nondynamic plugin // pluginResource is really just a placeholder with the wrong mid (because we couldn't calculate it until the plugin was on board) // mark is as arrived and delete it from modules; the real module was requested above map[pseudoPluginResource.mid] = modules[mid]; setArrived(pseudoPluginResource); delete modules[pseudoPluginResource.mid]; }); plugin.loadQ = 0; // step2: replace all references to any placeholder modules with real modules var substituteModules = function(module){ for(var replacement, deps = module.deps || [], i = 0; i")]); return (!module.def || strict) ? abortExec : (module.cjs && module.cjs.exports); } // at this point the module is either not executed or fully executed if(!module.executed){ if(!module.def){ return abortExec; } var mid = module.mid, deps = module.deps || [], arg, argResult, args = [], i = 0; if(has("dojo-trace-api")){ circleTrace.push(mid); req.trace("loader-exec-module", ["exec", circleTrace.length, mid]); } // for circular dependencies, assume the first module encountered was executed OK // modules that circularly depend on a module that has not run its factory will get // the pre-made cjs.exports===module.result. They can take a reference to this object and/or // add properties to it. When the module finally runs its factory, the factory can // read/write/replace this object. Notice that so long as the object isn't replaced, any // reference taken earlier while walking the deps list is still valid. module.executed = executing; while((arg = deps[i++])){ argResult = ((arg === cjsRequireModule) ? createRequire(module) : ((arg === cjsExportsModule) ? module.cjs.exports : ((arg === cjsModuleModule) ? module.cjs : execModule(arg, strict)))); if(argResult === abortExec){ module.executed = 0; req.trace("loader-exec-module", ["abort", mid]); has("dojo-trace-api") && circleTrace.pop(); return abortExec; } args.push(argResult); } runFactory(module, args); finishExec(module); has("dojo-trace-api") && circleTrace.pop(); } // at this point the module is guaranteed fully executed return module.result; }, checkCompleteGuard = 0, guardCheckComplete = function(proc){ try{ checkCompleteGuard++; proc(); }finally{ checkCompleteGuard--; } if(execComplete()){ signal("idle", []); } }, checkComplete = function(){ // keep going through the execQ as long as at least one factory is executed // plugins, recursion, cached modules all make for many execution path possibilities if(checkCompleteGuard){ return; } guardCheckComplete(function(){ checkDojoRequirePlugin(); for(var currentDefOrder, module, i = 0; i < execQ.length;){ currentDefOrder = defOrder; module = execQ[i]; execModule(module); if(currentDefOrder!=defOrder){ // defOrder was bumped one or more times indicating something was executed (note, this indicates // the execQ was modified, maybe a lot (for example a later module causes an earlier module to execute) checkDojoRequirePlugin(); i = 0; }else{ // nothing happened; check the next module in the exec queue i++; } } }); }; if(has("dojo-undef-api")){ req.undef = function(moduleId, referenceModule){ // In order to reload a module, it must be undefined (this routine) and then re-requested. // This is useful for testing frameworks (at least). var module = getModule(moduleId, referenceModule); setArrived(module); mix(module, {def:0, executed:0, injected:0, node:0}); }; } if(has("dojo-inject-api")){ if(has("dojo-loader-eval-hint-url")===undefined){ has.add("dojo-loader-eval-hint-url", 1); } var fixupUrl= function(url){ url += ""; // make sure url is a Javascript string (some paths may be a Java string) return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : ""); }, injectPlugin = function( module ){ // injects the plugin module given by module; may have to inject the plugin itself var plugin = module.plugin; if(plugin.executed === executed && !plugin.load){ // executed the module not knowing it was a plugin promoteModuleToPlugin(plugin); } var onLoad = function(def){ module.result = def; setArrived(module); finishExec(module); checkComplete(); }; if(plugin.load){ plugin.load(module.prid, module.req, onLoad); }else if(plugin.loadQ){ plugin.loadQ.push(module); }else{ // the unshift instead of push is important: we don't want plugins to execute as // dependencies of some other module because this may cause circles when the plugin // loadQ is run; also, generally, we want plugins to run early since they may load // several other modules and therefore can potentially unblock many modules plugin.loadQ = [module]; execQ.unshift(plugin); injectModule(plugin); } }, // for IE, injecting a module may result in a recursive execution if the module is in the cache cached = 0, injectingModule = 0, injectingCachedModule = 0, evalModuleText = function(text, module){ // see def() for the injectingCachedModule bracket; it simply causes a short, safe circuit if(has("config-stripStrict")){ text = text.replace(/"use strict"/g, ''); } injectingCachedModule = 1; if(has("config-dojo-loader-catches")){ try{ if(text===cached){ cached.call(null); }else{ req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); } }catch(e){ signal(error, makeError("evalModuleThrew", module)); } }else{ if(text===cached){ cached.call(null); }else{ req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); } } injectingCachedModule = 0; }, injectModule = function(module){ // Inject the module. In the browser environment, this means appending a script element into // the document; in other environments, it means loading a file. // // If in synchronous mode, then get the module synchronously if it's not xdomainLoading. var mid = module.mid, url = module.url; if(module.executed || module.injected || waiting[mid] || (module.url && ((module.pack && waiting[module.url]===module.pack) || waiting[module.url]==1))){ return; } setRequested(module); if(has("dojo-combo-api")){ var viaCombo = 0; if(module.plugin && module.plugin.isCombo){ // a combo plugin; therefore, must be handled by combo service // the prid should have already been converted to a URL (if required by the plugin) during // the normalize process; in any event, there is no way for the loader to know how to // to the conversion; therefore the third argument is zero req.combo.add(module.plugin.mid, module.prid, 0, req); viaCombo = 1; }else if(!module.plugin){ viaCombo = req.combo.add(0, module.mid, module.url, req); } if(viaCombo){ comboPending= 1; return; } } if(module.plugin){ injectPlugin(module); return; } // else a normal module (not a plugin) var onLoadCallback = function(){ runDefQ(module); if(module.injected !== arrived){ // the script that contained the module arrived and has been executed yet // nothing was added to the defQ (so it wasn't an AMD module) and the module // wasn't marked as arrived by dojo.provide (so it wasn't a v1.6- module); // therefore, it must not have been a module; adjust state accordingly if(has("dojo-enforceDefine")){ signal(error, makeError("noDefine", module)); return; } setArrived(module); mix(module, nonModuleProps); req.trace("loader-define-nonmodule", [module.url]); } if(has("dojo-sync-loader") && legacyMode){ // must call checkComplete even in for sync loader because we may be in xdomainLoading mode; // but, if xd loading, then don't call checkComplete until out of the current sync traversal // in order to preserve order of execution of the dojo.required modules !syncExecStack.length && checkComplete(); }else{ checkComplete(); } }; cached = cache[mid] || cache[urlKeyPrefix + module.url]; if(cached){ req.trace("loader-inject", ["cache", module.mid, url]); evalModuleText(cached, module); onLoadCallback(); return; } if(has("dojo-sync-loader") && legacyMode){ if(module.isXd){ // switch to async mode temporarily; if current legacyMode!=sync, then is must be one of {legacyAsync, xd, false} legacyMode==sync && (legacyMode = xd); // fall through and load via script injection }else if(module.isAmd && legacyMode!=sync){ // fall through and load via script injection }else{ // mode may be sync, xd/legacyAsync, or async; module may be AMD or legacy; but module is always located on the same domain var xhrCallback = function(text){ if(legacyMode==sync){ // the top of syncExecStack gives the current synchronously executing module; the loader needs // to know this if it has to switch to async loading in the middle of evaluating a legacy module // this happens when a modules dojo.require's a module that must be loaded async because it's xdomain // (using unshift/shift because there is no back() methods for Javascript arrays) syncExecStack.unshift(module); evalModuleText(text, module); syncExecStack.shift(); // maybe the module was an AMD module runDefQ(module); // legacy modules never get to defineModule() => cjs and injected never set; also evaluation implies executing if(!module.cjs){ setArrived(module); finishExec(module); } if(module.finish){ // while synchronously evaluating this module, dojo.require was applied referencing a module // that had to be loaded async; therefore, the loader stopped answering all dojo.require // requests so they could be answered completely in the correct sequence; module.finish gives // the list of dojo.requires that must be re-applied once all target modules are available; // make a synthetic module to execute the dojo.require's in the correct order // compute a guaranteed-unique mid for the synthetic finish module; remember the finish vector; remove it from the reference module // TODO: can we just leave the module.finish...what's it hurting? var finishMid = mid + "*finish", finish = module.finish; delete module.finish; def(finishMid, ["dojo", ("dojo/require!" + finish.join(",")).replace(/\./g, "/")], function(dojo){ forEach(finish, function(mid){ dojo.require(mid); }); }); // unshift, not push, which causes the current traversal to be reattempted from the top execQ.unshift(getModule(finishMid)); } onLoadCallback(); }else{ text = transformToAmd(module, text); if(text){ evalModuleText(text, module); onLoadCallback(); }else{ // if transformToAmd returned falsy, then the module was already AMD and it can be script-injected // do so to improve debugability(even though it means another download...which probably won't happen with a good browser cache) injectingModule = module; req.injectUrl(fixupUrl(url), onLoadCallback, module); injectingModule = 0; } } }; req.trace("loader-inject", ["xhr", module.mid, url, legacyMode!=sync]); if(has("config-dojo-loader-catches")){ try{ req.getText(url, legacyMode!=sync, xhrCallback); }catch(e){ signal(error, makeError("xhrInjectFailed", [module, e])); } }else{ req.getText(url, legacyMode!=sync, xhrCallback); } return; } } // else async mode or fell through in xdomain loading mode; either way, load by script injection req.trace("loader-inject", ["script", module.mid, url]); injectingModule = module; req.injectUrl(fixupUrl(url), onLoadCallback, module); injectingModule = 0; }, defineModule = function(module, deps, def){ req.trace("loader-define-module", [module.mid, deps]); if(has("dojo-combo-api") && module.plugin && module.plugin.isCombo){ // the module is a plugin resource loaded by the combo service // note: check for module.plugin should be enough since normal plugin resources should // not follow this path; module.plugin.isCombo is future-proofing belt and suspenders module.result = isFunction(def) ? def() : def; setArrived(module); finishExec(module); return module; } var mid = module.mid; if(module.injected === arrived){ signal(error, makeError("multipleDefine", module)); return module; } mix(module, { deps: deps, def: def, cjs: { id: module.mid, uri: module.url, exports: (module.result = {}), setExports: function(exports){ module.cjs.exports = exports; }, config:function(){ return module.config; } } }); // resolve deps with respect to this module for(var i = 0; deps[i]; i++){ deps[i] = getModule(deps[i], module); } if(has("dojo-sync-loader") && legacyMode && !waiting[mid]){ // the module showed up without being asked for; it was probably in a " ); iDoc.close(); test("isElement", function() { ok(!_.isElement('div'), 'strings are not dom elements'); ok(_.isElement($('html')[0]), 'the html tag is a DOM element'); ok(_.isElement(iElement), 'even from another frame'); }); test("isArguments", function() { var args = (function(){ return arguments; })(1, 2, 3); ok(!_.isArguments('string'), 'a string is not an arguments object'); ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); ok(_.isArguments(args), 'but the arguments object is an arguments object'); ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.'); ok(_.isArguments(iArguments), 'even from another frame'); }); test("isObject", function() { ok(_.isObject(arguments), 'the arguments object is object'); ok(_.isObject([1, 2, 3]), 'and arrays'); ok(_.isObject($('html')[0]), 'and DOM element'); ok(_.isObject(iElement), 'even from another frame'); ok(_.isObject(function () {}), 'and functions'); ok(_.isObject(iFunction), 'even from another frame'); ok(!_.isObject(null), 'but not null'); ok(!_.isObject(undefined), 'and not undefined'); ok(!_.isObject('string'), 'and not string'); ok(!_.isObject(12), 'and not number'); ok(!_.isObject(true), 'and not boolean'); ok(_.isObject(new String('string')), 'but new String()'); }); test("isArray", function() { ok(!_.isArray(undefined), 'undefined vars are not arrays'); ok(!_.isArray(arguments), 'the arguments object is not an array'); ok(_.isArray([1, 2, 3]), 'but arrays are'); ok(_.isArray(iArray), 'even from another frame'); }); test("isString", function() { var obj = new String("I am a string object"); ok(!_.isString(document.body), 'the document body is not a string'); ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); ok(_.isString(iString), 'even from another frame'); ok(_.isString("I am a string literal"), 'string literals are'); ok(_.isString(obj), 'so are String objects'); }); test("isNumber", function() { ok(!_.isNumber('string'), 'a string is not a number'); ok(!_.isNumber(arguments), 'the arguments object is not a number'); ok(!_.isNumber(undefined), 'undefined is not a number'); ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); ok(_.isNumber(NaN), 'NaN *is* a number'); ok(_.isNumber(Infinity), 'Infinity is a number'); ok(_.isNumber(iNumber), 'even from another frame'); ok(!_.isNumber('1'), 'numeric strings are not numbers'); }); test("isBoolean", function() { ok(!_.isBoolean(2), 'a number is not a boolean'); ok(!_.isBoolean("string"), 'a string is not a boolean'); ok(!_.isBoolean("false"), 'the string "false" is not a boolean'); ok(!_.isBoolean("true"), 'the string "true" is not a boolean'); ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); ok(!_.isBoolean(undefined), 'undefined is not a boolean'); ok(!_.isBoolean(NaN), 'NaN is not a boolean'); ok(!_.isBoolean(null), 'null is not a boolean'); ok(_.isBoolean(true), 'but true is'); ok(_.isBoolean(false), 'and so is false'); ok(_.isBoolean(iBoolean), 'even from another frame'); }); test("isFunction", function() { ok(!_.isFunction(undefined), 'undefined vars are not functions'); ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); ok(!_.isFunction('moe'), 'strings are not functions'); ok(_.isFunction(_.isFunction), 'but functions are'); ok(_.isFunction(iFunction), 'even from another frame'); ok(_.isFunction(function(){}), 'even anonymous ones'); }); test("isDate", function() { ok(!_.isDate(100), 'numbers are not dates'); ok(!_.isDate({}), 'objects are not dates'); ok(_.isDate(new Date()), 'but dates are'); ok(_.isDate(iDate), 'even from another frame'); }); test("isRegExp", function() { ok(!_.isRegExp(_.identity), 'functions are not RegExps'); ok(_.isRegExp(/identity/), 'but RegExps are'); ok(_.isRegExp(iRegExp), 'even from another frame'); }); test("isFinite", function() { ok(!_.isFinite(undefined), 'undefined is not Finite'); ok(!_.isFinite(null), 'null is not Finite'); ok(!_.isFinite(NaN), 'NaN is not Finite'); ok(!_.isFinite(Infinity), 'Infinity is not Finite'); ok(!_.isFinite(-Infinity), '-Infinity is not Finite'); ok(_.isFinite('12'), 'Numeric strings are numbers'); ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); ok(!_.isFinite(''), 'Empty strings are not numbers'); var obj = new Number(5); ok(_.isFinite(obj), 'Number instances can be finite'); ok(_.isFinite(0), '0 is Finite'); ok(_.isFinite(123), 'Ints are Finite'); ok(_.isFinite(-12.44), 'Floats are Finite'); }); test("isNaN", function() { ok(!_.isNaN(undefined), 'undefined is not NaN'); ok(!_.isNaN(null), 'null is not NaN'); ok(!_.isNaN(0), '0 is not NaN'); ok(_.isNaN(NaN), 'but NaN is'); ok(_.isNaN(iNaN), 'even from another frame'); ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); }); test("isNull", function() { ok(!_.isNull(undefined), 'undefined is not null'); ok(!_.isNull(NaN), 'NaN is not null'); ok(_.isNull(null), 'but null is'); ok(_.isNull(iNull), 'even from another frame'); }); test("isUndefined", function() { ok(!_.isUndefined(1), 'numbers are defined'); ok(!_.isUndefined(null), 'null is defined'); ok(!_.isUndefined(false), 'false is defined'); ok(!_.isUndefined(NaN), 'NaN is defined'); ok(_.isUndefined(), 'nothing is undefined'); ok(_.isUndefined(undefined), 'undefined is undefined'); ok(_.isUndefined(iUndefined), 'even from another frame'); }); if (window.ActiveXObject) { test("IE host objects", function() { var xml = new ActiveXObject("Msxml2.DOMDocument.3.0"); ok(!_.isNumber(xml)); ok(!_.isBoolean(xml)); ok(!_.isNaN(xml)); ok(!_.isFunction(xml)); ok(!_.isNull(xml)); ok(!_.isUndefined(xml)); }); } test("tap", function() { var intercepted = null; var interceptor = function(obj) { intercepted = obj; }; var returned = _.tap(1, interceptor); equal(intercepted, 1, "passes tapped object to interceptor"); equal(returned, 1, "returns tapped object"); returned = _([1,2,3]).chain(). map(function(n){ return n * 2; }). max(). tap(interceptor). value(); ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain'); }); test("has", function () { var obj = {foo: "bar", func: function () {} }; ok (_.has(obj, "foo"), "has() checks that the object has a property."); ok (_.has(obj, "baz") == false, "has() returns false if the object doesn't have the property."); ok (_.has(obj, "func"), "has() works for functions too."); obj.hasOwnProperty = null; ok (_.has(obj, "foo"), "has() works even when the hasOwnProperty method is deleted."); var child = {}; child.prototype = obj; ok (_.has(child, "foo") == false, "has() does not check the prototype chain for a property.") }); }); lodash-2.4.1/vendor/underscore/LICENSE0000644000175000017500000000213512247303154015607 0ustar ovdovdCopyright (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lodash-2.4.1/vendor/underscore/underscore.js0000644000175000017500000012506012247303154017314 0ustar ovdovd// Underscore.js 1.5.2 // http://underscorejs.org // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Establish the object that gets returned to break out of a loop iteration. var breaker = {}; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, slice = ArrayProto.slice, concat = ArrayProto.concat, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeForEach = ArrayProto.forEach, nativeMap = ArrayProto.map, nativeReduce = ArrayProto.reduce, nativeReduceRight = ArrayProto.reduceRight, nativeFilter = ArrayProto.filter, nativeEvery = ArrayProto.every, nativeSome = ArrayProto.some, nativeIndexOf = ArrayProto.indexOf, nativeLastIndexOf = ArrayProto.lastIndexOf, nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object via a string identifier, // for Closure Compiler "advanced" mode. if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // Current version. _.VERSION = '1.5.2'; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles objects with the built-in `forEach`, arrays, and raw objects. // Delegates to **ECMAScript 5**'s native `forEach` if available. var each = _.each = _.forEach = function(obj, iterator, context) { if (obj == null) return; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, length = obj.length; i < length; i++) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; } } }; // Return the results of applying the iterator to each element. // Delegates to **ECMAScript 5**'s native `map` if available. _.map = _.collect = function(obj, iterator, context) { var results = []; if (obj == null) return results; if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); each(obj, function(value, index, list) { results.push(iterator.call(context, value, index, list)); }); return results; }; var reduceError = 'Reduce of empty array with no initial value'; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { var initial = arguments.length > 2; if (obj == null) obj = []; if (nativeReduce && obj.reduce === nativeReduce) { if (context) iterator = _.bind(iterator, context); return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); } each(obj, function(value, index, list) { if (!initial) { memo = value; initial = true; } else { memo = iterator.call(context, memo, value, index, list); } }); if (!initial) throw new TypeError(reduceError); return memo; }; // The right-associative version of reduce, also known as `foldr`. // Delegates to **ECMAScript 5**'s native `reduceRight` if available. _.reduceRight = _.foldr = function(obj, iterator, memo, context) { var initial = arguments.length > 2; if (obj == null) obj = []; if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { if (context) iterator = _.bind(iterator, context); return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); } var length = obj.length; if (length !== +length) { var keys = _.keys(obj); length = keys.length; } each(obj, function(value, index, list) { index = keys ? keys[--length] : --length; if (!initial) { memo = obj[index]; initial = true; } else { memo = iterator.call(context, memo, obj[index], index, list); } }); if (!initial) throw new TypeError(reduceError); return memo; }; // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, iterator, context) { var result; any(obj, function(value, index, list) { if (iterator.call(context, value, index, list)) { result = value; return true; } }); return result; }; // Return all the elements that pass a truth test. // Delegates to **ECMAScript 5**'s native `filter` if available. // Aliased as `select`. _.filter = _.select = function(obj, iterator, context) { var results = []; if (obj == null) return results; if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); each(obj, function(value, index, list) { if (iterator.call(context, value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, iterator, context) { return _.filter(obj, function(value, index, list) { return !iterator.call(context, value, index, list); }, context); }; // Determine whether all of the elements match a truth test. // Delegates to **ECMAScript 5**'s native `every` if available. // Aliased as `all`. _.every = _.all = function(obj, iterator, context) { iterator || (iterator = _.identity); var result = true; if (obj == null) return result; if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); each(obj, function(value, index, list) { if (!(result = result && iterator.call(context, value, index, list))) return breaker; }); return !!result; }; // Determine if at least one element in the object matches a truth test. // Delegates to **ECMAScript 5**'s native `some` if available. // Aliased as `any`. var any = _.some = _.any = function(obj, iterator, context) { iterator || (iterator = _.identity); var result = false; if (obj == null) return result; if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); each(obj, function(value, index, list) { if (result || (result = iterator.call(context, value, index, list))) return breaker; }); return !!result; }; // Determine if the array or object contains a given value (using `===`). // Aliased as `include`. _.contains = _.include = function(obj, target) { if (obj == null) return false; if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; return any(obj, function(value) { return value === target; }); }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { return (isFunc ? method : value[method]).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, function(value){ return value[key]; }); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. _.where = function(obj, attrs, first) { if (_.isEmpty(attrs)) return first ? void 0 : []; return _[first ? 'find' : 'filter'](obj, function(value) { for (var key in attrs) { if (attrs[key] !== value[key]) return false; } return true; }); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { return _.where(obj, attrs, true); }; // Return the maximum element or (element-based computation). // Can't optimize arrays of integers longer than 65,535 elements. // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) _.max = function(obj, iterator, context) { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.max.apply(Math, obj); } if (!iterator && _.isEmpty(obj)) return -Infinity; var result = {computed : -Infinity, value: -Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed > result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Return the minimum element (or element-based computation). _.min = function(obj, iterator, context) { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.min.apply(Math, obj); } if (!iterator && _.isEmpty(obj)) return Infinity; var result = {computed : Infinity, value: Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed < result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Shuffle an array, using the modern version of the // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var rand; var index = 0; var shuffled = []; each(obj, function(value) { rand = _.random(index++); shuffled[index - 1] = shuffled[rand]; shuffled[rand] = value; }); return shuffled; }; // Sample **n** random values from an array. // If **n** is not specified, returns a single random element from the array. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { if (arguments.length < 2 || guard) { return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n)); }; // An internal function to generate lookup iterators. var lookupIterator = function(value) { return _.isFunction(value) ? value : function(obj){ return obj[value]; }; }; // Sort the object's values by a criterion produced by an iterator. _.sortBy = function(obj, value, context) { var iterator = lookupIterator(value); return _.pluck(_.map(obj, function(value, index, list) { return { value: value, index: index, criteria: iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. var group = function(behavior) { return function(obj, value, context) { var result = {}; var iterator = value == null ? _.identity : lookupIterator(value); each(obj, function(value, index) { var key = iterator.call(context, value, index, obj); behavior(result, key, value); }); return result; }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = group(function(result, key, value) { (_.has(result, key) ? result[key] : (result[key] = [])).push(value); }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. _.indexBy = group(function(result, key, value) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. _.countBy = group(function(result, key) { _.has(result, key) ? result[key]++ : result[key] = 1; }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iterator, context) { iterator = iterator == null ? _.identity : lookupIterator(iterator); var value = iterator.call(context, obj); var low = 0, high = array.length; while (low < high) { var mid = (low + high) >>> 1; iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; } return low; }; // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (obj.length === +obj.length) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; return (n == null) || guard ? array[0] : slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. The **guard** check allows it to work with // `_.map`. _.initial = function(array, n, guard) { return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; if ((n == null) || guard) { return array[array.length - 1]; } else { return slice.call(array, Math.max(array.length - n, 0)); } }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return // the rest N values in the array. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = _.drop = function(array, n, guard) { return slice.call(array, (n == null) || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, _.identity); }; // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, output) { if (shallow && _.every(input, _.isArray)) { return concat.apply(output, input); } each(input, function(value) { if (_.isArray(value) || _.isArguments(value)) { shallow ? push.apply(output, value) : flatten(value, shallow, output); } else { output.push(value); } }); return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iterator, context) { if (_.isFunction(isSorted)) { context = iterator; iterator = isSorted; isSorted = false; } var initial = iterator ? _.map(array, iterator, context) : array; var results = []; var seen = []; each(initial, function(value, index) { if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { seen.push(value); results.push(array[index]); } }); return results; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { return _.uniq(_.flatten(arguments, true)); }; // Produce an array that contains every item shared between all the // passed-in arrays. _.intersection = function(array) { var rest = slice.call(arguments, 1); return _.filter(_.uniq(array), function(item) { return _.every(rest, function(other) { return _.indexOf(other, item) >= 0; }); }); }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. _.difference = function(array) { var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); return _.filter(array, function(value){ return !_.contains(rest, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function() { var length = _.max(_.pluck(arguments, "length").concat(0)); var results = new Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(arguments, '' + i); } return results; }; // Converts lists into objects. Pass either a single array of `[key, value]` // pairs, or two parallel arrays of the same length -- one of keys, and one of // the corresponding values. _.object = function(list, values) { if (list == null) return {}; var result = {}; for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { result[list[i][0]] = list[i][1]; } } return result; }; // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), // we need this function. Return the position of the first occurrence of an // item in an array, or -1 if the item is not included in the array. // Delegates to **ECMAScript 5**'s native `indexOf` if available. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); for (; i < length; i++) if (array[i] === item) return i; return -1; }; // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. _.lastIndexOf = function(array, item, from) { if (array == null) return -1; var hasIndex = from != null; if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); } var i = (hasIndex ? from : array.length); while (i--) if (array[i] === item) return i; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { if (arguments.length <= 1) { stop = start || 0; start = 0; } step = arguments[2] || 1; var length = Math.max(Math.ceil((stop - start) / step), 0); var idx = 0; var range = new Array(length); while(idx < length) { range[idx++] = start; start += step; } return range; }; // Function (ahem) Functions // ------------------ // Reusable constructor function for prototype setting. var ctor = function(){}; // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError; args = slice.call(arguments, 2); return bound = function() { if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); ctor.prototype = func.prototype; var self = new ctor; ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (Object(result) === result) return result; return self; }; }; // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _.partial = function(func) { var args = slice.call(arguments, 1); return function() { return func.apply(this, args.concat(slice.call(arguments))); }; }; // Bind all of an object's methods to that object. Useful for ensuring that // all callbacks defined on an object belong to it. _.bindAll = function(obj) { var funcs = slice.call(arguments, 1); if (funcs.length === 0) throw new Error("bindAll must be passed function names"); each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); return obj; }; // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memo = {}; hasher || (hasher = _.identity); return function() { var key = hasher.apply(this, arguments); return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); }; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); }; // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; options || (options = {}); var later = function() { previous = options.leading === false ? 0 : new Date; timeout = null; result = func.apply(context, args); }; return function() { var now = new Date; if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; return function() { context = this; args = arguments; timestamp = new Date(); var later = function() { var last = (new Date()) - timestamp; if (last < wait) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) result = func.apply(context, args); } }; var callNow = immediate && !timeout; if (!timeout) { timeout = setTimeout(later, wait); } if (callNow) result = func.apply(context, args); return result; }; }; // Returns a function that will be executed at most one time, no matter how // often you call it. Useful for lazy initialization. _.once = function(func) { var ran = false, memo; return function() { if (ran) return memo; ran = true; memo = func.apply(this, arguments); func = null; return memo; }; }; // Returns the first function passed as an argument to the second, // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { return function() { var args = [func]; push.apply(args, arguments); return wrapper.apply(this, args); }; }; // Returns a function that is the composition of a list of functions, each // consuming the return value of the function that follows. _.compose = function() { var funcs = arguments; return function() { var args = arguments; for (var i = funcs.length - 1; i >= 0; i--) { args = [funcs[i].apply(this, args)]; } return args[0]; }; }; // Returns a function that will only be executed after being called N times. _.after = function(times, func) { return function() { if (--times < 1) { return func.apply(this, arguments); } }; }; // Object Functions // ---------------- // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` _.keys = nativeKeys || function(obj) { if (obj !== Object(obj)) throw new TypeError('Invalid object'); var keys = []; for (var key in obj) if (_.has(obj, key)) keys.push(key); return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { var keys = _.keys(obj); var length = keys.length; var values = new Array(length); for (var i = 0; i < length; i++) { values[i] = obj[keys[i]]; } return values; }; // Convert an object into a list of `[key, value]` pairs. _.pairs = function(obj) { var keys = _.keys(obj); var length = keys.length; var pairs = new Array(length); for (var i = 0; i < length; i++) { pairs[i] = [keys[i], obj[keys[i]]]; } return pairs; }; // Invert the keys and values of an object. The values must be serializable. _.invert = function(obj) { var result = {}; var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { result[obj[keys[i]]] = keys[i]; } return result; }; // Return a sorted list of the function names available on the object. // Aliased as `methods` _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; // Extend a given object with all the properties in passed-in object(s). _.extend = function(obj) { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { obj[prop] = source[prop]; } } }); return obj; }; // Return a copy of the object only containing the whitelisted properties. _.pick = function(obj) { var copy = {}; var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); each(keys, function(key) { if (key in obj) copy[key] = obj[key]; }); return copy; }; // Return a copy of the object without the blacklisted properties. _.omit = function(obj) { var copy = {}; var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); for (var key in obj) { if (!_.contains(keys, key)) copy[key] = obj[key]; } return copy; }; // Fill in a given object with default properties. _.defaults = function(obj) { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { if (obj[prop] === void 0) obj[prop] = source[prop]; } } }); return obj; }; // Create a (shallow-cloned) duplicate of an object. _.clone = function(obj) { if (!_.isObject(obj)) return obj; return _.isArray(obj) ? obj.slice() : _.extend({}, obj); }; // Invokes interceptor with the obj, and then returns obj. // The primary purpose of this method is to "tap into" a method chain, in // order to perform operations on intermediate results within the chain. _.tap = function(obj, interceptor) { interceptor(obj); return obj; }; // Internal recursive comparison function for `isEqual`. var eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a == 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); if (className != toString.call(b)) return false; switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return a == String(b); case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a == +b; // RegExps are compared by their source patterns and flags. case '[object RegExp]': return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) return bStack[length] == b; } // Objects with different constructors are not equivalent, but `Object`s // from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && _.isFunction(bCtor) && (bCtor instanceof bCtor))) { return false; } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size = 0, result = true; // Recursively compare objects and arrays. if (className == '[object Array]') { // Compare array 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 = eq(a[size], b[size], aStack, bStack))) break; } } } else { // Deep compare objects. for (var key in a) { if (_.has(a, key)) { // Count the expected number of properties. size++; // Deep compare each member. if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; } } // Ensure that both objects contain the same number of properties. if (result) { for (key in b) { if (_.has(b, key) && !(size--)) break; } result = !size; } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { return eq(a, b, [], []); }; // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { if (obj == null) return true; if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; for (var key in obj) if (_.has(obj, key)) return false; return true; }; // Is a given value a DOM element? _.isElement = function(obj) { return !!(obj && obj.nodeType === 1); }; // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { return toString.call(obj) == '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { return obj === Object(obj); }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) == '[object ' + name + ']'; }; }); // Define a fallback version of the method in browsers (ahem, IE), where // there isn't any inspectable "Arguments" type. if (!_.isArguments(arguments)) { _.isArguments = function(obj) { return !!(obj && _.has(obj, 'callee')); }; } // Optimize `isFunction` if appropriate. if (typeof (/./) !== 'function') { _.isFunction = function(obj) { return typeof obj === 'function'; }; } // Is a given object a finite number? _.isFinite = function(obj) { return isFinite(obj) && !isNaN(parseFloat(obj)); }; // Is the given value `NaN`? (NaN is the only number which does not equal itself). _.isNaN = function(obj) { return _.isNumber(obj) && obj != +obj; }; // Is a given value a boolean? _.isBoolean = function(obj) { return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; }; // Is a given value equal to null? _.isNull = function(obj) { return obj === null; }; // Is a given variable undefined? _.isUndefined = function(obj) { return obj === void 0; }; // Shortcut function for checking if an object has a given property directly // on itself (in other words, not on a prototype). _.has = function(obj, key) { return hasOwnProperty.call(obj, key); }; // Utility Functions // ----------------- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its // previous owner. Returns a reference to the Underscore object. _.noConflict = function() { root._ = previousUnderscore; return this; }; // Keep the identity function around for default iterators. _.identity = function(value) { return value; }; // Run a function **n** times. _.times = function(n, iterator, context) { var accum = Array(Math.max(0, n)); for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); return accum; }; // Return a random integer between min and max (inclusive). _.random = function(min, max) { if (max == null) { max = min; min = 0; } return min + Math.floor(Math.random() * (max - min + 1)); }; // List of HTML entities for escaping. var entityMap = { escape: { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' } }; entityMap.unescape = _.invert(entityMap.escape); // Regexes containing the keys and values listed immediately above. var entityRegexes = { escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') }; // Functions for escaping and unescaping strings to/from HTML interpolation. _.each(['escape', 'unescape'], function(method) { _[method] = function(string) { if (string == null) return ''; return ('' + string).replace(entityRegexes[method], function(match) { return entityMap[method][match]; }); }; }); // If the value of the named `property` is a function then invoke it with the // `object` as context; otherwise, return it. _.result = function(object, property) { if (object == null) return void 0; var value = object[property]; return _.isFunction(value) ? value.call(object) : value; }; // Add your own custom functions to the Underscore object. _.mixin = function(obj) { each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return result.call(this, func.apply(_, args)); }; }); }; // Generate a unique integer id (unique within the entire client session). // Useful for temporary DOM ids. var idCounter = 0; _.uniqueId = function(prefix) { var id = ++idCounter + ''; return prefix ? prefix + id : id; }; // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an // interpolation, evaluation or escaping regex, we need one that is // guaranteed not to match. var noMatch = /(.)^/; // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { "'": "'", '\\': '\\', '\r': 'r', '\n': 'n', '\t': 't', '\u2028': 'u2028', '\u2029': 'u2029' }; var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. _.template = function(text, data, settings) { var render; settings = _.defaults({}, settings, _.templateSettings); // Combine delimiters into one regular expression via alternation. var matcher = new RegExp([ (settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source ].join('|') + '|$', 'g'); // Compile the template source, escaping string literals appropriately. var index = 0; var source = "__p+='"; text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { source += text.slice(index, offset) .replace(escaper, function(match) { return '\\' + escapes[match]; }); if (escape) { source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; } if (interpolate) { source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; } if (evaluate) { source += "';\n" + evaluate + "\n__p+='"; } index = offset + match.length; return match; }); source += "';\n"; // If a variable is not specified, place data values in local scope. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + source + "return __p;\n"; try { render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; } if (data) return render(data, _); var template = function(data) { return render.call(this, data, _); }; // Provide the compiled function source as a convenience for precompilation. template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; return template; }; // Add a "chain" function, which will delegate to the wrapper. _.chain = function(obj) { return _(obj).chain(); }; // OOP // --------------- // If Underscore is called as a function, it returns a wrapped object that // can be used OO-style. This wrapper holds altered versions of all the // underscore functions. Wrapped objects may be chained. // Helper function to continue chaining intermediate results. var result = function(obj) { return this._chain ? _(obj).chain() : obj; }; // Add all of the Underscore functions to the wrapper object. _.mixin(_); // Add all mutator Array functions to the wrapper. each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; return result.call(this, obj); }; }); // Add all accessor Array functions to the wrapper. each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result.call(this, method.apply(this._wrapped, arguments)); }; }); _.extend(_.prototype, { // Start chaining a wrapped Underscore object. chain: function() { this._chain = true; return this; }, // Extracts the result from a wrapped and chained object. value: function() { return this._wrapped; } }); }).call(this);