1 // Underscore.js 1.4.42 // http://underscorejs.org3 // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.4 // Underscore may be freely distributed under the MIT license.56 (function() {78 // Baseline setup9 // --------------1011 // Establish the root object, `window` in the browser, or `global` on the server.12 var root = this;1314 // Save the previous value of the `_` variable.15 var previousUnderscore = root._;1617 // Establish the object that gets returned to break out of a loop iteration.18 var breaker = {};1920 // Save bytes in the minified (but not gzipped) version:21 var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;2223 // Create quick reference variables for speed access to core prototypes.24 var push = ArrayProto.push,25 slice = ArrayProto.slice,26 concat = ArrayProto.concat,27 toString = ObjProto.toString,28 hasOwnProperty = ObjProto.hasOwnProperty;2930 // All **ECMAScript 5** native function implementations that we hope to use31 // are declared here.32 var33 nativeForEach = ArrayProto.forEach,34 nativeMap = ArrayProto.map,35 nativeReduce = ArrayProto.reduce,36 nativeReduceRight = ArrayProto.reduceRight,37 nativeFilter = ArrayProto.filter,38 nativeEvery = ArrayProto.every,39 nativeSome = ArrayProto.some,40 nativeIndexOf = ArrayProto.indexOf,41 nativeLastIndexOf = ArrayProto.lastIndexOf,42 nativeIsArray = Array.isArray,43 nativeKeys = Object.keys,44 nativeBind = FuncProto.bind;4546 // Create a safe reference to the Underscore object for use below.47 var _ = function(obj) {48 if (obj instanceof _) return obj;49 if (!(this instanceof _)) return new _(obj);50 this._wrapped = obj;51 };5253 // Export the Underscore object for **Node.js**, with54 // backwards-compatibility for the old `require()` API. If we're in55 // the browser, add `_` as a global object via a string identifier,56 // for Closure Compiler "advanced" mode.57 if (typeof exports !== 'undefined') {58 if (typeof module !== 'undefined' && module.exports) {59 exports = module.exports = _;60 }61 exports._ = _;62 } else {63 root._ = _;64 }6566 // Current version.67 _.VERSION = '1.4.4';6869 // Collection Functions70 // --------------------7172 // The cornerstone, an `each` implementation, aka `forEach`.73 // Handles objects with the built-in `forEach`, arrays, and raw objects.74 // Delegates to **ECMAScript 5**'s native `forEach` if available.75 var each = _.each = _.forEach = function(obj, iterator, context) {76 if (obj == null) return;77 if (nativeForEach && obj.forEach === nativeForEach) {78 obj.forEach(iterator, context);79 } else if (obj.length === +obj.length) {80 for (var i = 0, l = obj.length; i < l; i++) {81 if (iterator.call(context, obj[i], i, obj) === breaker) return;82 }83 } else {84 for (var key in obj) {85 if (_.has(obj, key)) {86 if (iterator.call(context, obj[key], key, obj) === breaker) return;87 }88 }89 }90 };9192 // Return the results of applying the iterator to each element.93 // Delegates to **ECMAScript 5**'s native `map` if available.94 _.map = _.collect = function(obj, iterator, context) {95 var results = [];96 if (obj == null) return results;97 if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);98 each(obj, function(value, index, list) {99 results[results.length] = iterator.call(context, value, index, list);100 });101 return results;102 };103104 var reduceError = 'Reduce of empty array with no initial value';105106 // **Reduce** builds up a single result from a list of values, aka `inject`,107 // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.108 _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {109 var initial = arguments.length > 2;110 if (obj == null) obj = [];111 if (nativeReduce && obj.reduce === nativeReduce) {112 if (context) iterator = _.bind(iterator, context);113 return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);114 }115 each(obj, function(value, index, list) {116 if (!initial) {117 memo = value;118 initial = true;119 } else {120 memo = iterator.call(context, memo, value, index, list);121 }122 });123 if (!initial) throw new TypeError(reduceError);124 return memo;125 };126127 // The right-associative version of reduce, also known as `foldr`.128 // Delegates to **ECMAScript 5**'s native `reduceRight` if available.129 _.reduceRight = _.foldr = function(obj, iterator, memo, context) {130 var initial = arguments.length > 2;131 if (obj == null) obj = [];132 if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {133 if (context) iterator = _.bind(iterator, context);134 return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);135 }136 var length = obj.length;137 if (length !== +length) {138 var keys = _.keys(obj);139 length = keys.length;140 }141 each(obj, function(value, index, list) {142 index = keys ? keys[--length] : --length;143 if (!initial) {144 memo = obj[index];145 initial = true;146 } else {147 memo = iterator.call(context, memo, obj[index], index, list);148 }149 });150 if (!initial) throw new TypeError(reduceError);151 return memo;152 };153154 // Return the first value which passes a truth test. Aliased as `detect`.155 _.find = _.detect = function(obj, iterator, context) {156 var result;157 any(obj, function(value, index, list) {158 if (iterator.call(context, value, index, list)) {159 result = value;160 return true;161 }162 });163 return result;164 };165166 // Return all the elements that pass a truth test.167 // Delegates to **ECMAScript 5**'s native `filter` if available.168 // Aliased as `select`.169 _.filter = _.select = function(obj, iterator, context) {170 var results = [];171 if (obj == null) return results;172 if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);173 each(obj, function(value, index, list) {174 if (iterator.call(context, value, index, list)) results[results.length] = value;175 });176 return results;177 };178179 // Return all the elements for which a truth test fails.180 _.reject = function(obj, iterator, context) {181 return _.filter(obj, function(value, index, list) {182 return !iterator.call(context, value, index, list);183 }, context);184 };185186 // Determine whether all of the elements match a truth test.187 // Delegates to **ECMAScript 5**'s native `every` if available.188 // Aliased as `all`.189 _.every = _.all = function(obj, iterator, context) {190 iterator || (iterator = _.identity);191 var result = true;192 if (obj == null) return result;193 if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);194 each(obj, function(value, index, list) {195 if (!(result = result && iterator.call(context, value, index, list))) return breaker;196 });197 return !!result;198 };199200 // Determine if at least one element in the object matches a truth test.201 // Delegates to **ECMAScript 5**'s native `some` if available.202 // Aliased as `any`.203 var any = _.some = _.any = function(obj, iterator, context) {204 iterator || (iterator = _.identity);205 var result = false;206 if (obj == null) return result;207 if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);208 each(obj, function(value, index, list) {209 if (result || (result = iterator.call(context, value, index, list))) return breaker;210 });211 return !!result;212 };213214 // Determine if the array or object contains a given value (using `===`).215 // Aliased as `include`.216 _.contains = _.include = function(obj, target) {217 if (obj == null) return false;218 if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;219 return any(obj, function(value) {220 return value === target;221 });222 };223224 // Invoke a method (with arguments) on every item in a collection.225 _.invoke = function(obj, method) {226 var args = slice.call(arguments, 2);227 var isFunc = _.isFunction(method);228 return _.map(obj, function(value) {229 return (isFunc ? method : value[method]).apply(value, args);230 });231 };232233 // Convenience version of a common use case of `map`: fetching a property.234 _.pluck = function(obj, key) {235 return _.map(obj, function(value){ return value[key]; });236 };237238 // Convenience version of a common use case of `filter`: selecting only objects239 // containing specific `key:value` pairs.240 _.where = function(obj, attrs, first) {241 if (_.isEmpty(attrs)) return first ? null : [];242 return _[first ? 'find' : 'filter'](obj, function(value) {243 for (var key in attrs) {244 if (attrs[key] !== value[key]) return false;245 }246 return true;247 });248 };249250 // Convenience version of a common use case of `find`: getting the first object251 // containing specific `key:value` pairs.252 _.findWhere = function(obj, attrs) {253 return _.where(obj, attrs, true);254 };255256 // Return the maximum element or (element-based computation).257 // Can't optimize arrays of integers longer than 65,535 elements.258 // See: https://bugs.webkit.org/show_bug.cgi?id=80797259 _.max = function(obj, iterator, context) {260 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {261 return Math.max.apply(Math, obj);262 }263 if (!iterator && _.isEmpty(obj)) return -Infinity;264 var result = {computed : -Infinity, value: -Infinity};265 each(obj, function(value, index, list) {266 var computed = iterator ? iterator.call(context, value, index, list) : value;267 computed >= result.computed && (result = {value : value, computed : computed});268 });269 return result.value;270 };271272 // Return the minimum element (or element-based computation).273 _.min = function(obj, iterator, context) {274 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {275 return Math.min.apply(Math, obj);276 }277 if (!iterator && _.isEmpty(obj)) return Infinity;278 var result = {computed : Infinity, value: Infinity};279 each(obj, function(value, index, list) {280 var computed = iterator ? iterator.call(context, value, index, list) : value;281 computed < result.computed && (result = {value : value, computed : computed});282 });283 return result.value;284 };285286 // Shuffle an array.287 _.shuffle = function(obj) {288 var rand;289 var index = 0;290 var shuffled = [];291 each(obj, function(value) {292 rand = _.random(index++);293 shuffled[index - 1] = shuffled[rand];294 shuffled[rand] = value;295 });296 return shuffled;297 };298299 // An internal function to generate lookup iterators.300 var lookupIterator = function(value) {301 return _.isFunction(value) ? value : function(obj){ return obj[value]; };302 };303304 // Sort the object's values by a criterion produced by an iterator.305 _.sortBy = function(obj, value, context) {306 var iterator = lookupIterator(value);307 return _.pluck(_.map(obj, function(value, index, list) {308 return {309 value : value,310 index : index,311 criteria : iterator.call(context, value, index, list)312 };313 }).sort(function(left, right) {314 var a = left.criteria;315 var b = right.criteria;316 if (a !== b) {317 if (a > b || a === void 0) return 1;318 if (a < b || b === void 0) return -1;319 }320 return left.index < right.index ? -1 : 1;321 }), 'value');322 };323324 // An internal function used for aggregate "group by" operations.325 var group = function(obj, value, context, behavior) {326 var result = {};327 var iterator = lookupIterator(value || _.identity);328 each(obj, function(value, index) {329 var key = iterator.call(context, value, index, obj);330 behavior(result, key, value);331 });332 return result;333 };334335 // Groups the object's values by a criterion. Pass either a string attribute336 // to group by, or a function that returns the criterion.337 _.groupBy = function(obj, value, context) {338 return group(obj, value, context, function(result, key, value) {339 (_.has(result, key) ? result[key] : (result[key] = [])).push(value);340 });341 };342343 // Counts instances of an object that group by a certain criterion. Pass344 // either a string attribute to count by, or a function that returns the345 // criterion.346 _.countBy = function(obj, value, context) {347 return group(obj, value, context, function(result, key) {348 if (!_.has(result, key)) result[key] = 0;349 result[key]++;350 });351 };352353 // Use a comparator function to figure out the smallest index at which354 // an object should be inserted so as to maintain order. Uses binary search.355 _.sortedIndex = function(array, obj, iterator, context) {356 iterator = iterator == null ? _.identity : lookupIterator(iterator);357 var value = iterator.call(context, obj);358 var low = 0, high = array.length;359 while (low < high) {360 var mid = (low + high) >>> 1;361 iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;362 }363 return low;364 };365366 // Safely convert anything iterable into a real, live array.367 _.toArray = function(obj) {368 if (!obj) return [];369 if (_.isArray(obj)) return slice.call(obj);370 if (obj.length === +obj.length) return _.map(obj, _.identity);371 return _.values(obj);372 };373374 // Return the number of elements in an object.375 _.size = function(obj) {376 if (obj == null) return 0;377 return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;378 };379380 // Array Functions381 // ---------------382383 // Get the first element of an array. Passing **n** will return the first N384 // values in the array. Aliased as `head` and `take`. The **guard** check385 // allows it to work with `_.map`.386 _.first = _.head = _.take = function(array, n, guard) {387 if (array == null) return void 0;388 return (n != null) && !guard ? slice.call(array, 0, n) : array[0];389 };390391 // Returns everything but the last entry of the array. Especially useful on392 // the arguments object. Passing **n** will return all the values in393 // the array, excluding the last N. The **guard** check allows it to work with394 // `_.map`.395 _.initial = function(array, n, guard) {396 return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));397 };398399 // Get the last element of an array. Passing **n** will return the last N400 // values in the array. The **guard** check allows it to work with `_.map`.401 _.last = function(array, n, guard) {402 if (array == null) return void 0;403 if ((n != null) && !guard) {404 return slice.call(array, Math.max(array.length - n, 0));405 } else {406 return array[array.length - 1];407 }408 };409410 // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.411 // Especially useful on the arguments object. Passing an **n** will return412 // the rest N values in the array. The **guard**413 // check allows it to work with `_.map`.414 _.rest = _.tail = _.drop = function(array, n, guard) {415 return slice.call(array, (n == null) || guard ? 1 : n);416 };417418 // Trim out all falsy values from an array.419 _.compact = function(array) {420 return _.filter(array, _.identity);421 };422423 // Internal implementation of a recursive `flatten` function.424 var flatten = function(input, shallow, output) {425 each(input, function(value) {426 if (_.isArray(value)) {427 shallow ? push.apply(output, value) : flatten(value, shallow, output);428 } else {429 output.push(value);430 }431 });432 return output;433 };434435 // Return a completely flattened version of an array.436 _.flatten = function(array, shallow) {437 return flatten(array, shallow, []);438 };439440 // Return a version of the array that does not contain the specified value(s).441 _.without = function(array) {442 return _.difference(array, slice.call(arguments, 1));443 };444445 // Produce a duplicate-free version of the array. If the array has already446 // been sorted, you have the option of using a faster algorithm.447 // Aliased as `unique`.448 _.uniq = _.unique = function(array, isSorted, iterator, context) {449 if (_.isFunction(isSorted)) {450 context = iterator;451 iterator = isSorted;452 isSorted = false;453 }454 var initial = iterator ? _.map(array, iterator, context) : array;455 var results = [];456 var seen = [];457 each(initial, function(value, index) {458 if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {459 seen.push(value);460 results.push(array[index]);461 }462 });463 return results;464 };465466 // Produce an array that contains the union: each distinct element from all of467 // the passed-in arrays.468 _.union = function() {469 return _.uniq(concat.apply(ArrayProto, arguments));470 };471472 // Produce an array that contains every item shared between all the473 // passed-in arrays.474 _.intersection = function(array) {475 var rest = slice.call(arguments, 1);476 return _.filter(_.uniq(array), function(item) {477 return _.every(rest, function(other) {478 return _.indexOf(other, item) >= 0;479 });480 });481 };482483 // Take the difference between one array and a number of other arrays.484 // Only the elements present in just the first array will remain.485 _.difference = function(array) {486 var rest = concat.apply(ArrayProto, slice.call(arguments, 1));487 return _.filter(array, function(value){ return !_.contains(rest, value); });488 };489490 // Zip together multiple lists into a single array -- elements that share491 // an index go together.492 _.zip = function() {493 var args = slice.call(arguments);494 var length = _.max(_.pluck(args, 'length'));495 var results = new Array(length);496 for (var i = 0; i < length; i++) {497 results[i] = _.pluck(args, "" + i);498 }499 return results;500 };501502 // Converts lists into objects. Pass either a single array of `[key, value]`503 // pairs, or two parallel arrays of the same length -- one of keys, and one of504 // the corresponding values.505 _.object = function(list, values) {506 if (list == null) return {};507 var result = {};508 for (var i = 0, l = list.length; i < l; i++) {509 if (values) {510 result[list[i]] = values[i];511 } else {512 result[list[i][0]] = list[i][1];513 }514 }515 return result;516 };517518 // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),519 // we need this function. Return the position of the first occurrence of an520 // item in an array, or -1 if the item is not included in the array.521 // Delegates to **ECMAScript 5**'s native `indexOf` if available.522 // If the array is large and already in sort order, pass `true`523 // for **isSorted** to use binary search.524 _.indexOf = function(array, item, isSorted) {525 if (array == null) return -1;526 var i = 0, l = array.length;527 if (isSorted) {528 if (typeof isSorted == 'number') {529 i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);530 } else {531 i = _.sortedIndex(array, item);532 return array[i] === item ? i : -1;533 }534 }535 if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);536 for (; i < l; i++) if (array[i] === item) return i;537 return -1;538 };539540 // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.541 _.lastIndexOf = function(array, item, from) {542 if (array == null) return -1;543 var hasIndex = from != null;544 if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {545 return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);546 }547 var i = (hasIndex ? from : array.length);548 while (i--) if (array[i] === item) return i;549 return -1;550 };551552 // Generate an integer Array containing an arithmetic progression. A port of553 // the native Python `range()` function. See554 // [the Python documentation](http://docs.python.org/library/functions.html#range).555 _.range = function(start, stop, step) {556 if (arguments.length <= 1) {557 stop = start || 0;558 start = 0;559 }560 step = arguments[2] || 1;561562 var len = Math.max(Math.ceil((stop - start) / step), 0);563 var idx = 0;564 var range = new Array(len);565566 while(idx < len) {567 range[idx++] = start;568 start += step;569 }570571 return range;572 };573574 // Function (ahem) Functions575 // ------------------576577 // Create a function bound to a given object (assigning `this`, and arguments,578 // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if579 // available.580 _.bind = function(func, context) {581 if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));582 var args = slice.call(arguments, 2);583 return function() {584 return func.apply(context, args.concat(slice.call(arguments)));585 };586 };587588 // Partially apply a function by creating a version that has had some of its589 // arguments pre-filled, without changing its dynamic `this` context.590 _.partial = function(func) {591 var args = slice.call(arguments, 1);592 return function() {593 return func.apply(this, args.concat(slice.call(arguments)));594 };595 };596597 // Bind all of an object's methods to that object. Useful for ensuring that598 // all callbacks defined on an object belong to it.599 _.bindAll = function(obj) {600 var funcs = slice.call(arguments, 1);601 if (funcs.length === 0) funcs = _.functions(obj);602 each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });603 return obj;604 };605606 // Memoize an expensive function by storing its results.607 _.memoize = function(func, hasher) {608 var memo = {};609 hasher || (hasher = _.identity);610 return function() {611 var key = hasher.apply(this, arguments);612 return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));613 };614 };615616 // Delays a function for the given number of milliseconds, and then calls617 // it with the arguments supplied.618 _.delay = function(func, wait) {619 var args = slice.call(arguments, 2);620 return setTimeout(function(){ return func.apply(null, args); }, wait);621 };622623 // Defers a function, scheduling it to run after the current call stack has624 // cleared.625 _.defer = function(func) {626 return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));627 };628629 // Returns a function, that, when invoked, will only be triggered at most once630 // during a given window of time.631 _.throttle = function(func, wait) {632 var context, args, timeout, result;633 var previous = 0;634 var later = function() {635 previous = new Date;636 timeout = null;637 result = func.apply(context, args);638 };639 return function() {640 var now = new Date;641 var remaining = wait - (now - previous);642 context = this;643 args = arguments;644 if (remaining <= 0) {645 clearTimeout(timeout);646 timeout = null;647 previous = now;648 result = func.apply(context, args);649 } else if (!timeout) {650 timeout = setTimeout(later, remaining);651 }652 return result;653 };654 };655656 // Returns a function, that, as long as it continues to be invoked, will not657 // be triggered. The function will be called after it stops being called for658 // N milliseconds. If `immediate` is passed, trigger the function on the659 // leading edge, instead of the trailing.660 _.debounce = function(func, wait, immediate) {661 var timeout, result;662 return function() {663 var context = this, args = arguments;664 var later = function() {665 timeout = null;666 if (!immediate) result = func.apply(context, args);667 };668 var callNow = immediate && !timeout;669 clearTimeout(timeout);670 timeout = setTimeout(later, wait);671 if (callNow) result = func.apply(context, args);672 return result;673 };674 };675676 // Returns a function that will be executed at most one time, no matter how677 // often you call it. Useful for lazy initialization.678 _.once = function(func) {679 var ran = false, memo;680 return function() {681 if (ran) return memo;682 ran = true;683 memo = func.apply(this, arguments);684 func = null;685 return memo;686 };687 };688689 // Returns the first function passed as an argument to the second,690 // allowing you to adjust arguments, run code before and after, and691 // conditionally execute the original function.692 _.wrap = function(func, wrapper) {693 return function() {694 var args = [func];695 push.apply(args, arguments);696 return wrapper.apply(this, args);697 };698 };699700 // Returns a function that is the composition of a list of functions, each701 // consuming the return value of the function that follows.702 _.compose = function() {703 var funcs = arguments;704 return function() {705 var args = arguments;706 for (var i = funcs.length - 1; i >= 0; i--) {707 args = [funcs[i].apply(this, args)];708 }709 return args[0];710 };711 };712713 // Returns a function that will only be executed after being called N times.714 _.after = function(times, func) {715 if (times <= 0) return func();716 return function() {717 if (--times < 1) {718 return func.apply(this, arguments);719 }720 };721 };722723 // Object Functions724 // ----------------725726 // Retrieve the names of an object's properties.727 // Delegates to **ECMAScript 5**'s native `Object.keys`728 _.keys = nativeKeys || function(obj) {729 if (obj !== Object(obj)) throw new TypeError('Invalid object');730 var keys = [];731 for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;732 return keys;733 };734735 // Retrieve the values of an object's properties.736 _.values = function(obj) {737 var values = [];738 for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);739 return values;740 };741742 // Convert an object into a list of `[key, value]` pairs.743 _.pairs = function(obj) {744 var pairs = [];745 for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);746 return pairs;747 };748749 // Invert the keys and values of an object. The values must be serializable.750 _.invert = function(obj) {751 var result = {};752 for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;753 return result;754 };755756 // Return a sorted list of the function names available on the object.757 // Aliased as `methods`758 _.functions = _.methods = function(obj) {759 var names = [];760 for (var key in obj) {761 if (_.isFunction(obj[key])) names.push(key);762 }763 return names.sort();764 };765766 // Extend a given object with all the properties in passed-in object(s).767 _.extend = function(obj) {768 each(slice.call(arguments, 1), function(source) {769 if (source) {770 for (var prop in source) {771 obj[prop] = source[prop];772 }773 }774 });775 return obj;776 };777778 // Return a copy of the object only containing the whitelisted properties.779 _.pick = function(obj) {780 var copy = {};781 var keys = concat.apply(ArrayProto, slice.call(arguments, 1));782 each(keys, function(key) {783 if (key in obj) copy[key] = obj[key];784 });785 return copy;786 };787788 // Return a copy of the object without the blacklisted properties.789 _.omit = function(obj) {790 var copy = {};791 var keys = concat.apply(ArrayProto, slice.call(arguments, 1));792 for (var key in obj) {793 if (!_.contains(keys, key)) copy[key] = obj[key];794 }795 return copy;796 };797798 // Fill in a given object with default properties.799 _.defaults = function(obj) {800 each(slice.call(arguments, 1), function(source) {801 if (source) {802 for (var prop in source) {803 if (obj[prop] == null) obj[prop] = source[prop];804 }805 }806 });807 return obj;808 };809810 // Create a (shallow-cloned) duplicate of an object.811 _.clone = function(obj) {812 if (!_.isObject(obj)) return obj;813 return _.isArray(obj) ? obj.slice() : _.extend({}, obj);814 };815816 // Invokes interceptor with the obj, and then returns obj.817 // The primary purpose of this method is to "tap into" a method chain, in818 // order to perform operations on intermediate results within the chain.819 _.tap = function(obj, interceptor) {820 interceptor(obj);821 return obj;822 };823824 // Internal recursive comparison function for `isEqual`.825 var eq = function(a, b, aStack, bStack) {826 // Identical objects are equal. `0 === -0`, but they aren't identical.827 // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.828 if (a === b) return a !== 0 || 1 / a == 1 / b;829 // A strict comparison is necessary because `null == undefined`.830 if (a == null || b == null) return a === b;831 // Unwrap any wrapped objects.832 if (a instanceof _) a = a._wrapped;833 if (b instanceof _) b = b._wrapped;834 // Compare `[[Class]]` names.835 var className = toString.call(a);836 if (className != toString.call(b)) return false;837 switch (className) {838 // Strings, numbers, dates, and booleans are compared by value.839 case '[object String]':840 // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is841 // equivalent to `new String("5")`.842 return a == String(b);843 case '[object Number]':844 // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for845 // other numeric values.846 return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);847 case '[object Date]':848 case '[object Boolean]':849 // Coerce dates and booleans to numeric primitive values. Dates are compared by their850 // millisecond representations. Note that invalid dates with millisecond representations851 // of `NaN` are not equivalent.852 return +a == +b;853 // RegExps are compared by their source patterns and flags.854 case '[object RegExp]':855 return a.source == b.source &&856 a.global == b.global &&857 a.multiline == b.multiline &&858 a.ignoreCase == b.ignoreCase;859 }860 if (typeof a != 'object' || typeof b != 'object') return false;861 // Assume equality for cyclic structures. The algorithm for detecting cyclic862 // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.863 var length = aStack.length;864 while (length--) {865 // Linear search. Performance is inversely proportional to the number of866 // unique nested structures.867 if (aStack[length] == a) return bStack[length] == b;868 }869 // Add the first object to the stack of traversed objects.870 aStack.push(a);871 bStack.push(b);872 var size = 0, result = true;873 // Recursively compare objects and arrays.874 if (className == '[object Array]') {875 // Compare array lengths to determine if a deep comparison is necessary.876 size = a.length;877 result = size == b.length;878 if (result) {879 // Deep compare the contents, ignoring non-numeric properties.880 while (size--) {881 if (!(result = eq(a[size], b[size], aStack, bStack))) break;882 }883 }884 } else {885 // Objects with different constructors are not equivalent, but `Object`s886 // from different frames are.887 var aCtor = a.constructor, bCtor = b.constructor;888 if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&889 _.isFunction(bCtor) && (bCtor instanceof bCtor))) {890 return false;891 }892 // Deep compare objects.893 for (var key in a) {894 if (_.has(a, key)) {895 // Count the expected number of properties.896 size++;897 // Deep compare each member.898 if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;899 }900 }901 // Ensure that both objects contain the same number of properties.902 if (result) {903 for (key in b) {904 if (_.has(b, key) && !(size--)) break;905 }906 result = !size;907 }908 }909 // Remove the first object from the stack of traversed objects.910 aStack.pop();911 bStack.pop();912 return result;913 };914915 // Perform a deep comparison to check if two objects are equal.916 _.isEqual = function(a, b) {917 return eq(a, b, [], []);918 };919920 // Is a given array, string, or object empty?921 // An "empty" object has no enumerable own-properties.922 _.isEmpty = function(obj) {923 if (obj == null) return true;924 if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;925 for (var key in obj) if (_.has(obj, key)) return false;926 return true;927 };928929 // Is a given value a DOM element?930 _.isElement = function(obj) {931 return !!(obj && obj.nodeType === 1);932 };933934 // Is a given value an array?935 // Delegates to ECMA5's native Array.isArray936 _.isArray = nativeIsArray || function(obj) {937 return toString.call(obj) == '[object Array]';938 };939940 // Is a given variable an object?941 _.isObject = function(obj) {942 return obj === Object(obj);943 };944945 // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.946 each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {947 _['is' + name] = function(obj) {948 return toString.call(obj) == '[object ' + name + ']';949 };950 });951952 // Define a fallback version of the method in browsers (ahem, IE), where953 // there isn't any inspectable "Arguments" type.954 if (!_.isArguments(arguments)) {955 _.isArguments = function(obj) {956 return !!(obj && _.has(obj, 'callee'));957 };958 }959960 // Optimize `isFunction` if appropriate.961 if (typeof (/./) !== 'function') {962 _.isFunction = function(obj) {963 return typeof obj === 'function';964 };965 }966967 // Is a given object a finite number?968 _.isFinite = function(obj) {969 return isFinite(obj) && !isNaN(parseFloat(obj));970 };971972 // Is the given value `NaN`? (NaN is the only number which does not equal itself).973 _.isNaN = function(obj) {974 return _.isNumber(obj) && obj != +obj;975 };976977 // Is a given value a boolean?978 _.isBoolean = function(obj) {979 return obj === true || obj === false || toString.call(obj) == '[object Boolean]';980 };981982 // Is a given value equal to null?983 _.isNull = function(obj) {984 return obj === null;985 };986987 // Is a given variable undefined?988 _.isUndefined = function(obj) {989 return obj === void 0;990 };991992 // Shortcut function for checking if an object has a given property directly993 // on itself (in other words, not on a prototype).994 _.has = function(obj, key) {995 return hasOwnProperty.call(obj, key);996 };997998 // Utility Functions999 // -----------------10001001 // Run Underscore.js in *noConflict* mode, returning the `_` variable to its1002 // previous owner. Returns a reference to the Underscore object.1003 _.noConflict = function() {1004 root._ = previousUnderscore;1005 return this;1006 };10071008 // Keep the identity function around for default iterators.1009 _.identity = function(value) {1010 return value;1011 };10121013 // Run a function **n** times.1014 _.times = function(n, iterator, context) {1015 var accum = Array(n);1016 for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);1017 return accum;1018 };10191020 // Return a random integer between min and max (inclusive).1021 _.random = function(min, max) {1022 if (max == null) {1023 max = min;1024 min = 0;1025 }1026 return min + Math.floor(Math.random() * (max - min + 1));1027 };10281029 // List of HTML entities for escaping.1030 var entityMap = {1031 escape: {1032 '&': '&',1033 '<': '<',1034 '>': '>',1035 '"': '"',1036 "'": ''',1037 '/': '/'1038 }1039 };1040 entityMap.unescape = _.invert(entityMap.escape);10411042 // Regexes containing the keys and values listed immediately above.1043 var entityRegexes = {1044 escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),1045 unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')1046 };10471048 // Functions for escaping and unescaping strings to/from HTML interpolation.1049 _.each(['escape', 'unescape'], function(method) {1050 _[method] = function(string) {1051 if (string == null) return '';1052 return ('' + string).replace(entityRegexes[method], function(match) {1053 return entityMap[method][match];1054 });1055 };1056 });10571058 // If the value of the named property is a function then invoke it;1059 // otherwise, return it.1060 _.result = function(object, property) {1061 if (object == null) return null;1062 var value = object[property];1063 return _.isFunction(value) ? value.call(object) : value;1064 };10651066 // Add your own custom functions to the Underscore object.1067 _.mixin = function(obj) {1068 each(_.functions(obj), function(name){1069 var func = _[name] = obj[name];1070 _.prototype[name] = function() {1071 var args = [this._wrapped];1072 push.apply(args, arguments);1073 return result.call(this, func.apply(_, args));1074 };1075 });1076 };10771078 // Generate a unique integer id (unique within the entire client session).1079 // Useful for temporary DOM ids.1080 var idCounter = 0;1081 _.uniqueId = function(prefix) {1082 var id = ++idCounter + '';1083 return prefix ? prefix + id : id;1084 };10851086 // By default, Underscore uses ERB-style template delimiters, change the1087 // following template settings to use alternative delimiters.1088 _.templateSettings = {1089 evaluate : /<%([\s\S]+?)%>/g,1090 interpolate : /<%=([\s\S]+?)%>/g,1091 escape : /<%-([\s\S]+?)%>/g1092 };10931094 // When customizing `templateSettings`, if you don't want to define an1095 // interpolation, evaluation or escaping regex, we need one that is1096 // guaranteed not to match.1097 var noMatch = /(.)^/;10981099 // Certain characters need to be escaped so that they can be put into a1100 // string literal.1101 var escapes = {1102 "'": "'",1103 '\\': '\\',1104 '\r': 'r',1105 '\n': 'n',1106 '\t': 't',1107 '\u2028': 'u2028',1108 '\u2029': 'u2029'1109 };11101111 var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;11121113 // JavaScript micro-templating, similar to John Resig's implementation.1114 // Underscore templating handles arbitrary delimiters, preserves whitespace,1115 // and correctly escapes quotes within interpolated code.1116 _.template = function(text, data, settings) {1117 var render;1118 settings = _.defaults({}, settings, _.templateSettings);11191120 // Combine delimiters into one regular expression via alternation.1121 var matcher = new RegExp([1122 (settings.escape || noMatch).source,1123 (settings.interpolate || noMatch).source,1124 (settings.evaluate || noMatch).source1125 ].join('|') + '|$', 'g');11261127 // Compile the template source, escaping string literals appropriately.1128 var index = 0;1129 var source = "__p+='";1130 text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {1131 source += text.slice(index, offset)1132 .replace(escaper, function(match) { return '\\' + escapes[match]; });11331134 if (escape) {1135 source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";1136 }1137 if (interpolate) {1138 source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";1139 }1140 if (evaluate) {1141 source += "';\n" + evaluate + "\n__p+='";1142 }1143 index = offset + match.length;1144 return match;1145 });1146 source += "';\n";11471148 // If a variable is not specified, place data values in local scope.1149 if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';11501151 source = "var __t,__p='',__j=Array.prototype.join," +1152 "print=function(){__p+=__j.call(arguments,'');};\n" +1153 source + "return __p;\n";11541155 try {1156 render = new Function(settings.variable || 'obj', '_', source);1157 } catch (e) {1158 e.source = source;1159 throw e;1160 }11611162 if (data) return render(data, _);1163 var template = function(data) {1164 return render.call(this, data, _);1165 };11661167 // Provide the compiled function source as a convenience for precompilation.1168 template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';11691170 return template;1171 };11721173 // Add a "chain" function, which will delegate to the wrapper.1174 _.chain = function(obj) {1175 return _(obj).chain();1176 };11771178 // OOP1179 // ---------------1180 // If Underscore is called as a function, it returns a wrapped object that1181 // can be used OO-style. This wrapper holds altered versions of all the1182 // underscore functions. Wrapped objects may be chained.11831184 // Helper function to continue chaining intermediate results.1185 var result = function(obj) {1186 return this._chain ? _(obj).chain() : obj;1187 };11881189 // Add all of the Underscore functions to the wrapper object.1190 _.mixin(_);11911192 // Add all mutator Array functions to the wrapper.1193 each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {1194 var method = ArrayProto[name];1195 _.prototype[name] = function() {1196 var obj = this._wrapped;1197 method.apply(obj, arguments);1198 if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];1199 return result.call(this, obj);1200 };1201 });12021203 // Add all accessor Array functions to the wrapper.1204 each(['concat', 'join', 'slice'], function(name) {1205 var method = ArrayProto[name];1206 _.prototype[name] = function() {1207 return result.call(this, method.apply(this._wrapped, arguments));1208 };1209 });12101211 _.extend(_.prototype, {12121213 // Start chaining a wrapped Underscore object.1214 chain: function() {1215 this._chain = true;1216 return this;1217 },12181219 // Extracts the result from a wrapped and chained object.1220 value: function() {1221 return this._wrapped;1222 }12231224 });12251226 }).call(this);1 // Underscore.js 1.4.4 2 // http://underscorejs.org 3 // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. 4 // Underscore may be freely distributed under the MIT license. 5 6 (function() { 7 8 // Baseline setup 9 // -------------- 10 11 // Establish the root object, `window` in the browser, or `global` on the server. 12 var root = this; 13 14 // Save the previous value of the `_` variable. 15 var previousUnderscore = root._; 16 17 // Establish the object that gets returned to break out of a loop iteration. 18 var breaker = {}; 19 20 // Save bytes in the minified (but not gzipped) version: 21 var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 22 23 // Create quick reference variables for speed access to core prototypes. 24 var push = ArrayProto.push, 25 slice = ArrayProto.slice, 26 concat = ArrayProto.concat, 27 toString = ObjProto.toString, 28 hasOwnProperty = ObjProto.hasOwnProperty; 29 30 // All **ECMAScript 5** native function implementations that we hope to use 31 // are declared here. 32 var 33 nativeForEach = ArrayProto.forEach, 34 nativeMap = ArrayProto.map, 35 nativeReduce = ArrayProto.reduce, 36 nativeReduceRight = ArrayProto.reduceRight, 37 nativeFilter = ArrayProto.filter, 38 nativeEvery = ArrayProto.every, 39 nativeSome = ArrayProto.some, 40 nativeIndexOf = ArrayProto.indexOf, 41 nativeLastIndexOf = ArrayProto.lastIndexOf, 42 nativeIsArray = Array.isArray, 43 nativeKeys = Object.keys, 44 nativeBind = FuncProto.bind; 45 46 // Create a safe reference to the Underscore object for use below. 47 var _ = function(obj) { 48 if (obj instanceof _) return obj; 49 if (!(this instanceof _)) return new _(obj); 50 this._wrapped = obj; 51 }; 52 53 // Export the Underscore object for **Node.js**, with 54 // backwards-compatibility for the old `require()` API. If we're in 55 // the browser, add `_` as a global object via a string identifier, 56 // for Closure Compiler "advanced" mode. 57 if (typeof exports !== 'undefined') { 58 if (typeof module !== 'undefined' && module.exports) { 59 exports = module.exports = _; 60 } 61 exports._ = _; 62 } else { 63 root._ = _; 64 } 65 66 // Current version. 67 _.VERSION = '1.4.4'; 68 69 // Collection Functions 70 // -------------------- 71 72 // The cornerstone, an `each` implementation, aka `forEach`. 73 // Handles objects with the built-in `forEach`, arrays, and raw objects. 74 // Delegates to **ECMAScript 5**'s native `forEach` if available. 75 var each = _.each = _.forEach = function(obj, iterator, context) { 76 if (obj == null) return; 77 if (nativeForEach && obj.forEach === nativeForEach) { 78 obj.forEach(iterator, context); 79 } else if (obj.length === +obj.length) { 80 for (var i = 0, l = obj.length; i < l; i++) { 81 if (iterator.call(context, obj[i], i, obj) === breaker) return; 82 } 83 } else { 84 for (var key in obj) { 85 if (_.has(obj, key)) { 86 if (iterator.call(context, obj[key], key, obj) === breaker) return; 87 } 88 } 89 } 90 }; 91 92 // Return the results of applying the iterator to each element. 93 // Delegates to **ECMAScript 5**'s native `map` if available. 94 _.map = _.collect = function(obj, iterator, context) { 95 var results = []; 96 if (obj == null) return results; 97 if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); 98 each(obj, function(value, index, list) { 99 results[results.length] = iterator.call(context, value, index, list); 100 }); 101 return results; 102 }; 103 104 var reduceError = 'Reduce of empty array with no initial value'; 105 106 // **Reduce** builds up a single result from a list of values, aka `inject`, 107 // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. 108 _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { 109 var initial = arguments.length > 2; 110 if (obj == null) obj = []; 111 if (nativeReduce && obj.reduce === nativeReduce) { 112 if (context) iterator = _.bind(iterator, context); 113 return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); 114 } 115 each(obj, function(value, index, list) { 116 if (!initial) { 117 memo = value; 118 initial = true; 119 } else { 120 memo = iterator.call(context, memo, value, index, list); 121 } 122 }); 123 if (!initial) throw new TypeError(reduceError); 124 return memo; 125 }; 126 127 // The right-associative version of reduce, also known as `foldr`. 128 // Delegates to **ECMAScript 5**'s native `reduceRight` if available. 129 _.reduceRight = _.foldr = function(obj, iterator, memo, context) { 130 var initial = arguments.length > 2; 131 if (obj == null) obj = []; 132 if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { 133 if (context) iterator = _.bind(iterator, context); 134 return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); 135 } 136 var length = obj.length; 137 if (length !== +length) { 138 var keys = _.keys(obj); 139 length = keys.length; 140 } 141 each(obj, function(value, index, list) { 142 index = keys ? keys[--length] : --length; 143 if (!initial) { 144 memo = obj[index]; 145 initial = true; 146 } else { 147 memo = iterator.call(context, memo, obj[index], index, list); 148 } 149 }); 150 if (!initial) throw new TypeError(reduceError); 151 return memo; 152 }; 153 154 // Return the first value which passes a truth test. Aliased as `detect`. 155 _.find = _.detect = function(obj, iterator, context) { 156 var result; 157 any(obj, function(value, index, list) { 158 if (iterator.call(context, value, index, list)) { 159 result = value; 160 return true; 161 } 162 }); 163 return result; 164 }; 165 166 // Return all the elements that pass a truth test. 167 // Delegates to **ECMAScript 5**'s native `filter` if available. 168 // Aliased as `select`. 169 _.filter = _.select = function(obj, iterator, context) { 170 var results = []; 171 if (obj == null) return results; 172 if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); 173 each(obj, function(value, index, list) { 174 if (iterator.call(context, value, index, list)) results[results.length] = value; 175 }); 176 return results; 177 }; 178 179 // Return all the elements for which a truth test fails. 180 _.reject = function(obj, iterator, context) { 181 return _.filter(obj, function(value, index, list) { 182 return !iterator.call(context, value, index, list); 183 }, context); 184 }; 185 186 // Determine whether all of the elements match a truth test. 187 // Delegates to **ECMAScript 5**'s native `every` if available. 188 // Aliased as `all`. 189 _.every = _.all = function(obj, iterator, context) { 190 iterator || (iterator = _.identity); 191 var result = true; 192 if (obj == null) return result; 193 if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); 194 each(obj, function(value, index, list) { 195 if (!(result = result && iterator.call(context, value, index, list))) return breaker; 196 }); 197 return !!result; 198 }; 199 200 // Determine if at least one element in the object matches a truth test. 201 // Delegates to **ECMAScript 5**'s native `some` if available. 202 // Aliased as `any`. 203 var any = _.some = _.any = function(obj, iterator, context) { 204 iterator || (iterator = _.identity); 205 var result = false; 206 if (obj == null) return result; 207 if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); 208 each(obj, function(value, index, list) { 209 if (result || (result = iterator.call(context, value, index, list))) return breaker; 210 }); 211 return !!result; 212 }; 213 214 // Determine if the array or object contains a given value (using `===`). 215 // Aliased as `include`. 216 _.contains = _.include = function(obj, target) { 217 if (obj == null) return false; 218 if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; 219 return any(obj, function(value) { 220 return value === target; 221 }); 222 }; 223 224 // Invoke a method (with arguments) on every item in a collection. 225 _.invoke = function(obj, method) { 226 var args = slice.call(arguments, 2); 227 var isFunc = _.isFunction(method); 228 return _.map(obj, function(value) { 229 return (isFunc ? method : value[method]).apply(value, args); 230 }); 231 }; 232 233 // Convenience version of a common use case of `map`: fetching a property. 234 _.pluck = function(obj, key) { 235 return _.map(obj, function(value){ return value[key]; }); 236 }; 237 238 // Convenience version of a common use case of `filter`: selecting only objects 239 // containing specific `key:value` pairs. 240 _.where = function(obj, attrs, first) { 241 if (_.isEmpty(attrs)) return first ? null : []; 242 return _[first ? 'find' : 'filter'](obj, function(value) { 243 for (var key in attrs) { 244 if (attrs[key] !== value[key]) return false; 245 } 246 return true; 247 }); 248 }; 249 250 // Convenience version of a common use case of `find`: getting the first object 251 // containing specific `key:value` pairs. 252 _.findWhere = function(obj, attrs) { 253 return _.where(obj, attrs, true); 254 }; 255 256 // Return the maximum element or (element-based computation). 257 // Can't optimize arrays of integers longer than 65,535 elements. 258 // See: https://bugs.webkit.org/show_bug.cgi?id=80797 259 _.max = function(obj, iterator, context) { 260 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 261 return Math.max.apply(Math, obj); 262 } 263 if (!iterator && _.isEmpty(obj)) return -Infinity; 264 var result = {computed : -Infinity, value: -Infinity}; 265 each(obj, function(value, index, list) { 266 var computed = iterator ? iterator.call(context, value, index, list) : value; 267 computed >= result.computed && (result = {value : value, computed : computed}); 268 }); 269 return result.value; 270 }; 271 272 // Return the minimum element (or element-based computation). 273 _.min = function(obj, iterator, context) { 274 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 275 return Math.min.apply(Math, obj); 276 } 277 if (!iterator && _.isEmpty(obj)) return Infinity; 278 var result = {computed : Infinity, value: Infinity}; 279 each(obj, function(value, index, list) { 280 var computed = iterator ? iterator.call(context, value, index, list) : value; 281 computed < result.computed && (result = {value : value, computed : computed}); 282 }); 283 return result.value; 284 }; 285 286 // Shuffle an array. 287 _.shuffle = function(obj) { 288 var rand; 289 var index = 0; 290 var shuffled = []; 291 each(obj, function(value) { 292 rand = _.random(index++); 293 shuffled[index - 1] = shuffled[rand]; 294 shuffled[rand] = value; 295 }); 296 return shuffled; 297 }; 298 299 // An internal function to generate lookup iterators. 300 var lookupIterator = function(value) { 301 return _.isFunction(value) ? value : function(obj){ return obj[value]; }; 302 }; 303 304 // Sort the object's values by a criterion produced by an iterator. 305 _.sortBy = function(obj, value, context) { 306 var iterator = lookupIterator(value); 307 return _.pluck(_.map(obj, function(value, index, list) { 308 return { 309 value : value, 310 index : index, 311 criteria : iterator.call(context, value, index, list) 312 }; 313 }).sort(function(left, right) { 314 var a = left.criteria; 315 var b = right.criteria; 316 if (a !== b) { 317 if (a > b || a === void 0) return 1; 318 if (a < b || b === void 0) return -1; 319 } 320 return left.index < right.index ? -1 : 1; 321 }), 'value'); 322 }; 323 324 // An internal function used for aggregate "group by" operations. 325 var group = function(obj, value, context, behavior) { 326 var result = {}; 327 var iterator = lookupIterator(value || _.identity); 328 each(obj, function(value, index) { 329 var key = iterator.call(context, value, index, obj); 330 behavior(result, key, value); 331 }); 332 return result; 333 }; 334 335 // Groups the object's values by a criterion. Pass either a string attribute 336 // to group by, or a function that returns the criterion. 337 _.groupBy = function(obj, value, context) { 338 return group(obj, value, context, function(result, key, value) { 339 (_.has(result, key) ? result[key] : (result[key] = [])).push(value); 340 }); 341 }; 342 343 // Counts instances of an object that group by a certain criterion. Pass 344 // either a string attribute to count by, or a function that returns the 345 // criterion. 346 _.countBy = function(obj, value, context) { 347 return group(obj, value, context, function(result, key) { 348 if (!_.has(result, key)) result[key] = 0; 349 result[key]++; 350 }); 351 }; 352 353 // Use a comparator function to figure out the smallest index at which 354 // an object should be inserted so as to maintain order. Uses binary search. 355 _.sortedIndex = function(array, obj, iterator, context) { 356 iterator = iterator == null ? _.identity : lookupIterator(iterator); 357 var value = iterator.call(context, obj); 358 var low = 0, high = array.length; 359 while (low < high) { 360 var mid = (low + high) >>> 1; 361 iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; 362 } 363 return low; 364 }; 365 366 // Safely convert anything iterable into a real, live array. 367 _.toArray = function(obj) { 368 if (!obj) return []; 369 if (_.isArray(obj)) return slice.call(obj); 370 if (obj.length === +obj.length) return _.map(obj, _.identity); 371 return _.values(obj); 372 }; 373 374 // Return the number of elements in an object. 375 _.size = function(obj) { 376 if (obj == null) return 0; 377 return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; 378 }; 379 380 // Array Functions 381 // --------------- 382 383 // Get the first element of an array. Passing **n** will return the first N 384 // values in the array. Aliased as `head` and `take`. The **guard** check 385 // allows it to work with `_.map`. 386 _.first = _.head = _.take = function(array, n, guard) { 387 if (array == null) return void 0; 388 return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; 389 }; 390 391 // Returns everything but the last entry of the array. Especially useful on 392 // the arguments object. Passing **n** will return all the values in 393 // the array, excluding the last N. The **guard** check allows it to work with 394 // `_.map`. 395 _.initial = function(array, n, guard) { 396 return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); 397 }; 398 399 // Get the last element of an array. Passing **n** will return the last N 400 // values in the array. The **guard** check allows it to work with `_.map`. 401 _.last = function(array, n, guard) { 402 if (array == null) return void 0; 403 if ((n != null) && !guard) { 404 return slice.call(array, Math.max(array.length - n, 0)); 405 } else { 406 return array[array.length - 1]; 407 } 408 }; 409 410 // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 411 // Especially useful on the arguments object. Passing an **n** will return 412 // the rest N values in the array. The **guard** 413 // check allows it to work with `_.map`. 414 _.rest = _.tail = _.drop = function(array, n, guard) { 415 return slice.call(array, (n == null) || guard ? 1 : n); 416 }; 417 418 // Trim out all falsy values from an array. 419 _.compact = function(array) { 420 return _.filter(array, _.identity); 421 }; 422 423 // Internal implementation of a recursive `flatten` function. 424 var flatten = function(input, shallow, output) { 425 each(input, function(value) { 426 if (_.isArray(value)) { 427 shallow ? push.apply(output, value) : flatten(value, shallow, output); 428 } else { 429 output.push(value); 430 } 431 }); 432 return output; 433 }; 434 435 // Return a completely flattened version of an array. 436 _.flatten = function(array, shallow) { 437 return flatten(array, shallow, []); 438 }; 439 440 // Return a version of the array that does not contain the specified value(s). 441 _.without = function(array) { 442 return _.difference(array, slice.call(arguments, 1)); 443 }; 444 445 // Produce a duplicate-free version of the array. If the array has already 446 // been sorted, you have the option of using a faster algorithm. 447 // Aliased as `unique`. 448 _.uniq = _.unique = function(array, isSorted, iterator, context) { 449 if (_.isFunction(isSorted)) { 450 context = iterator; 451 iterator = isSorted; 452 isSorted = false; 453 } 454 var initial = iterator ? _.map(array, iterator, context) : array; 455 var results = []; 456 var seen = []; 457 each(initial, function(value, index) { 458 if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { 459 seen.push(value); 460 results.push(array[index]); 461 } 462 }); 463 return results; 464 }; 465 466 // Produce an array that contains the union: each distinct element from all of 467 // the passed-in arrays. 468 _.union = function() { 469 return _.uniq(concat.apply(ArrayProto, arguments)); 470 }; 471 472 // Produce an array that contains every item shared between all the 473 // passed-in arrays. 474 _.intersection = function(array) { 475 var rest = slice.call(arguments, 1); 476 return _.filter(_.uniq(array), function(item) { 477 return _.every(rest, function(other) { 478 return _.indexOf(other, item) >= 0; 479 }); 480 }); 481 }; 482 483 // Take the difference between one array and a number of other arrays. 484 // Only the elements present in just the first array will remain. 485 _.difference = function(array) { 486 var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); 487 return _.filter(array, function(value){ return !_.contains(rest, value); }); 488 }; 489 490 // Zip together multiple lists into a single array -- elements that share 491 // an index go together. 492 _.zip = function() { 493 var args = slice.call(arguments); 494 var length = _.max(_.pluck(args, 'length')); 495 var results = new Array(length); 496 for (var i = 0; i < length; i++) { 497 results[i] = _.pluck(args, "" + i); 498 } 499 return results; 500 }; 501 502 // Converts lists into objects. Pass either a single array of `[key, value]` 503 // pairs, or two parallel arrays of the same length -- one of keys, and one of 504 // the corresponding values. 505 _.object = function(list, values) { 506 if (list == null) return {}; 507 var result = {}; 508 for (var i = 0, l = list.length; i < l; i++) { 509 if (values) { 510 result[list[i]] = values[i]; 511 } else { 512 result[list[i][0]] = list[i][1]; 513 } 514 } 515 return result; 516 }; 517 518 // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), 519 // we need this function. Return the position of the first occurrence of an 520 // item in an array, or -1 if the item is not included in the array. 521 // Delegates to **ECMAScript 5**'s native `indexOf` if available. 522 // If the array is large and already in sort order, pass `true` 523 // for **isSorted** to use binary search. 524 _.indexOf = function(array, item, isSorted) { 525 if (array == null) return -1; 526 var i = 0, l = array.length; 527 if (isSorted) { 528 if (typeof isSorted == 'number') { 529 i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); 530 } else { 531 i = _.sortedIndex(array, item); 532 return array[i] === item ? i : -1; 533 } 534 } 535 if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); 536 for (; i < l; i++) if (array[i] === item) return i; 537 return -1; 538 }; 539 540 // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. 541 _.lastIndexOf = function(array, item, from) { 542 if (array == null) return -1; 543 var hasIndex = from != null; 544 if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { 545 return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); 546 } 547 var i = (hasIndex ? from : array.length); 548 while (i--) if (array[i] === item) return i; 549 return -1; 550 }; 551 552 // Generate an integer Array containing an arithmetic progression. A port of 553 // the native Python `range()` function. See 554 // [the Python documentation](http://docs.python.org/library/functions.html#range). 555 _.range = function(start, stop, step) { 556 if (arguments.length <= 1) { 557 stop = start || 0; 558 start = 0; 559 } 560 step = arguments[2] || 1; 561 562 var len = Math.max(Math.ceil((stop - start) / step), 0); 563 var idx = 0; 564 var range = new Array(len); 565 566 while(idx < len) { 567 range[idx++] = start; 568 start += step; 569 } 570 571 return range; 572 }; 573 574 // Function (ahem) Functions 575 // ------------------ 576 577 // Create a function bound to a given object (assigning `this`, and arguments, 578 // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 579 // available. 580 _.bind = function(func, context) { 581 if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 582 var args = slice.call(arguments, 2); 583 return function() { 584 return func.apply(context, args.concat(slice.call(arguments))); 585 }; 586 }; 587 588 // Partially apply a function by creating a version that has had some of its 589 // arguments pre-filled, without changing its dynamic `this` context. 590 _.partial = function(func) { 591 var args = slice.call(arguments, 1); 592 return function() { 593 return func.apply(this, args.concat(slice.call(arguments))); 594 }; 595 }; 596 597 // Bind all of an object's methods to that object. Useful for ensuring that 598 // all callbacks defined on an object belong to it. 599 _.bindAll = function(obj) { 600 var funcs = slice.call(arguments, 1); 601 if (funcs.length === 0) funcs = _.functions(obj); 602 each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); 603 return obj; 604 }; 605 606 // Memoize an expensive function by storing its results. 607 _.memoize = function(func, hasher) { 608 var memo = {}; 609 hasher || (hasher = _.identity); 610 return function() { 611 var key = hasher.apply(this, arguments); 612 return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); 613 }; 614 }; 615 616 // Delays a function for the given number of milliseconds, and then calls 617 // it with the arguments supplied. 618 _.delay = function(func, wait) { 619 var args = slice.call(arguments, 2); 620 return setTimeout(function(){ return func.apply(null, args); }, wait); 621 }; 622 623 // Defers a function, scheduling it to run after the current call stack has 624 // cleared. 625 _.defer = function(func) { 626 return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); 627 }; 628 629 // Returns a function, that, when invoked, will only be triggered at most once 630 // during a given window of time. 631 _.throttle = function(func, wait) { 632 var context, args, timeout, result; 633 var previous = 0; 634 var later = function() { 635 previous = new Date; 636 timeout = null; 637 result = func.apply(context, args); 638 }; 639 return function() { 640 var now = new Date; 641 var remaining = wait - (now - previous); 642 context = this; 643 args = arguments; 644 if (remaining <= 0) { 645 clearTimeout(timeout); 646 timeout = null; 647 previous = now; 648 result = func.apply(context, args); 649 } else if (!timeout) { 650 timeout = setTimeout(later, remaining); 651 } 652 return result; 653 }; 654 }; 655 656 // Returns a function, that, as long as it continues to be invoked, will not 657 // be triggered. The function will be called after it stops being called for 658 // N milliseconds. If `immediate` is passed, trigger the function on the 659 // leading edge, instead of the trailing. 660 _.debounce = function(func, wait, immediate) { 661 var timeout, result; 662 return function() { 663 var context = this, args = arguments; 664 var later = function() { 665 timeout = null; 666 if (!immediate) result = func.apply(context, args); 667 }; 668 var callNow = immediate && !timeout; 669 clearTimeout(timeout); 670 timeout = setTimeout(later, wait); 671 if (callNow) result = func.apply(context, args); 672 return result; 673 }; 674 }; 675 676 // Returns a function that will be executed at most one time, no matter how 677 // often you call it. Useful for lazy initialization. 678 _.once = function(func) { 679 var ran = false, memo; 680 return function() { 681 if (ran) return memo; 682 ran = true; 683 memo = func.apply(this, arguments); 684 func = null; 685 return memo; 686 }; 687 }; 688 689 // Returns the first function passed as an argument to the second, 690 // allowing you to adjust arguments, run code before and after, and 691 // conditionally execute the original function. 692 _.wrap = function(func, wrapper) { 693 return function() { 694 var args = [func]; 695 push.apply(args, arguments); 696 return wrapper.apply(this, args); 697 }; 698 }; 699 700 // Returns a function that is the composition of a list of functions, each 701 // consuming the return value of the function that follows. 702 _.compose = function() { 703 var funcs = arguments; 704 return function() { 705 var args = arguments; 706 for (var i = funcs.length - 1; i >= 0; i--) { 707 args = [funcs[i].apply(this, args)]; 708 } 709 return args[0]; 710 }; 711 }; 712 713 // Returns a function that will only be executed after being called N times. 714 _.after = function(times, func) { 715 if (times <= 0) return func(); 716 return function() { 717 if (--times < 1) { 718 return func.apply(this, arguments); 719 } 720 }; 721 }; 722 723 // Object Functions 724 // ---------------- 725 726 // Retrieve the names of an object's properties. 727 // Delegates to **ECMAScript 5**'s native `Object.keys` 728 _.keys = nativeKeys || function(obj) { 729 if (obj !== Object(obj)) throw new TypeError('Invalid object'); 730 var keys = []; 731 for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; 732 return keys; 733 }; 734 735 // Retrieve the values of an object's properties. 736 _.values = function(obj) { 737 var values = []; 738 for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); 739 return values; 740 }; 741 742 // Convert an object into a list of `[key, value]` pairs. 743 _.pairs = function(obj) { 744 var pairs = []; 745 for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); 746 return pairs; 747 }; 748 749 // Invert the keys and values of an object. The values must be serializable. 750 _.invert = function(obj) { 751 var result = {}; 752 for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; 753 return result; 754 }; 755 756 // Return a sorted list of the function names available on the object. 757 // Aliased as `methods` 758 _.functions = _.methods = function(obj) { 759 var names = []; 760 for (var key in obj) { 761 if (_.isFunction(obj[key])) names.push(key); 762 } 763 return names.sort(); 764 }; 765 766 // Extend a given object with all the properties in passed-in object(s). 767 _.extend = function(obj) { 768 each(slice.call(arguments, 1), function(source) { 769 if (source) { 770 for (var prop in source) { 771 obj[prop] = source[prop]; 772 } 773 } 774 }); 775 return obj; 776 }; 777 778 // Return a copy of the object only containing the whitelisted properties. 779 _.pick = function(obj) { 780 var copy = {}; 781 var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 782 each(keys, function(key) { 783 if (key in obj) copy[key] = obj[key]; 784 }); 785 return copy; 786 }; 787 788 // Return a copy of the object without the blacklisted properties. 789 _.omit = function(obj) { 790 var copy = {}; 791 var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 792 for (var key in obj) { 793 if (!_.contains(keys, key)) copy[key] = obj[key]; 794 } 795 return copy; 796 }; 797 798 // Fill in a given object with default properties. 799 _.defaults = function(obj) { 800 each(slice.call(arguments, 1), function(source) { 801 if (source) { 802 for (var prop in source) { 803 if (obj[prop] == null) obj[prop] = source[prop]; 804 } 805 } 806 }); 807 return obj; 808 }; 809 810 // Create a (shallow-cloned) duplicate of an object. 811 _.clone = function(obj) { 812 if (!_.isObject(obj)) return obj; 813 return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 814 }; 815 816 // Invokes interceptor with the obj, and then returns obj. 817 // The primary purpose of this method is to "tap into" a method chain, in 818 // order to perform operations on intermediate results within the chain. 819 _.tap = function(obj, interceptor) { 820 interceptor(obj); 821 return obj; 822 }; 823 824 // Internal recursive comparison function for `isEqual`. 825 var eq = function(a, b, aStack, bStack) { 826 // Identical objects are equal. `0 === -0`, but they aren't identical. 827 // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. 828 if (a === b) return a !== 0 || 1 / a == 1 / b; 829 // A strict comparison is necessary because `null == undefined`. 830 if (a == null || b == null) return a === b; 831 // Unwrap any wrapped objects. 832 if (a instanceof _) a = a._wrapped; 833 if (b instanceof _) b = b._wrapped; 834 // Compare `[[Class]]` names. 835 var className = toString.call(a); 836 if (className != toString.call(b)) return false; 837 switch (className) { 838 // Strings, numbers, dates, and booleans are compared by value. 839 case '[object String]': 840 // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 841 // equivalent to `new String("5")`. 842 return a == String(b); 843 case '[object Number]': 844 // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 845 // other numeric values. 846 return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); 847 case '[object Date]': 848 case '[object Boolean]': 849 // Coerce dates and booleans to numeric primitive values. Dates are compared by their 850 // millisecond representations. Note that invalid dates with millisecond representations 851 // of `NaN` are not equivalent. 852 return +a == +b; 853 // RegExps are compared by their source patterns and flags. 854 case '[object RegExp]': 855 return a.source == b.source && 856 a.global == b.global && 857 a.multiline == b.multiline && 858 a.ignoreCase == b.ignoreCase; 859 } 860 if (typeof a != 'object' || typeof b != 'object') return false; 861 // Assume equality for cyclic structures. The algorithm for detecting cyclic 862 // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 863 var length = aStack.length; 864 while (length--) { 865 // Linear search. Performance is inversely proportional to the number of 866 // unique nested structures. 867 if (aStack[length] == a) return bStack[length] == b; 868 } 869 // Add the first object to the stack of traversed objects. 870 aStack.push(a); 871 bStack.push(b); 872 var size = 0, result = true; 873 // Recursively compare objects and arrays. 874 if (className == '[object Array]') { 875 // Compare array lengths to determine if a deep comparison is necessary. 876 size = a.length; 877 result = size == b.length; 878 if (result) { 879 // Deep compare the contents, ignoring non-numeric properties. 880 while (size--) { 881 if (!(result = eq(a[size], b[size], aStack, bStack))) break; 882 } 883 } 884 } else { 885 // Objects with different constructors are not equivalent, but `Object`s 886 // from different frames are. 887 var aCtor = a.constructor, bCtor = b.constructor; 888 if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && 889 _.isFunction(bCtor) && (bCtor instanceof bCtor))) { 890 return false; 891 } 892 // Deep compare objects. 893 for (var key in a) { 894 if (_.has(a, key)) { 895 // Count the expected number of properties. 896 size++; 897 // Deep compare each member. 898 if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; 899 } 900 } 901 // Ensure that both objects contain the same number of properties. 902 if (result) { 903 for (key in b) { 904 if (_.has(b, key) && !(size--)) break; 905 } 906 result = !size; 907 } 908 } 909 // Remove the first object from the stack of traversed objects. 910 aStack.pop(); 911 bStack.pop(); 912 return result; 913 }; 914 915 // Perform a deep comparison to check if two objects are equal. 916 _.isEqual = function(a, b) { 917 return eq(a, b, [], []); 918 }; 919 920 // Is a given array, string, or object empty? 921 // An "empty" object has no enumerable own-properties. 922 _.isEmpty = function(obj) { 923 if (obj == null) return true; 924 if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; 925 for (var key in obj) if (_.has(obj, key)) return false; 926 return true; 927 }; 928 929 // Is a given value a DOM element? 930 _.isElement = function(obj) { 931 return !!(obj && obj.nodeType === 1); 932 }; 933 934 // Is a given value an array? 935 // Delegates to ECMA5's native Array.isArray 936 _.isArray = nativeIsArray || function(obj) { 937 return toString.call(obj) == '[object Array]'; 938 }; 939 940 // Is a given variable an object? 941 _.isObject = function(obj) { 942 return obj === Object(obj); 943 }; 944 945 // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. 946 each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { 947 _['is' + name] = function(obj) { 948 return toString.call(obj) == '[object ' + name + ']'; 949 }; 950 }); 951 952 // Define a fallback version of the method in browsers (ahem, IE), where 953 // there isn't any inspectable "Arguments" type. 954 if (!_.isArguments(arguments)) { 955 _.isArguments = function(obj) { 956 return !!(obj && _.has(obj, 'callee')); 957 }; 958 } 959 960 // Optimize `isFunction` if appropriate. 961 if (typeof (/./) !== 'function') { 962 _.isFunction = function(obj) { 963 return typeof obj === 'function'; 964 }; 965 } 966 967 // Is a given object a finite number? 968 _.isFinite = function(obj) { 969 return isFinite(obj) && !isNaN(parseFloat(obj)); 970 }; 971 972 // Is the given value `NaN`? (NaN is the only number which does not equal itself). 973 _.isNaN = function(obj) { 974 return _.isNumber(obj) && obj != +obj; 975 }; 976 977 // Is a given value a boolean? 978 _.isBoolean = function(obj) { 979 return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; 980 }; 981 982 // Is a given value equal to null? 983 _.isNull = function(obj) { 984 return obj === null; 985 }; 986 987 // Is a given variable undefined? 988 _.isUndefined = function(obj) { 989 return obj === void 0; 990 }; 991 992 // Shortcut function for checking if an object has a given property directly 993 // on itself (in other words, not on a prototype). 994 _.has = function(obj, key) { 995 return hasOwnProperty.call(obj, key); 996 }; 997 998 // Utility Functions 999 // ----------------- 1000 1001 // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1002 // previous owner. Returns a reference to the Underscore object. 1003 _.noConflict = function() { 1004 root._ = previousUnderscore; 1005 return this; 1006 }; 1007 1008 // Keep the identity function around for default iterators. 1009 _.identity = function(value) { 1010 return value; 1011 }; 1012 1013 // Run a function **n** times. 1014 _.times = function(n, iterator, context) { 1015 var accum = Array(n); 1016 for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); 1017 return accum; 1018 }; 1019 1020 // Return a random integer between min and max (inclusive). 1021 _.random = function(min, max) { 1022 if (max == null) { 1023 max = min; 1024 min = 0; 1025 } 1026 return min + Math.floor(Math.random() * (max - min + 1)); 1027 }; 1028 1029 // List of HTML entities for escaping. 1030 var entityMap = { 1031 escape: { 1032 '&': '&', 1033 '<': '<', 1034 '>': '>', 1035 '"': '"', 1036 "'": ''', 1037 '/': '/' 1038 } 1039 }; 1040 entityMap.unescape = _.invert(entityMap.escape); 1041 1042 // Regexes containing the keys and values listed immediately above. 1043 var entityRegexes = { 1044 escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), 1045 unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') 1046 }; 1047 1048 // Functions for escaping and unescaping strings to/from HTML interpolation. 1049 _.each(['escape', 'unescape'], function(method) { 1050 _[method] = function(string) { 1051 if (string == null) return ''; 1052 return ('' + string).replace(entityRegexes[method], function(match) { 1053 return entityMap[method][match]; 1054 }); 1055 }; 1056 }); 1057 1058 // If the value of the named property is a function then invoke it; 1059 // otherwise, return it. 1060 _.result = function(object, property) { 1061 if (object == null) return null; 1062 var value = object[property]; 1063 return _.isFunction(value) ? value.call(object) : value; 1064 }; 1065 1066 // Add your own custom functions to the Underscore object. 1067 _.mixin = function(obj) { 1068 each(_.functions(obj), function(name){ 1069 var func = _[name] = obj[name]; 1070 _.prototype[name] = function() { 1071 var args = [this._wrapped]; 1072 push.apply(args, arguments); 1073 return result.call(this, func.apply(_, args)); 1074 }; 1075 }); 1076 }; 1077 1078 // Generate a unique integer id (unique within the entire client session). 1079 // Useful for temporary DOM ids. 1080 var idCounter = 0; 1081 _.uniqueId = function(prefix) { 1082 var id = ++idCounter + ''; 1083 return prefix ? prefix + id : id; 1084 }; 1085 1086 // By default, Underscore uses ERB-style template delimiters, change the 1087 // following template settings to use alternative delimiters. 1088 _.templateSettings = { 1089 evaluate : /<%([\s\S]+?)%>/g, 1090 interpolate : /<%=([\s\S]+?)%>/g, 1091 escape : /<%-([\s\S]+?)%>/g 1092 }; 1093 1094 // When customizing `templateSettings`, if you don't want to define an 1095 // interpolation, evaluation or escaping regex, we need one that is 1096 // guaranteed not to match. 1097 var noMatch = /(.)^/; 1098 1099 // Certain characters need to be escaped so that they can be put into a 1100 // string literal. 1101 var escapes = { 1102 "'": "'", 1103 '\\': '\\', 1104 '\r': 'r', 1105 '\n': 'n', 1106 '\t': 't', 1107 '\u2028': 'u2028', 1108 '\u2029': 'u2029' 1109 }; 1110 1111 var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 1112 1113 // JavaScript micro-templating, similar to John Resig's implementation. 1114 // Underscore templating handles arbitrary delimiters, preserves whitespace, 1115 // and correctly escapes quotes within interpolated code. 1116 _.template = function(text, data, settings) { 1117 var render; 1118 settings = _.defaults({}, settings, _.templateSettings); 1119 1120 // Combine delimiters into one regular expression via alternation. 1121 var matcher = new RegExp([ 1122 (settings.escape || noMatch).source, 1123 (settings.interpolate || noMatch).source, 1124 (settings.evaluate || noMatch).source 1125 ].join('|') + '|$', 'g'); 1126 1127 // Compile the template source, escaping string literals appropriately. 1128 var index = 0; 1129 var source = "__p+='"; 1130 text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1131 source += text.slice(index, offset) 1132 .replace(escaper, function(match) { return '\\' + escapes[match]; }); 1133 1134 if (escape) { 1135 source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1136 } 1137 if (interpolate) { 1138 source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1139 } 1140 if (evaluate) { 1141 source += "';\n" + evaluate + "\n__p+='"; 1142 } 1143 index = offset + match.length; 1144 return match; 1145 }); 1146 source += "';\n"; 1147 1148 // If a variable is not specified, place data values in local scope. 1149 if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1150 1151 source = "var __t,__p='',__j=Array.prototype.join," + 1152 "print=function(){__p+=__j.call(arguments,'');};\n" + 1153 source + "return __p;\n"; 1154 1155 try { 1156 render = new Function(settings.variable || 'obj', '_', source); 1157 } catch (e) { 1158 e.source = source; 1159 throw e; 1160 } 1161 1162 if (data) return render(data, _); 1163 var template = function(data) { 1164 return render.call(this, data, _); 1165 }; 1166 1167 // Provide the compiled function source as a convenience for precompilation. 1168 template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 1169 1170 return template; 1171 }; 1172 1173 // Add a "chain" function, which will delegate to the wrapper. 1174 _.chain = function(obj) { 1175 return _(obj).chain(); 1176 }; 1177 1178 // OOP 1179 // --------------- 1180 // If Underscore is called as a function, it returns a wrapped object that 1181 // can be used OO-style. This wrapper holds altered versions of all the 1182 // underscore functions. Wrapped objects may be chained. 1183 1184 // Helper function to continue chaining intermediate results. 1185 var result = function(obj) { 1186 return this._chain ? _(obj).chain() : obj; 1187 }; 1188 1189 // Add all of the Underscore functions to the wrapper object. 1190 _.mixin(_); 1191 1192 // Add all mutator Array functions to the wrapper. 1193 each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1194 var method = ArrayProto[name]; 1195 _.prototype[name] = function() { 1196 var obj = this._wrapped; 1197 method.apply(obj, arguments); 1198 if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; 1199 return result.call(this, obj); 1200 }; 1201 }); 1202 1203 // Add all accessor Array functions to the wrapper. 1204 each(['concat', 'join', 'slice'], function(name) { 1205 var method = ArrayProto[name]; 1206 _.prototype[name] = function() { 1207 return result.call(this, method.apply(this._wrapped, arguments)); 1208 }; 1209 }); 1210 1211 _.extend(_.prototype, { 1212 1213 // Start chaining a wrapped Underscore object. 1214 chain: function() { 1215 this._chain = true; 1216 return this; 1217 }, 1218 1219 // Extracts the result from a wrapped and chained object. 1220 value: function() { 1221 return this._wrapped; 1222 } 1223 1224 }); 1225 1226 }).call(this);1 // Underscore.js 1.4.4 2 // http://underscorejs.org 3 // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. 4 // Underscore may be freely distributed under the MIT license. 5 6 (function() { 7 8 // Baseline setup 9 // -------------- 10 11 // Establish the root object, `window` in the browser, or `global` on the server. 12 var root = this; 13 14 // Save the previous value of the `_` variable. 15 var previousUnderscore = root._; 16 17 // Establish the object that gets returned to break out of a loop iteration. 18 var breaker = {}; 19 20 // Save bytes in the minified (but not gzipped) version: 21 var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 22 23 // Create quick reference variables for speed access to core prototypes. 24 var push = ArrayProto.push, 25 slice = ArrayProto.slice, 26 concat = ArrayProto.concat, 27 toString = ObjProto.toString, 28 hasOwnProperty = ObjProto.hasOwnProperty; 29 30 // All **ECMAScript 5** native function implementations that we hope to use 31 // are declared here. 32 var 33 nativeForEach = ArrayProto.forEach, 34 nativeMap = ArrayProto.map, 35 nativeReduce = ArrayProto.reduce, 36 nativeReduceRight = ArrayProto.reduceRight, 37 nativeFilter = ArrayProto.filter, 38 nativeEvery = ArrayProto.every, 39 nativeSome = ArrayProto.some, 40 nativeIndexOf = ArrayProto.indexOf, 41 nativeLastIndexOf = ArrayProto.lastIndexOf, 42 nativeIsArray = Array.isArray, 43 nativeKeys = Object.keys, 44 nativeBind = FuncProto.bind; 45 46 // Create a safe reference to the Underscore object for use below. 47 var _ = function(obj) { 48 if (obj instanceof _) return obj; 49 if (!(this instanceof _)) return new _(obj); 50 this._wrapped = obj; 51 }; 52 53 // Export the Underscore object for **Node.js**, with 54 // backwards-compatibility for the old `require()` API. If we're in 55 // the browser, add `_` as a global object via a string identifier, 56 // for Closure Compiler "advanced" mode. 57 if (typeof exports !== 'undefined') { 58 if (typeof module !== 'undefined' && module.exports) { 59 exports = module.exports = _; 60 } 61 exports._ = _; 62 } else { 63 root._ = _; 64 } 65 66 // Current version. 67 _.VERSION = '1.4.4'; 68 69 // Collection Functions 70 // -------------------- 71 72 // The cornerstone, an `each` implementation, aka `forEach`. 73 // Handles objects with the built-in `forEach`, arrays, and raw objects. 74 // Delegates to **ECMAScript 5**'s native `forEach` if available. 75 var each = _.each = _.forEach = function(obj, iterator, context) { 76 if (obj == null) return; 77 if (nativeForEach && obj.forEach === nativeForEach) { 78 obj.forEach(iterator, context); 79 } else if (obj.length === +obj.length) { 80 for (var i = 0, l = obj.length; i < l; i++) { 81 if (iterator.call(context, obj[i], i, obj) === breaker) return; 82 } 83 } else { 84 for (var key in obj) { 85 if (_.has(obj, key)) { 86 if (iterator.call(context, obj[key], key, obj) === breaker) return; 87 } 88 } 89 } 90 }; 91 92 // Return the results of applying the iterator to each element. 93 // Delegates to **ECMAScript 5**'s native `map` if available. 94 _.map = _.collect = function(obj, iterator, context) { 95 var results = []; 96 if (obj == null) return results; 97 if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); 98 each(obj, function(value, index, list) { 99 results[results.length] = iterator.call(context, value, index, list); 100 }); 101 return results; 102 }; 103 104 var reduceError = 'Reduce of empty array with no initial value'; 105 106 // **Reduce** builds up a single result from a list of values, aka `inject`, 107 // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. 108 _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { 109 var initial = arguments.length > 2; 110 if (obj == null) obj = []; 111 if (nativeReduce && obj.reduce === nativeReduce) { 112 if (context) iterator = _.bind(iterator, context); 113 return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); 114 } 115 each(obj, function(value, index, list) { 116 if (!initial) { 117 memo = value; 118 initial = true; 119 } else { 120 memo = iterator.call(context, memo, value, index, list); 121 } 122 }); 123 if (!initial) throw new TypeError(reduceError); 124 return memo; 125 }; 126 127 // The right-associative version of reduce, also known as `foldr`. 128 // Delegates to **ECMAScript 5**'s native `reduceRight` if available. 129 _.reduceRight = _.foldr = function(obj, iterator, memo, context) { 130 var initial = arguments.length > 2; 131 if (obj == null) obj = []; 132 if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { 133 if (context) iterator = _.bind(iterator, context); 134 return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); 135 } 136 var length = obj.length; 137 if (length !== +length) { 138 var keys = _.keys(obj); 139 length = keys.length; 140 } 141 each(obj, function(value, index, list) { 142 index = keys ? keys[--length] : --length; 143 if (!initial) { 144 memo = obj[index]; 145 initial = true; 146 } else { 147 memo = iterator.call(context, memo, obj[index], index, list); 148 } 149 }); 150 if (!initial) throw new TypeError(reduceError); 151 return memo; 152 }; 153 154 // Return the first value which passes a truth test. Aliased as `detect`. 155 _.find = _.detect = function(obj, iterator, context) { 156 var result; 157 any(obj, function(value, index, list) { 158 if (iterator.call(context, value, index, list)) { 159 result = value; 160 return true; 161 } 162 }); 163 return result; 164 }; 165 166 // Return all the elements that pass a truth test. 167 // Delegates to **ECMAScript 5**'s native `filter` if available. 168 // Aliased as `select`. 169 _.filter = _.select = function(obj, iterator, context) { 170 var results = []; 171 if (obj == null) return results; 172 if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); 173 each(obj, function(value, index, list) { 174 if (iterator.call(context, value, index, list)) results[results.length] = value; 175 }); 176 return results; 177 }; 178 179 // Return all the elements for which a truth test fails. 180 _.reject = function(obj, iterator, context) { 181 return _.filter(obj, function(value, index, list) { 182 return !iterator.call(context, value, index, list); 183 }, context); 184 }; 185 186 // Determine whether all of the elements match a truth test. 187 // Delegates to **ECMAScript 5**'s native `every` if available. 188 // Aliased as `all`. 189 _.every = _.all = function(obj, iterator, context) { 190 iterator || (iterator = _.identity); 191 var result = true; 192 if (obj == null) return result; 193 if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); 194 each(obj, function(value, index, list) { 195 if (!(result = result && iterator.call(context, value, index, list))) return breaker; 196 }); 197 return !!result; 198 }; 199 200 // Determine if at least one element in the object matches a truth test. 201 // Delegates to **ECMAScript 5**'s native `some` if available. 202 // Aliased as `any`. 203 var any = _.some = _.any = function(obj, iterator, context) { 204 iterator || (iterator = _.identity); 205 var result = false; 206 if (obj == null) return result; 207 if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); 208 each(obj, function(value, index, list) { 209 if (result || (result = iterator.call(context, value, index, list))) return breaker; 210 }); 211 return !!result; 212 }; 213 214 // Determine if the array or object contains a given value (using `===`). 215 // Aliased as `include`. 216 _.contains = _.include = function(obj, target) { 217 if (obj == null) return false; 218 if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; 219 return any(obj, function(value) { 220 return value === target; 221 }); 222 }; 223 224 // Invoke a method (with arguments) on every item in a collection. 225 _.invoke = function(obj, method) { 226 var args = slice.call(arguments, 2); 227 var isFunc = _.isFunction(method); 228 return _.map(obj, function(value) { 229 return (isFunc ? method : value[method]).apply(value, args); 230 }); 231 }; 232 233 // Convenience version of a common use case of `map`: fetching a property. 234 _.pluck = function(obj, key) { 235 return _.map(obj, function(value){ return value[key]; }); 236 }; 237 238 // Convenience version of a common use case of `filter`: selecting only objects 239 // containing specific `key:value` pairs. 240 _.where = function(obj, attrs, first) { 241 if (_.isEmpty(attrs)) return first ? null : []; 242 return _[first ? 'find' : 'filter'](obj, function(value) { 243 for (var key in attrs) { 244 if (attrs[key] !== value[key]) return false; 245 } 246 return true; 247 }); 248 }; 249 250 // Convenience version of a common use case of `find`: getting the first object 251 // containing specific `key:value` pairs. 252 _.findWhere = function(obj, attrs) { 253 return _.where(obj, attrs, true); 254 }; 255 256 // Return the maximum element or (element-based computation). 257 // Can't optimize arrays of integers longer than 65,535 elements. 258 // See: https://bugs.webkit.org/show_bug.cgi?id=80797 259 _.max = function(obj, iterator, context) { 260 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 261 return Math.max.apply(Math, obj); 262 } 263 if (!iterator && _.isEmpty(obj)) return -Infinity; 264 var result = {computed : -Infinity, value: -Infinity}; 265 each(obj, function(value, index, list) { 266 var computed = iterator ? iterator.call(context, value, index, list) : value; 267 computed >= result.computed && (result = {value : value, computed : computed}); 268 }); 269 return result.value; 270 }; 271 272 // Return the minimum element (or element-based computation). 273 _.min = function(obj, iterator, context) { 274 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 275 return Math.min.apply(Math, obj); 276 } 277 if (!iterator && _.isEmpty(obj)) return Infinity; 278 var result = {computed : Infinity, value: Infinity}; 279 each(obj, function(value, index, list) { 280 var computed = iterator ? iterator.call(context, value, index, list) : value; 281 computed < result.computed && (result = {value : value, computed : computed}); 282 }); 283 return result.value; 284 }; 285 286 // Shuffle an array. 287 _.shuffle = function(obj) { 288 var rand; 289 var index = 0; 290 var shuffled = []; 291 each(obj, function(value) { 292 rand = _.random(index++); 293 shuffled[index - 1] = shuffled[rand]; 294 shuffled[rand] = value; 295 }); 296 return shuffled; 297 }; 298 299 // An internal function to generate lookup iterators. 300 var lookupIterator = function(value) { 301 return _.isFunction(value) ? value : function(obj){ return obj[value]; }; 302 }; 303 304 // Sort the object's values by a criterion produced by an iterator. 305 _.sortBy = function(obj, value, context) { 306 var iterator = lookupIterator(value); 307 return _.pluck(_.map(obj, function(value, index, list) { 308 return { 309 value : value, 310 index : index, 311 criteria : iterator.call(context, value, index, list) 312 }; 313 }).sort(function(left, right) { 314 var a = left.criteria; 315 var b = right.criteria; 316 if (a !== b) { 317 if (a > b || a === void 0) return 1; 318 if (a < b || b === void 0) return -1; 319 } 320 return left.index < right.index ? -1 : 1; 321 }), 'value'); 322 }; 323 324 // An internal function used for aggregate "group by" operations. 325 var group = function(obj, value, context, behavior) { 326 var result = {}; 327 var iterator = lookupIterator(value || _.identity); 328 each(obj, function(value, index) { 329 var key = iterator.call(context, value, index, obj); 330 behavior(result, key, value); 331 }); 332 return result; 333 }; 334 335 // Groups the object's values by a criterion. Pass either a string attribute 336 // to group by, or a function that returns the criterion. 337 _.groupBy = function(obj, value, context) { 338 return group(obj, value, context, function(result, key, value) { 339 (_.has(result, key) ? result[key] : (result[key] = [])).push(value); 340 }); 341 }; 342 343 // Counts instances of an object that group by a certain criterion. Pass 344 // either a string attribute to count by, or a function that returns the 345 // criterion. 346 _.countBy = function(obj, value, context) { 347 return group(obj, value, context, function(result, key) { 348 if (!_.has(result, key)) result[key] = 0; 349 result[key]++; 350 }); 351 }; 352 353 // Use a comparator function to figure out the smallest index at which 354 // an object should be inserted so as to maintain order. Uses binary search. 355 _.sortedIndex = function(array, obj, iterator, context) { 356 iterator = iterator == null ? _.identity : lookupIterator(iterator); 357 var value = iterator.call(context, obj); 358 var low = 0, high = array.length; 359 while (low < high) { 360 var mid = (low + high) >>> 1; 361 iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; 362 } 363 return low; 364 }; 365 366 // Safely convert anything iterable into a real, live array. 367 _.toArray = function(obj) { 368 if (!obj) return []; 369 if (_.isArray(obj)) return slice.call(obj); 370 if (obj.length === +obj.length) return _.map(obj, _.identity); 371 return _.values(obj); 372 }; 373 374 // Return the number of elements in an object. 375 _.size = function(obj) { 376 if (obj == null) return 0; 377 return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; 378 }; 379 380 // Array Functions 381 // --------------- 382 383 // Get the first element of an array. Passing **n** will return the first N 384 // values in the array. Aliased as `head` and `take`. The **guard** check 385 // allows it to work with `_.map`. 386 _.first = _.head = _.take = function(array, n, guard) { 387 if (array == null) return void 0; 388 return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; 389 }; 390 391 // Returns everything but the last entry of the array. Especially useful on 392 // the arguments object. Passing **n** will return all the values in 393 // the array, excluding the last N. The **guard** check allows it to work with 394 // `_.map`. 395 _.initial = function(array, n, guard) { 396 return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); 397 }; 398 399 // Get the last element of an array. Passing **n** will return the last N 400 // values in the array. The **guard** check allows it to work with `_.map`. 401 _.last = function(array, n, guard) { 402 if (array == null) return void 0; 403 if ((n != null) && !guard) { 404 return slice.call(array, Math.max(array.length - n, 0)); 405 } else { 406 return array[array.length - 1]; 407 } 408 }; 409 410 // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 411 // Especially useful on the arguments object. Passing an **n** will return 412 // the rest N values in the array. The **guard** 413 // check allows it to work with `_.map`. 414 _.rest = _.tail = _.drop = function(array, n, guard) { 415 return slice.call(array, (n == null) || guard ? 1 : n); 416 }; 417 418 // Trim out all falsy values from an array. 419 _.compact = function(array) { 420 return _.filter(array, _.identity); 421 }; 422 423 // Internal implementation of a recursive `flatten` function. 424 var flatten = function(input, shallow, output) { 425 each(input, function(value) { 426 if (_.isArray(value)) { 427 shallow ? push.apply(output, value) : flatten(value, shallow, output); 428 } else { 429 output.push(value); 430 } 431 }); 432 return output; 433 }; 434 435 // Return a completely flattened version of an array. 436 _.flatten = function(array, shallow) { 437 return flatten(array, shallow, []); 438 }; 439 440 // Return a version of the array that does not contain the specified value(s). 441 _.without = function(array) { 442 return _.difference(array, slice.call(arguments, 1)); 443 }; 444 445 // Produce a duplicate-free version of the array. If the array has already 446 // been sorted, you have the option of using a faster algorithm. 447 // Aliased as `unique`. 448 _.uniq = _.unique = function(array, isSorted, iterator, context) { 449 if (_.isFunction(isSorted)) { 450 context = iterator; 451 iterator = isSorted; 452 isSorted = false; 453 } 454 var initial = iterator ? _.map(array, iterator, context) : array; 455 var results = []; 456 var seen = []; 457 each(initial, function(value, index) { 458 if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { 459 seen.push(value); 460 results.push(array[index]); 461 } 462 }); 463 return results; 464 }; 465 466 // Produce an array that contains the union: each distinct element from all of 467 // the passed-in arrays. 468 _.union = function() { 469 return _.uniq(concat.apply(ArrayProto, arguments)); 470 }; 471 472 // Produce an array that contains every item shared between all the 473 // passed-in arrays. 474 _.intersection = function(array) { 475 var rest = slice.call(arguments, 1); 476 return _.filter(_.uniq(array), function(item) { 477 return _.every(rest, function(other) { 478 return _.indexOf(other, item) >= 0; 479 }); 480 }); 481 }; 482 483 // Take the difference between one array and a number of other arrays. 484 // Only the elements present in just the first array will remain. 485 _.difference = function(array) { 486 var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); 487 return _.filter(array, function(value){ return !_.contains(rest, value); }); 488 }; 489 490 // Zip together multiple lists into a single array -- elements that share 491 // an index go together. 492 _.zip = function() { 493 var args = slice.call(arguments); 494 var length = _.max(_.pluck(args, 'length')); 495 var results = new Array(length); 496 for (var i = 0; i < length; i++) { 497 results[i] = _.pluck(args, "" + i); 498 } 499 return results; 500 }; 501 502 // Converts lists into objects. Pass either a single array of `[key, value]` 503 // pairs, or two parallel arrays of the same length -- one of keys, and one of 504 // the corresponding values. 505 _.object = function(list, values) { 506 if (list == null) return {}; 507 var result = {}; 508 for (var i = 0, l = list.length; i < l; i++) { 509 if (values) { 510 result[list[i]] = values[i]; 511 } else { 512 result[list[i][0]] = list[i][1]; 513 } 514 } 515 return result; 516 }; 517 518 // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), 519 // we need this function. Return the position of the first occurrence of an 520 // item in an array, or -1 if the item is not included in the array. 521 // Delegates to **ECMAScript 5**'s native `indexOf` if available. 522 // If the array is large and already in sort order, pass `true` 523 // for **isSorted** to use binary search. 524 _.indexOf = function(array, item, isSorted) { 525 if (array == null) return -1; 526 var i = 0, l = array.length; 527 if (isSorted) { 528 if (typeof isSorted == 'number') { 529 i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); 530 } else { 531 i = _.sortedIndex(array, item); 532 return array[i] === item ? i : -1; 533 } 534 } 535 if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); 536 for (; i < l; i++) if (array[i] === item) return i; 537 return -1; 538 }; 539 540 // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. 541 _.lastIndexOf = function(array, item, from) { 542 if (array == null) return -1; 543 var hasIndex = from != null; 544 if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { 545 return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); 546 } 547 var i = (hasIndex ? from : array.length); 548 while (i--) if (array[i] === item) return i; 549 return -1; 550 }; 551 552 // Generate an integer Array containing an arithmetic progression. A port of 553 // the native Python `range()` function. See 554 // [the Python documentation](http://docs.python.org/library/functions.html#range). 555 _.range = function(start, stop, step) { 556 if (arguments.length <= 1) { 557 stop = start || 0; 558 start = 0; 559 } 560 step = arguments[2] || 1; 561 562 var len = Math.max(Math.ceil((stop - start) / step), 0); 563 var idx = 0; 564 var range = new Array(len); 565 566 while(idx < len) { 567 range[idx++] = start; 568 start += step; 569 } 570 571 return range; 572 }; 573 574 // Function (ahem) Functions 575 // ------------------ 576 577 // Create a function bound to a given object (assigning `this`, and arguments, 578 // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 579 // available. 580 _.bind = function(func, context) { 581 if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 582 var args = slice.call(arguments, 2); 583 return function() { 584 return func.apply(context, args.concat(slice.call(arguments))); 585 }; 586 }; 587 588 // Partially apply a function by creating a version that has had some of its 589 // arguments pre-filled, without changing its dynamic `this` context. 590 _.partial = function(func) { 591 var args = slice.call(arguments, 1); 592 return function() { 593 return func.apply(this, args.concat(slice.call(arguments))); 594 }; 595 }; 596 597 // Bind all of an object's methods to that object. Useful for ensuring that 598 // all callbacks defined on an object belong to it. 599 _.bindAll = function(obj) { 600 var funcs = slice.call(arguments, 1); 601 if (funcs.length === 0) funcs = _.functions(obj); 602 each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); 603 return obj; 604 }; 605 606 // Memoize an expensive function by storing its results. 607 _.memoize = function(func, hasher) { 608 var memo = {}; 609 hasher || (hasher = _.identity); 610 return function() { 611 var key = hasher.apply(this, arguments); 612 return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); 613 }; 614 }; 615 616 // Delays a function for the given number of milliseconds, and then calls 617 // it with the arguments supplied. 618 _.delay = function(func, wait) { 619 var args = slice.call(arguments, 2); 620 return setTimeout(function(){ return func.apply(null, args); }, wait); 621 }; 622 623 // Defers a function, scheduling it to run after the current call stack has 624 // cleared. 625 _.defer = function(func) { 626 return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); 627 }; 628 629 // Returns a function, that, when invoked, will only be triggered at most once 630 // during a given window of time. 631 _.throttle = function(func, wait) { 632 var context, args, timeout, result; 633 var previous = 0; 634 var later = function() { 635 previous = new Date; 636 timeout = null; 637 result = func.apply(context, args); 638 }; 639 return function() { 640 var now = new Date; 641 var remaining = wait - (now - previous); 642 context = this; 643 args = arguments; 644 if (remaining <= 0) { 645 clearTimeout(timeout); 646 timeout = null; 647 previous = now; 648 result = func.apply(context, args); 649 } else if (!timeout) { 650 timeout = setTimeout(later, remaining); 651 } 652 return result; 653 }; 654 }; 655 656 // Returns a function, that, as long as it continues to be invoked, will not 657 // be triggered. The function will be called after it stops being called for 658 // N milliseconds. If `immediate` is passed, trigger the function on the 659 // leading edge, instead of the trailing. 660 _.debounce = function(func, wait, immediate) { 661 var timeout, result; 662 return function() { 663 var context = this, args = arguments; 664 var later = function() { 665 timeout = null; 666 if (!immediate) result = func.apply(context, args); 667 }; 668 var callNow = immediate && !timeout; 669 clearTimeout(timeout); 670 timeout = setTimeout(later, wait); 671 if (callNow) result = func.apply(context, args); 672 return result; 673 }; 674 }; 675 676 // Returns a function that will be executed at most one time, no matter how 677 // often you call it. Useful for lazy initialization. 678 _.once = function(func) { 679 var ran = false, memo; 680 return function() { 681 if (ran) return memo; 682 ran = true; 683 memo = func.apply(this, arguments); 684 func = null; 685 return memo; 686 }; 687 }; 688 689 // Returns the first function passed as an argument to the second, 690 // allowing you to adjust arguments, run code before and after, and 691 // conditionally execute the original function. 692 _.wrap = function(func, wrapper) { 693 return function() { 694 var args = [func]; 695 push.apply(args, arguments); 696 return wrapper.apply(this, args); 697 }; 698 }; 699 700 // Returns a function that is the composition of a list of functions, each 701 // consuming the return value of the function that follows. 702 _.compose = function() { 703 var funcs = arguments; 704 return function() { 705 var args = arguments; 706 for (var i = funcs.length - 1; i >= 0; i--) { 707 args = [funcs[i].apply(this, args)]; 708 } 709 return args[0]; 710 }; 711 }; 712 713 // Returns a function that will only be executed after being called N times. 714 _.after = function(times, func) { 715 if (times <= 0) return func(); 716 return function() { 717 if (--times < 1) { 718 return func.apply(this, arguments); 719 } 720 }; 721 }; 722 723 // Object Functions 724 // ---------------- 725 726 // Retrieve the names of an object's properties. 727 // Delegates to **ECMAScript 5**'s native `Object.keys` 728 _.keys = nativeKeys || function(obj) { 729 if (obj !== Object(obj)) throw new TypeError('Invalid object'); 730 var keys = []; 731 for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; 732 return keys; 733 }; 734 735 // Retrieve the values of an object's properties. 736 _.values = function(obj) { 737 var values = []; 738 for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); 739 return values; 740 }; 741 742 // Convert an object into a list of `[key, value]` pairs. 743 _.pairs = function(obj) { 744 var pairs = []; 745 for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); 746 return pairs; 747 }; 748 749 // Invert the keys and values of an object. The values must be serializable. 750 _.invert = function(obj) { 751 var result = {}; 752 for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; 753 return result; 754 }; 755 756 // Return a sorted list of the function names available on the object. 757 // Aliased as `methods` 758 _.functions = _.methods = function(obj) { 759 var names = []; 760 for (var key in obj) { 761 if (_.isFunction(obj[key])) names.push(key); 762 } 763 return names.sort(); 764 }; 765 766 // Extend a given object with all the properties in passed-in object(s). 767 _.extend = function(obj) { 768 each(slice.call(arguments, 1), function(source) { 769 if (source) { 770 for (var prop in source) { 771 obj[prop] = source[prop]; 772 } 773 } 774 }); 775 return obj; 776 }; 777 778 // Return a copy of the object only containing the whitelisted properties. 779 _.pick = function(obj) { 780 var copy = {}; 781 var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 782 each(keys, function(key) { 783 if (key in obj) copy[key] = obj[key]; 784 }); 785 return copy; 786 }; 787 788 // Return a copy of the object without the blacklisted properties. 789 _.omit = function(obj) { 790 var copy = {}; 791 var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 792 for (var key in obj) { 793 if (!_.contains(keys, key)) copy[key] = obj[key]; 794 } 795 return copy; 796 }; 797 798 // Fill in a given object with default properties. 799 _.defaults = function(obj) { 800 each(slice.call(arguments, 1), function(source) { 801 if (source) { 802 for (var prop in source) { 803 if (obj[prop] == null) obj[prop] = source[prop]; 804 } 805 } 806 }); 807 return obj; 808 }; 809 810 // Create a (shallow-cloned) duplicate of an object. 811 _.clone = function(obj) { 812 if (!_.isObject(obj)) return obj; 813 return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 814 }; 815 816 // Invokes interceptor with the obj, and then returns obj. 817 // The primary purpose of this method is to "tap into" a method chain, in 818 // order to perform operations on intermediate results within the chain. 819 _.tap = function(obj, interceptor) { 820 interceptor(obj); 821 return obj; 822 }; 823 824 // Internal recursive comparison function for `isEqual`. 825 var eq = function(a, b, aStack, bStack) { 826 // Identical objects are equal. `0 === -0`, but they aren't identical. 827 // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. 828 if (a === b) return a !== 0 || 1 / a == 1 / b; 829 // A strict comparison is necessary because `null == undefined`. 830 if (a == null || b == null) return a === b; 831 // Unwrap any wrapped objects. 832 if (a instanceof _) a = a._wrapped; 833 if (b instanceof _) b = b._wrapped; 834 // Compare `[[Class]]` names. 835 var className = toString.call(a); 836 if (className != toString.call(b)) return false; 837 switch (className) { 838 // Strings, numbers, dates, and booleans are compared by value. 839 case '[object String]': 840 // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 841 // equivalent to `new String("5")`. 842 return a == String(b); 843 case '[object Number]': 844 // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 845 // other numeric values. 846 return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); 847 case '[object Date]': 848 case '[object Boolean]': 849 // Coerce dates and booleans to numeric primitive values. Dates are compared by their 850 // millisecond representations. Note that invalid dates with millisecond representations 851 // of `NaN` are not equivalent. 852 return +a == +b; 853 // RegExps are compared by their source patterns and flags. 854 case '[object RegExp]': 855 return a.source == b.source && 856 a.global == b.global && 857 a.multiline == b.multiline && 858 a.ignoreCase == b.ignoreCase; 859 } 860 if (typeof a != 'object' || typeof b != 'object') return false; 861 // Assume equality for cyclic structures. The algorithm for detecting cyclic 862 // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 863 var length = aStack.length; 864 while (length--) { 865 // Linear search. Performance is inversely proportional to the number of 866 // unique nested structures. 867 if (aStack[length] == a) return bStack[length] == b; 868 } 869 // Add the first object to the stack of traversed objects. 870 aStack.push(a); 871 bStack.push(b); 872 var size = 0, result = true; 873 // Recursively compare objects and arrays. 874 if (className == '[object Array]') { 875 // Compare array lengths to determine if a deep comparison is necessary. 876 size = a.length; 877 result = size == b.length; 878 if (result) { 879 // Deep compare the contents, ignoring non-numeric properties. 880 while (size--) { 881 if (!(result = eq(a[size], b[size], aStack, bStack))) break; 882 } 883 } 884 } else { 885 // Objects with different constructors are not equivalent, but `Object`s 886 // from different frames are. 887 var aCtor = a.constructor, bCtor = b.constructor; 888 if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && 889 _.isFunction(bCtor) && (bCtor instanceof bCtor))) { 890 return false; 891 } 892 // Deep compare objects. 893 for (var key in a) { 894 if (_.has(a, key)) { 895 // Count the expected number of properties. 896 size++; 897 // Deep compare each member. 898 if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; 899 } 900 } 901 // Ensure that both objects contain the same number of properties. 902 if (result) { 903 for (key in b) { 904 if (_.has(b, key) && !(size--)) break; 905 } 906 result = !size; 907 } 908 } 909 // Remove the first object from the stack of traversed objects. 910 aStack.pop(); 911 bStack.pop(); 912 return result; 913 }; 914 915 // Perform a deep comparison to check if two objects are equal. 916 _.isEqual = function(a, b) { 917 return eq(a, b, [], []); 918 }; 919 920 // Is a given array, string, or object empty? 921 // An "empty" object has no enumerable own-properties. 922 _.isEmpty = function(obj) { 923 if (obj == null) return true; 924 if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; 925 for (var key in obj) if (_.has(obj, key)) return false; 926 return true; 927 }; 928 929 // Is a given value a DOM element? 930 _.isElement = function(obj) { 931 return !!(obj && obj.nodeType === 1); 932 }; 933 934 // Is a given value an array? 935 // Delegates to ECMA5's native Array.isArray 936 _.isArray = nativeIsArray || function(obj) { 937 return toString.call(obj) == '[object Array]'; 938 }; 939 940 // Is a given variable an object? 941 _.isObject = function(obj) { 942 return obj === Object(obj); 943 }; 944 945 // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. 946 each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { 947 _['is' + name] = function(obj) { 948 return toString.call(obj) == '[object ' + name + ']'; 949 }; 950 }); 951 952 // Define a fallback version of the method in browsers (ahem, IE), where 953 // there isn't any inspectable "Arguments" type. 954 if (!_.isArguments(arguments)) { 955 _.isArguments = function(obj) { 956 return !!(obj && _.has(obj, 'callee')); 957 }; 958 } 959 960 // Optimize `isFunction` if appropriate. 961 if (typeof (/./) !== 'function') { 962 _.isFunction = function(obj) { 963 return typeof obj === 'function'; 964 }; 965 } 966 967 // Is a given object a finite number? 968 _.isFinite = function(obj) { 969 return isFinite(obj) && !isNaN(parseFloat(obj)); 970 }; 971 972 // Is the given value `NaN`? (NaN is the only number which does not equal itself). 973 _.isNaN = function(obj) { 974 return _.isNumber(obj) && obj != +obj; 975 }; 976 977 // Is a given value a boolean? 978 _.isBoolean = function(obj) { 979 return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; 980 }; 981 982 // Is a given value equal to null? 983 _.isNull = function(obj) { 984 return obj === null; 985 }; 986 987 // Is a given variable undefined? 988 _.isUndefined = function(obj) { 989 return obj === void 0; 990 }; 991 992 // Shortcut function for checking if an object has a given property directly 993 // on itself (in other words, not on a prototype). 994 _.has = function(obj, key) { 995 return hasOwnProperty.call(obj, key); 996 }; 997 998 // Utility Functions 999 // ----------------- 1000 1001 // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1002 // previous owner. Returns a reference to the Underscore object. 1003 _.noConflict = function() { 1004 root._ = previousUnderscore; 1005 return this; 1006 }; 1007 1008 // Keep the identity function around for default iterators. 1009 _.identity = function(value) { 1010 return value; 1011 }; 1012 1013 // Run a function **n** times. 1014 _.times = function(n, iterator, context) { 1015 var accum = Array(n); 1016 for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); 1017 return accum; 1018 }; 1019 1020 // Return a random integer between min and max (inclusive). 1021 _.random = function(min, max) { 1022 if (max == null) { 1023 max = min; 1024 min = 0; 1025 } 1026 return min + Math.floor(Math.random() * (max - min + 1)); 1027 }; 1028 1029 // List of HTML entities for escaping. 1030 var entityMap = { 1031 escape: { 1032 '&': '&', 1033 '<': '<', 1034 '>': '>', 1035 '"': '"', 1036 "'": ''', 1037 '/': '/' 1038 } 1039 }; 1040 entityMap.unescape = _.invert(entityMap.escape); 1041 1042 // Regexes containing the keys and values listed immediately above. 1043 var entityRegexes = { 1044 escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), 1045 unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') 1046 }; 1047 1048 // Functions for escaping and unescaping strings to/from HTML interpolation. 1049 _.each(['escape', 'unescape'], function(method) { 1050 _[method] = function(string) { 1051 if (string == null) return ''; 1052 return ('' + string).replace(entityRegexes[method], function(match) { 1053 return entityMap[method][match]; 1054 }); 1055 }; 1056 }); 1057 1058 // If the value of the named property is a function then invoke it; 1059 // otherwise, return it. 1060 _.result = function(object, property) { 1061 if (object == null) return null; 1062 var value = object[property]; 1063 return _.isFunction(value) ? value.call(object) : value; 1064 }; 1065 1066 // Add your own custom functions to the Underscore object. 1067 _.mixin = function(obj) { 1068 each(_.functions(obj), function(name){ 1069 var func = _[name] = obj[name]; 1070 _.prototype[name] = function() { 1071 var args = [this._wrapped]; 1072 push.apply(args, arguments); 1073 return result.call(this, func.apply(_, args)); 1074 }; 1075 }); 1076 }; 1077 1078 // Generate a unique integer id (unique within the entire client session). 1079 // Useful for temporary DOM ids. 1080 var idCounter = 0; 1081 _.uniqueId = function(prefix) { 1082 var id = ++idCounter + ''; 1083 return prefix ? prefix + id : id; 1084 }; 1085 1086 // By default, Underscore uses ERB-style template delimiters, change the 1087 // following template settings to use alternative delimiters. 1088 _.templateSettings = { 1089 evaluate : /<%([\s\S]+?)%>/g, 1090 interpolate : /<%=([\s\S]+?)%>/g, 1091 escape : /<%-([\s\S]+?)%>/g 1092 }; 1093 1094 // When customizing `templateSettings`, if you don't want to define an 1095 // interpolation, evaluation or escaping regex, we need one that is 1096 // guaranteed not to match. 1097 var noMatch = /(.)^/; 1098 1099 // Certain characters need to be escaped so that they can be put into a 1100 // string literal. 1101 var escapes = { 1102 "'": "'", 1103 '\\': '\\', 1104 '\r': 'r', 1105 '\n': 'n', 1106 '\t': 't', 1107 '\u2028': 'u2028', 1108 '\u2029': 'u2029' 1109 }; 1110 1111 var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 1112 1113 // JavaScript micro-templating, similar to John Resig's implementation. 1114 // Underscore templating handles arbitrary delimiters, preserves whitespace, 1115 // and correctly escapes quotes within interpolated code. 1116 _.template = function(text, data, settings) { 1117 var render; 1118 settings = _.defaults({}, settings, _.templateSettings); 1119 1120 // Combine delimiters into one regular expression via alternation. 1121 var matcher = new RegExp([ 1122 (settings.escape || noMatch).source, 1123 (settings.interpolate || noMatch).source, 1124 (settings.evaluate || noMatch).source 1125 ].join('|') + '|$', 'g'); 1126 1127 // Compile the template source, escaping string literals appropriately. 1128 var index = 0; 1129 var source = "__p+='"; 1130 text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1131 source += text.slice(index, offset) 1132 .replace(escaper, function(match) { return '\\' + escapes[match]; }); 1133 1134 if (escape) { 1135 source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1136 } 1137 if (interpolate) { 1138 source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1139 } 1140 if (evaluate) { 1141 source += "';\n" + evaluate + "\n__p+='"; 1142 } 1143 index = offset + match.length; 1144 return match; 1145 }); 1146 source += "';\n"; 1147 1148 // If a variable is not specified, place data values in local scope. 1149 if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1150 1151 source = "var __t,__p='',__j=Array.prototype.join," + 1152 "print=function(){__p+=__j.call(arguments,'');};\n" + 1153 source + "return __p;\n"; 1154 1155 try { 1156 render = new Function(settings.variable || 'obj', '_', source); 1157 } catch (e) { 1158 e.source = source; 1159 throw e; 1160 } 1161 1162 if (data) return render(data, _); 1163 var template = function(data) { 1164 return render.call(this, data, _); 1165 }; 1166 1167 // Provide the compiled function source as a convenience for precompilation. 1168 template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 1169 1170 return template; 1171 }; 1172 1173 // Add a "chain" function, which will delegate to the wrapper. 1174 _.chain = function(obj) { 1175 return _(obj).chain(); 1176 }; 1177 1178 // OOP 1179 // --------------- 1180 // If Underscore is called as a function, it returns a wrapped object that 1181 // can be used OO-style. This wrapper holds altered versions of all the 1182 // underscore functions. Wrapped objects may be chained. 1183 1184 // Helper function to continue chaining intermediate results. 1185 var result = function(obj) { 1186 return this._chain ? _(obj).chain() : obj; 1187 }; 1188 1189 // Add all of the Underscore functions to the wrapper object. 1190 _.mixin(_); 1191 1192 // Add all mutator Array functions to the wrapper. 1193 each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1194 var method = ArrayProto[name]; 1195 _.prototype[name] = function() { 1196 var obj = this._wrapped; 1197 method.apply(obj, arguments); 1198 if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; 1199 return result.call(this, obj); 1200 }; 1201 }); 1202 1203 // Add all accessor Array functions to the wrapper. 1204 each(['concat', 'join', 'slice'], function(name) { 1205 var method = ArrayProto[name]; 1206 _.prototype[name] = function() { 1207 return result.call(this, method.apply(this._wrapped, arguments)); 1208 }; 1209 }); 1210 1211 _.extend(_.prototype, { 1212 1213 // Start chaining a wrapped Underscore object. 1214 chain: function() { 1215 this._chain = true; 1216 return this; 1217 }, 1218 1219 // Extracts the result from a wrapped and chained object. 1220 value: function() { 1221 return this._wrapped; 1222 } 1223 1224 }); 1225 1226 }).call(this);
联系我们 |
---|
文章看不懂?联系我们为您免费解答!免费助力个人,小企站点! |
① 电话:020-2206-9892 |
② QQ咨询:1025174874 |
③ 邮件:info@361sale.com |
④ 工作时间:周一至周五,9:30-18:30,节假日休息 |
THE END
暂无评论内容