/*! * warmsea JavaScript Library v1.0.0 * * Copyright 2009, 2014 Su Su * Released under the MIT license * * Date: 2014-10-24 */ //// START OF THE underscore.js SOURCE CODE // Underscore.js 1.7.0 // http://underscorejs.org // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, slice = ArrayProto.slice, concat = ArrayProto.concat, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object. if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // Current version. _.VERSION = '1.7.0'; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. var createCallback = function(func, context, argCount) { if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; case 2: return function(value, other) { return func.call(context, value, other); }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; }; // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either // identity, an arbitrary callback, a property matcher, or a property accessor. _.iteratee = function(value, context, argCount) { if (value == null) return _.identity; if (_.isFunction(value)) return createCallback(value, context, argCount); if (_.isObject(value)) return _.matches(value); return _.property(value); }; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles raw objects in addition to array-likes. Treats all // sparse array-likes as if they were dense. _.each = _.forEach = function(obj, iteratee, context) { if (obj == null) return obj; iteratee = createCallback(iteratee, context); var i, length = obj.length; if (length === +length) { for (i = 0; i < length; i++) { iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); } } return obj; }; // Return the results of applying the iteratee to each element. _.map = _.collect = function(obj, iteratee, context) { if (obj == null) return []; iteratee = _.iteratee(iteratee, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, results = Array(length), currentKey; for (var index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results; }; var reduceError = 'Reduce of empty array with no initial value'; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. _.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) { if (obj == null) obj = []; iteratee = createCallback(iteratee, context, 4); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index = 0, currentKey; if (arguments.length < 3) { if (!length) throw new TypeError(reduceError); memo = obj[keys ? keys[index++] : index++]; } for (; index < length; index++) { currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; // The right-associative version of reduce, also known as `foldr`. _.reduceRight = _.foldr = function(obj, iteratee, memo, context) { if (obj == null) obj = []; iteratee = createCallback(iteratee, context, 4); var keys = obj.length !== + obj.length && _.keys(obj), index = (keys || obj).length, currentKey; if (arguments.length < 3) { if (!index) throw new TypeError(reduceError); memo = obj[keys ? keys[--index] : --index]; } while (index--) { currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, predicate, context) { var result; predicate = _.iteratee(predicate, context); _.some(obj, function(value, index, list) { if (predicate(value, index, list)) { result = value; return true; } }); return result; }; // Return all the elements that pass a truth test. // Aliased as `select`. _.filter = _.select = function(obj, predicate, context) { var results = []; if (obj == null) return results; predicate = _.iteratee(predicate, context); _.each(obj, function(value, index, list) { if (predicate(value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, predicate, context) { return _.filter(obj, _.negate(_.iteratee(predicate)), context); }; // Determine whether all of the elements match a truth test. // Aliased as `all`. _.every = _.all = function(obj, predicate, context) { if (obj == null) return true; predicate = _.iteratee(predicate, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index, currentKey; for (index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; }; // Determine if at least one element in the object matches a truth test. // Aliased as `any`. _.some = _.any = function(obj, predicate, context) { if (obj == null) return false; predicate = _.iteratee(predicate, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index, currentKey; for (index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false; }; // Determine if the array or object contains a given value (using `===`). // Aliased as `include`. _.contains = _.include = function(obj, target) { if (obj == null) return false; if (obj.length !== +obj.length) obj = _.values(obj); return _.indexOf(obj, target) >= 0; }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { return (isFunc ? method : value[method]).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. _.where = function(obj, attrs) { return _.filter(obj, _.matches(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { return _.find(obj, _.matches(attrs)); }; // Return the maximum element (or element-based computation). _.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value > result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Return the minimum element (or element-based computation). _.min = function(obj, iteratee, context) { var result = Infinity, lastComputed = Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value < result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed < lastComputed || computed === Infinity && result === Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Shuffle a collection, using the modern version of the // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var set = obj && obj.length === +obj.length ? obj : _.values(obj); var length = set.length; var shuffled = Array(length); for (var index = 0, rand; index < length; index++) { rand = _.random(0, index); if (rand !== index) shuffled[index] = shuffled[rand]; shuffled[rand] = set[index]; } return shuffled; }; // Sample **n** random values from a collection. // If **n** is not specified, returns a single random element. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { if (n == null || guard) { if (obj.length !== +obj.length) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n)); }; // Sort the object's values by a criterion produced by an iteratee. _.sortBy = function(obj, iteratee, context) { iteratee = _.iteratee(iteratee, context); return _.pluck(_.map(obj, function(value, index, list) { return { value: value, index: index, criteria: iteratee(value, index, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. var group = function(behavior) { return function(obj, iteratee, context) { var result = {}; iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); behavior(result, value, key); }); return result; }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = group(function(result, value, key) { if (_.has(result, key)) result[key].push(value); else result[key] = [value]; }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. _.indexBy = group(function(result, value, key) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. _.countBy = group(function(result, value, key) { if (_.has(result, key)) result[key]++; else result[key] = 1; }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iteratee, context) { iteratee = _.iteratee(iteratee, context, 1); var value = iteratee(obj); var low = 0, high = array.length; while (low < high) { var mid = low + high >>> 1; if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; } return low; }; // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (obj.length === +obj.length) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; return obj.length === +obj.length ? obj.length : _.keys(obj).length; }; // Split a collection into two arrays: one whose elements all satisfy the given // predicate, and one whose elements all do not satisfy the predicate. _.partition = function(obj, predicate, context) { predicate = _.iteratee(predicate, context); var pass = [], fail = []; _.each(obj, function(value, key, obj) { (predicate(value, key, obj) ? pass : fail).push(value); }); return [pass, fail]; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[0]; if (n < 0) return []; return slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. The **guard** check allows it to work with // `_.map`. _.initial = function(array, n, guard) { return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[array.length - 1]; return slice.call(array, Math.max(array.length - n, 0)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return // the rest N values in the array. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = _.drop = function(array, n, guard) { return slice.call(array, n == null || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, _.identity); }; // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, strict, output) { if (shallow && _.every(input, _.isArray)) { return concat.apply(output, input); } for (var i = 0, length = input.length; i < length; i++) { var value = input[i]; if (!_.isArray(value) && !_.isArguments(value)) { if (!strict) output.push(value); } else if (shallow) { push.apply(output, value); } else { flatten(value, shallow, strict, output); } } return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, false, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iteratee, context) { if (array == null) return []; if (!_.isBoolean(isSorted)) { context = iteratee; iteratee = isSorted; isSorted = false; } if (iteratee != null) iteratee = _.iteratee(iteratee, context); var result = []; var seen = []; for (var i = 0, length = array.length; i < length; i++) { var value = array[i]; if (isSorted) { if (!i || seen !== value) result.push(value); seen = value; } else if (iteratee) { var computed = iteratee(value, i, array); if (_.indexOf(seen, computed) < 0) { seen.push(computed); result.push(value); } } else if (_.indexOf(result, value) < 0) { result.push(value); } } return result; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { return _.uniq(flatten(arguments, true, true, [])); }; // Produce an array that contains every item shared between all the // passed-in arrays. _.intersection = function(array) { if (array == null) return []; var result = []; var argsLength = arguments.length; for (var i = 0, length = array.length; i < length; i++) { var item = array[i]; if (_.contains(result, item)) continue; for (var j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } if (j === argsLength) result.push(item); } return result; }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. _.difference = function(array) { var rest = flatten(slice.call(arguments, 1), true, true, []); return _.filter(array, function(value){ return !_.contains(rest, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function(array) { if (array == null) return []; var length = _.max(arguments, 'length').length; var results = Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(arguments, i); } return results; }; // Converts lists into objects. Pass either a single array of `[key, value]` // pairs, or two parallel arrays of the same length -- one of keys, and one of // the corresponding values. _.object = function(list, values) { if (list == null) return {}; var result = {}; for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { result[list[i][0]] = list[i][1]; } } return result; }; // Return the position of the first occurrence of an item in an array, // or -1 if the item is not included in the array. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted; } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } for (; i < length; i++) if (array[i] === item) return i; return -1; }; _.lastIndexOf = function(array, item, from) { if (array == null) return -1; var idx = array.length; if (typeof from == 'number') { idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1); } while (--idx >= 0) if (array[idx] === item) return idx; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { if (arguments.length <= 1) { stop = start || 0; start = 0; } step = step || 1; var length = Math.max(Math.ceil((stop - start) / step), 0); var range = Array(length); for (var idx = 0; idx < length; idx++, start += step) { range[idx] = start; } return range; }; // Function (ahem) Functions // ------------------ // Reusable constructor function for prototype setting. var Ctor = function(){}; // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); args = slice.call(arguments, 2); bound = function() { if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); Ctor.prototype = func.prototype; var self = new Ctor; Ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (_.isObject(result)) return result; return self; }; return bound; }; // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder, allowing any combination of arguments to be pre-filled. _.partial = function(func) { var boundArgs = slice.call(arguments, 1); return function() { var position = 0; var args = boundArgs.slice(); for (var i = 0, length = args.length; i < length; i++) { if (args[i] === _) args[i] = arguments[position++]; } while (position < arguments.length) args.push(arguments[position++]); return func.apply(this, args); }; }; // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. _.bindAll = function(obj) { var i, length = arguments.length, key; if (length <= 1) throw new Error('bindAll must be passed function names'); for (i = 1; i < length; i++) { key = arguments[i]; obj[key] = _.bind(obj[key], obj); } return obj; }; // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memoize = function(key) { var cache = memoize.cache; var address = hasher ? hasher.apply(this, arguments) : key; if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); return cache[address]; }; memoize.cache = {}; return memoize; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); }; // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = _.now() - timestamp; if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; // Returns the first function passed as an argument to the second, // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { return _.partial(wrapper, func); }; // Returns a negated version of the passed-in predicate. _.negate = function(predicate) { return function() { return !predicate.apply(this, arguments); }; }; // Returns a function that is the composition of a list of functions, each // consuming the return value of the function that follows. _.compose = function() { var args = arguments; var start = args.length - 1; return function() { var i = start; var result = args[start].apply(this, arguments); while (i--) result = args[i].call(this, result); return result; }; }; // Returns a function that will only be executed after being called N times. _.after = function(times, func) { return function() { if (--times < 1) { return func.apply(this, arguments); } }; }; // Returns a function that will only be executed before being called N times. _.before = function(times, func) { var memo; return function() { if (--times > 0) { memo = func.apply(this, arguments); } else { func = null; } return memo; }; }; // Returns a function that will be executed at most one time, no matter how // often you call it. Useful for lazy initialization. _.once = _.partial(_.before, 2); // Object Functions // ---------------- // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` _.keys = function(obj) { if (!_.isObject(obj)) return []; if (nativeKeys) return nativeKeys(obj); var keys = []; for (var key in obj) if (_.has(obj, key)) keys.push(key); return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { var keys = _.keys(obj); var length = keys.length; var values = Array(length); for (var i = 0; i < length; i++) { values[i] = obj[keys[i]]; } return values; }; // Convert an object into a list of `[key, value]` pairs. _.pairs = function(obj) { var keys = _.keys(obj); var length = keys.length; var pairs = Array(length); for (var i = 0; i < length; i++) { pairs[i] = [keys[i], obj[keys[i]]]; } return pairs; }; // Invert the keys and values of an object. The values must be serializable. _.invert = function(obj) { var result = {}; var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { result[obj[keys[i]]] = keys[i]; } return result; }; // Return a sorted list of the function names available on the object. // Aliased as `methods` _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; // Extend a given object with all the properties in passed-in object(s). _.extend = function(obj) { if (!_.isObject(obj)) return obj; var source, prop; for (var i = 1, length = arguments.length; i < length; i++) { source = arguments[i]; for (prop in source) { if (hasOwnProperty.call(source, prop)) { obj[prop] = source[prop]; } } } return obj; }; // Return a copy of the object only containing the whitelisted properties. _.pick = function(obj, iteratee, context) { var result = {}, key; if (obj == null) return result; if (_.isFunction(iteratee)) { iteratee = createCallback(iteratee, context); for (key in obj) { var value = obj[key]; if (iteratee(value, key, obj)) result[key] = value; } } else { var keys = concat.apply([], slice.call(arguments, 1)); obj = new Object(obj); for (var i = 0, length = keys.length; i < length; i++) { key = keys[i]; if (key in obj) result[key] = obj[key]; } } return result; }; // Return a copy of the object without the blacklisted properties. _.omit = function(obj, iteratee, context) { if (_.isFunction(iteratee)) { iteratee = _.negate(iteratee); } else { var keys = _.map(concat.apply([], slice.call(arguments, 1)), String); iteratee = function(value, key) { return !_.contains(keys, key); }; } return _.pick(obj, iteratee, context); }; // Fill in a given object with default properties. _.defaults = function(obj) { if (!_.isObject(obj)) return obj; for (var i = 1, length = arguments.length; i < length; i++) { var source = arguments[i]; for (var prop in source) { if (obj[prop] === void 0) obj[prop] = source[prop]; } } return obj; }; // Create a (shallow-cloned) duplicate of an object. _.clone = function(obj) { if (!_.isObject(obj)) return obj; return _.isArray(obj) ? obj.slice() : _.extend({}, obj); }; // Invokes interceptor with the obj, and then returns obj. // The primary purpose of this method is to "tap into" a method chain, in // order to perform operations on intermediate results within the chain. _.tap = function(obj, interceptor) { interceptor(obj); return obj; }; // Internal recursive comparison function for `isEqual`. var eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a === 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); if (className !== toString.call(b)) return false; switch (className) { // Strings, numbers, regular expressions, dates, and booleans are compared by value. case '[object RegExp]': // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return '' + a === '' + b; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. // Object(NaN) is equivalent to NaN if (+a !== +a) return +b !== +b; // An `egal` comparison is performed for other numeric values. return +a === 0 ? 1 / +a === 1 / b : +a === +b; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a === +b; } if (typeof a != 'object' || typeof b != 'object') return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] === a) return bStack[length] === b; } // Objects with different constructors are not equivalent, but `Object`s // from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && // Handle Object.create(x) cases 'constructor' in a && 'constructor' in b && !(_.isFunction(aCtor) && aCtor instanceof aCtor && _.isFunction(bCtor) && bCtor instanceof bCtor) ) { return false; } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size, result; // Recursively compare objects and arrays. if (className === '[object Array]') { // Compare array lengths to determine if a deep comparison is necessary. size = a.length; result = size === b.length; if (result) { // Deep compare the contents, ignoring non-numeric properties. while (size--) { if (!(result = eq(a[size], b[size], aStack, bStack))) break; } } } else { // Deep compare objects. var keys = _.keys(a), key; size = keys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. result = _.keys(b).length === size; if (result) { while (size--) { // Deep compare each member key = keys[size]; if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; } } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { return eq(a, b, [], []); }; // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { if (obj == null) return true; if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0; for (var key in obj) if (_.has(obj, key)) return false; return true; }; // Is a given value a DOM element? _.isElement = function(obj) { return !!(obj && obj.nodeType === 1); }; // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { return toString.call(obj) === '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { var type = typeof obj; return type === 'function' || type === 'object' && !!obj; }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; }); // Define a fallback version of the method in browsers (ahem, IE), where // there isn't any inspectable "Arguments" type. if (!_.isArguments(arguments)) { _.isArguments = function(obj) { return _.has(obj, 'callee'); }; } // Optimize `isFunction` if appropriate. Work around an IE 11 bug. if (typeof /./ !== 'function') { _.isFunction = function(obj) { return typeof obj == 'function' || false; }; } // Is a given object a finite number? _.isFinite = function(obj) { return isFinite(obj) && !isNaN(parseFloat(obj)); }; // Is the given value `NaN`? (NaN is the only number which does not equal itself). _.isNaN = function(obj) { return _.isNumber(obj) && obj !== +obj; }; // Is a given value a boolean? _.isBoolean = function(obj) { return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; }; // Is a given value equal to null? _.isNull = function(obj) { return obj === null; }; // Is a given variable undefined? _.isUndefined = function(obj) { return obj === void 0; }; // Shortcut function for checking if an object has a given property directly // on itself (in other words, not on a prototype). _.has = function(obj, key) { return obj != null && hasOwnProperty.call(obj, key); }; // Utility Functions // ----------------- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its // previous owner. Returns a reference to the Underscore object. _.noConflict = function() { root._ = previousUnderscore; return this; }; // Keep the identity function around for default iteratees. _.identity = function(value) { return value; }; _.constant = function(value) { return function() { return value; }; }; _.noop = function(){}; _.property = function(key) { return function(obj) { return obj[key]; }; }; // Returns a predicate for checking whether an object has a given set of `key:value` pairs. _.matches = function(attrs) { var pairs = _.pairs(attrs), length = pairs.length; return function(obj) { if (obj == null) return !length; obj = new Object(obj); for (var i = 0; i < length; i++) { var pair = pairs[i], key = pair[0]; if (pair[1] !== obj[key] || !(key in obj)) return false; } return true; }; }; // Run a function **n** times. _.times = function(n, iteratee, context) { var accum = Array(Math.max(0, n)); iteratee = createCallback(iteratee, context, 1); for (var i = 0; i < n; i++) accum[i] = iteratee(i); return accum; }; // Return a random integer between min and max (inclusive). _.random = function(min, max) { if (max == null) { max = min; min = 0; } return min + Math.floor(Math.random() * (max - min + 1)); }; // A (possibly faster) way to get the current timestamp as an integer. _.now = Date.now || function() { return new Date().getTime(); }; // List of HTML entities for escaping. var escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' }; var unescapeMap = _.invert(escapeMap); // Functions for escaping and unescaping strings to/from HTML interpolation. var createEscaper = function(map) { var escaper = function(match) { return map[match]; }; // Regexes for identifying a key that needs to be escaped var source = '(?:' + _.keys(map).join('|') + ')'; var testRegexp = RegExp(source); var replaceRegexp = RegExp(source, 'g'); return function(string) { string = string == null ? '' : '' + string; return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; }; }; _.escape = createEscaper(escapeMap); _.unescape = createEscaper(unescapeMap); // If the value of the named `property` is a function then invoke it with the // `object` as context; otherwise, return it. _.result = function(object, property) { if (object == null) return void 0; var value = object[property]; return _.isFunction(value) ? object[property]() : value; }; // Generate a unique integer id (unique within the entire client session). // Useful for temporary DOM ids. var idCounter = 0; _.uniqueId = function(prefix) { var id = ++idCounter + ''; return prefix ? prefix + id : id; }; // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an // interpolation, evaluation or escaping regex, we need one that is // guaranteed not to match. var noMatch = /(.)^/; // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { "'": "'", '\\': '\\', '\r': 'r', '\n': 'n', '\u2028': 'u2028', '\u2029': 'u2029' }; var escaper = /\\|'|\r|\n|\u2028|\u2029/g; var escapeChar = function(match) { return '\\' + escapes[match]; }; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. // NB: `oldSettings` only exists for backwards compatibility. _.template = function(text, settings, oldSettings) { if (!settings && oldSettings) settings = oldSettings; settings = _.defaults({}, settings, _.templateSettings); // Combine delimiters into one regular expression via alternation. var matcher = RegExp([ (settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source ].join('|') + '|$', 'g'); // Compile the template source, escaping string literals appropriately. var index = 0; var source = "__p+='"; text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { source += text.slice(index, offset).replace(escaper, escapeChar); index = offset + match.length; if (escape) { source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; } else if (interpolate) { source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; } else if (evaluate) { source += "';\n" + evaluate + "\n__p+='"; } // Adobe VMs need the match returned to produce the correct offest. return match; }); source += "';\n"; // If a variable is not specified, place data values in local scope. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + source + 'return __p;\n'; try { var render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; } var template = function(data) { return render.call(this, data, _); }; // Provide the compiled source as a convenience for precompilation. var argument = settings.variable || 'obj'; template.source = 'function(' + argument + '){\n' + source + '}'; return template; }; // Add a "chain" function. Start chaining a wrapped Underscore object. _.chain = function(obj) { var instance = _(obj); instance._chain = true; return instance; }; // OOP // --------------- // If Underscore is called as a function, it returns a wrapped object that // can be used OO-style. This wrapper holds altered versions of all the // underscore functions. Wrapped objects may be chained. // Helper function to continue chaining intermediate results. var result = function(obj) { return this._chain ? _(obj).chain() : obj; }; // Add your own custom functions to the Underscore object. _.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return result.call(this, func.apply(_, args)); }; }); }; // Add all of the Underscore functions to the wrapper object. _.mixin(_); // Add all mutator Array functions to the wrapper. _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; return result.call(this, obj); }; }); // Add all accessor Array functions to the wrapper. _.each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result.call(this, method.apply(this._wrapped, arguments)); }; }); // Extracts the result from a wrapped and chained object. _.prototype.value = function() { return this._wrapped; }; // AMD registration happens at the end for compatibility with AMD loaders // that may not enforce next-turn semantics on modules. Even though general // practice for AMD registration is to be anonymous, underscore registers // as a named module because, like jQuery, it is a base library that is // popular enough to be bundled in a third party lib, but not be part of // an AMD load request. Those cases could generate an error when an // anonymous define() is called outside of a loader request. if (typeof define === 'function' && define.amd) { define('underscore', [], function() { return _; }); } }.call(this)); //// END OF THE underscore.js SOURCE CODE (function(global, _) { 'use strict'; _ = _.noConflict(); var underscoreKeys = _.keys(_); /** * This will be the warmsea namespace. * @namespace * @alias warmsea */ var w = _.extend({}, _); /** * The unmodified underlying underscore object. */ w._ = w.underscore = _; /** * The version of this WarmseaJS. * @type {string} */ w.VERSION = '1.0.0'; /** * The global object of the executing environment. * @type {object} */ w.global = global; /** * Save the previous `warmsea`. */ var previousWarmsea = global.warmsea; /** * Return the current `warmsea` and restore the previous global one. * @return {warmsea} This warmsea object. */ w.noConflict = function() { global.warmsea = previousWarmsea; return this; }; /** * A function that throws an error with the message "Unimplemented". */ w.unimplemented = function() { w.error('Unimplemented'); }; /** * Throws an Error. * @method * @param {string} msg * @throws {Error} */ w.error = function(msg) { if (w.isFunction(w.format)) { msg = w.format.apply(w, arguments); } throw new Error(msg); }; /** * A compare function returns 1, if a < b; -1, if a > b; 0, otherwise. * @param {any} a * @param {any} b * @return {number} */ w.cmp = function(a, b) { return a > b ? 1 : a < b ? -1 : 0; }; /** * Tests if an item is in an array. * * @param {Array} arr The array * @param {*} item The item * @returns {Boolean} true, if the item is in the array; false, if not. */ w.inArray = function(arr, item) { if (!w.isArray(arr)) { return false; } return w.indexOf(arr, item) >= 0; }; /** * In-place stable sort. * * @param {Array} list The array to be sorted. * @param {Function} cmp The compare function; by default, it's w.cmp。 * @param {Object} context The context for cmp. * @returns {Array} The sorted array. */ w.sort = function(list, cmp, context) { cmp = cmp || w.cmp; var i; var len = list.length; for (i = 0; i < len; ++i) { // _$wssi stands for Warmsea Stable Sort Id // I hope no one is using it. list[i]._$wssi = i; } list.sort(function(a, b) { return cmp.call(context, a, b) || w.cmp(a._$wssi - b._$wssi); }); for (i = 0; i < len; ++i) { delete list[i]._$wssi; } return list; }; /** * Returns a (stably) sorted (shallow) copy of an array. * * @param {Array} list The array to be sorted. * @param {Function} cmp The compare function; by default, it's w.cmp. * @param {Object} context The context for cmp. * @returns {Array} A sorted copy. */ w.sorted = function(list, cmp, context) { var copy = w.clone(list); w.sort(copy, cmp, context); return copy; }; w.Queue = (function() { /** * Queue class. A Queue is a FIFO (First In, First Out) collection. * * @class * @name warmsea.Queue * @param {?function} validator A value validation function. */ var Queue = function(validator) { this._validator = validator; this.clear(); this.allowEmptyDequeue = true; }; /** * Returns number of elements in the Queue. * * @return {number} */ Queue.prototype.count = function() { return this._queue.length - this._offset; }; /** * Number of elements in the Queue. * @type {number} */ if (w.isFunction(Object.defineProperty)) { // I'm not interested in fully supporting IE 8. // But I don't want it dies in the first place. Object.defineProperty(Queue.prototype, 'length', { enumerable: true, get: function() { return this.count(); } }); } /** * Remove all objects from the Queue. * * @method * @name warmsea.Queue.prototype.clear */ Queue.prototype.clear = function() { this._queue = []; this._offset = 0; }; /** * Add an object to the end of the Queue. * * @method * @name warmsea.Queue.prototype.enqueue * @param {Object} value An object. * @throws {Error} If value can't pass the validation. */ Queue.prototype.enqueue = function(value) { if (this._validator && !this._validator(value)) { w.error('Value not accepted: ' + value); } this._queue.push(value); }; /** * Remove the object at the beginning of the Queue and return it. * * @method * @name warmsea.Queue.prototype.dequeue * @return {any} The object at the beginning of the Queue. */ Queue.prototype.dequeue = function() { if (this.length === 0) { if (this.allowEmptyDequeue) { return undefined; } else { w.error('The queue is empty'); } } var item = this._queue[this._offset++]; if (this._offset > 16 && this._offset * 2 > this._queue.length) { this._queue = this._queue.slice(this._offset); this._offset = 0; } return item; }; /** * Return the object at the beginning of the Queue without removing it. * * @method * @name warmsea.Queue.prototype.peek * @return {any} The object at the beginning of the Queue. */ Queue.prototype.peek = function() { if (this.length === 0) { if (this.allowEmptyDequeue) { return undefined; } else { w.error('Dequeueing an empty queue is not allowed'); } } return this._queue[this._offset]; }; return Queue; })(); w.Stack = (function() { /** * Stack class. A Stack is a FILO (First In, Last Out) collection. * * @class * @name warmsea.Stack * @param {?function} validator A value validation function. */ var Stack = function(validator) { this._validator = validator; this.clear(); this.allowEmptyPop = true; }; /** * Returns number of elements in the Stack. * * @return {number} */ Stack.prototype.count = function() { return this._top + 1; }; /** * Number of elements in the Queue. * @type {number} */ if (w.isFunction(Object.defineProperty)) { // I'm not interested in fully supporting IE 8. // But I don't want it dies in the first place. Object.defineProperty(Stack.prototype, 'length', { enumerable: true, get: function() { return this.count(); } }); } /** * Remove all objects from the Stack. * * @method * @name warmsea.Stack.prototype.clear */ Stack.prototype.clear = function() { this._stack = []; this._top = -1; }; /** * Add an object to the top of the Stack. * * @method * @name warmsea.Stack.prototype.push * @param {Object} value An object. * @throws {Error} If value can't pass the validation. */ Stack.prototype.push = function(value) { if (this._validator && !this._validator(value)) { w.error('Value not accepted: ' + value); } this._top++; if (this._top < this.length) { this._stack[this._top] = value; } else { this._stack.push(value); } }; /** * Remove the object at the top of the Stack and return it. * * @method * @name warmsea.Stack.prototype.pop * @return {any} The object at the top of the Stack. */ Stack.prototype.pop = function() { if (this.length === 0) { if (this.allowEmptyPop) { return undefined; } else { w.error('The stack is empty'); } } var item = this._stack[this._top--]; if (this._top > 16 && this._top * 2 < this._stack.length) { this._stack = this._stack.slice(0, this._top + 1); } return item; }; /** * Return the object at the top of the Stack without removing it. * * @method * @name warmsea.Stack.prototype.peek * @return {any} The object at the top of the Stack. */ Stack.prototype.peek = function() { if (this.length === 0) { if (this.allowEmptyPop) { return undefined; } else { w.error('Popping an empty stack is not allowed'); } } return this._stack[this._top]; }; return Stack; })(); /** * Convert an arbitrary value to a boolean value. * @param {any} value * @return {boolean} */ w.bool = function(value) { var v = value; if (w.isObject(v) && '__bool__' in v) { return w.bool(w.isFunction(v.__bool__) ? v.__bool__() : v.__bool__); } else { return !!value; } }; /** * Convert a value to an Integer. * @param {any} value * @param {number} radix An integer between 2 and 32. * @return {number} */ w.i = function(value, radix) { var v = value; if (v === false) { return 0; } else if (v === true) { return 1; } else if (w.isObject(v) && '__int__' in v) { return w.i(w.isFunction(v.__int__) ? v.__int__() : v.__int__, radix); } else if (!radix && w.isString(v) && (v.indexOf('0x') === 0 || v.indexOf('0X') === 0)) { return parseInt(v, 16); } else if (!radix || radix === 10) { return Math.round(parseFloat(v)); } else { return parseInt(v, radix); } }; /** * Convert a value to a number. * @param {any} valueq * @return {number} */ w.f = function(value) { var v = value; if (v === false) { return 0.0; } else if (v === true) { return 1.0; } else if (w.isObject(v) && '__float__' in v) { return w.f(w.isFunction(v.__float__) ? v.__float__() : v.__float__); } else { return parseFloat(v); } }; /** * Convert a value to a String. * @param {any} value * @return {string} */ w.str = function(value) { var v = value; if (w.isObject(v) && '__str__' in v) { return w.str(w.isFunction(v.__str__) ? v.__str__() : v.__str__); } else { return String(value); } }; /** * Convert a value to an array. * @param {any} value * @return {Array} */ w.array = function(value) { if (w.isArray(value)) { return value; } else if (value === undefined || value === null) { return []; } else { return [value]; } }; /** * Test whether a value is an safe integer. *

* JavaScript has only one number type, that is 64-bit floating-point number. So we test whether the value is an * integer in that system. Which means 1.0 and 1 are both integers, but 9007199254740994 is not an integer because it * exceeds the max/min safe integer value a 64-bit floating-point number can present. * @param {any} value * @return {boolean} */ w.isInt = function(value) { // return w.isNumber(value) && value % 1 === 0 && // value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER; return w.isNumber(value) && value % 1 === 0 && value >= -9007199254740991 && value <= 9007199254740991; }; /** * Test whether a value is a plain object. *

* ATTENSION: I'm not pretty sure whether this function works everywhere. Can anyone help me? *

* A plain object is typically an object defined with {} or new Object. * * @param {any} value * @return {boolean} */ w.isPlainObject = function(value) { if (!w.isObject(value)) { return false; } try { if (!value.constructor.prototype.hasOwnProperty('isPrototypeOf')) { return false; } } catch (e) { return false; } return true; }; /** * Pad a string to a given length by adding leading characters. * @method * @param {number|string} value The value; it can be a number. * @param {int} length The wanted length. Defaults to 2. * @param {string} leading The leading character. Defaults to a space (' '). */ w.pad = function(value, length, leading) { value = w.str(value); length = w.max([0, length === undefined ? 0 : length, value.length]); leading = w.str(leading === undefined ? ' ' : leading); var a = new Array(Math.ceil((length - value.length) / leading.length) + 1); return a.join(leading).substring(0, length - value.length) + value; }; /** * Replace each conversion specifier of a format string with the string * representation of a specified object. * @method * @param {string|function} format the format string. * @param {...any|any[]|PlainObject} args Arguments one by one, or a list, * or a map. * @return {string} The formatted string. */ w.format = (function(w) { var f = function(format /* , args_array | args_map | arg1, arg2, ... */) { if (!arguments.length) { return ''; } format = func2str(arguments[0]); var params; if (w.isArray(arguments[1])) { params = arguments[1]; } else if (w.isPlainObject(arguments[1])) { params = arguments[1]; } else { params = Array.prototype.slice.call(arguments, 1); } var converter = new FormatConverter(params); var specifiers = /%(?:\((.*?)\))?([#|0|\-| |\+]+)?(\d+|\*)?(?:.(\d+|\*))?(?:[h|l|L])?(.)?/g; format = format.replace(specifiers, function() { return FormatConverter.prototype.convert.apply(converter, arguments); }); return format; }; /** * Convert a function into a (multi-line) string. * @method * @param {function} func * @return {string} */ var func2str = function(func) { if (w.isString(func)) { return func; } else if (!w.isFunction(func)) { return w.str(func); } var lines = w.str(func).split(/\r?\n/); var i = 0, j = 0, len = lines.length; var startIndex, endIndex; var endTag, options = {}; var dent = null; // Find the start line. while (i < len) { var start = lines[i++].match(/^[ \t]*\/\*\*?!?<<<(\w+)(?:;(.+)?)?$/); if (!start) { continue; } startIndex = i; endTag = start[1]; var optionTags = (start[2] || '').split(/[ \t]*,[ \t]*/); for (j = 0; j < optionTags.length; ++j) { var tag = optionTags[j].toLowerCase().split(/-(.*)/); if (tag[0]) { options[tag[0]] = tag[1] || true; } } break; } if (!startIndex) { w.error('Format failed: start line missing'); } // The default WS option is "OUTDENT". options.ws = options.ws || 'outdent'; var result = []; // Find the end line. while (i < len) { var line = lines[i]; if (line.match(new RegExp('^[ \\t]*' + endTag + '\\*\\/'))) { endIndex = i; break; } i++; // Count the dent here. if (options.ws === 'outdent') { var match = line.match(/^([ \t]*).*$/); var cur = match[1]; if (dent === null) { dent = cur; } else { j = 0; while (j < dent.length && j < cur.length && dent[j] === cur[j]) { j++; } if (dent.length > j) { dent = dent.substring(0, j); } } } result.push(line); } if (!endIndex) { w.error('Format failed: end line missing'); } var wsFunc; switch (options.ws) { case 'outdent': wsFunc = function(s) { return s.substring(dent.length); }; break; case 'keep': wsFunc = w.identity; break; case 'trim': wsFunc = function(s) { return s.trim(); }; break; default: w.error('Format failed: Unsupported WS option'); } for (i = 0, len = result.length; i < len; ++i) { result[i] = wsFunc(result[i]); } return result.join('\n'); }; /** * Converter for warmsea.format() function. * @class * @param {string[]} params */ var FormatConverter = function(params) { this.params = params || []; this.index = 0; }; /** * Convert a specifier to wanted string. * @method * @param {string} specifier * @param {string} key * @param {string} flags * @param {string} width * @param {string} precision * @param {string} type * @param {string} position */ FormatConverter.prototype.convert = function(specifier, key, flags, width, precision, type, position) { if (type === undefined) { w.error('format incomplete specifier "%s" in position (%d).', specifier, position); } if (!this.convertors.hasOwnProperty(type)) { w.error('format unsupported type "%s" in position (%d)', type, position); } var next; if (width === '*') { next = this.index++; if (!this.params.hasOwnProperty(next)) { w.error('format key error "%s" in position (%d).', key, position); } width = this.params[next]; } width = width || 0; if (precision === '*') { next = this.index++; if (!this.params.hasOwnProperty(next)) { w.error('format key error "%s" in position (%d).', key, position); } precision = this.params[next]; } if (precision === undefined) { precision = 6; } if (type === '%' || type === '/') { return type; } key = key === undefined ? this.index++ : key; if (!this.params.hasOwnProperty(key)) { w.error('format key error "%s" in position (%d).', key, position); } var value = this.params[key]; flags = flags || ''; flags = { '#': flags.indexOf('#') >= 0, '0': flags.indexOf('0') >= 0 && flags.indexOf('-') < 0, '-': flags.indexOf('-') >= 0, ' ': flags.indexOf(' ') >= 0 && flags.indexOf('+') < 0, '+': flags.indexOf('+') >= 0 }; return this.convertors[type](value, flags, width, precision); }; /** * The converters of each type. */ FormatConverter.prototype.convertors = { 'd': function(value, flags, width /*, precision */) { return FormatConverter.prototype.convertors.ic('d', value, flags, width); }, 'o': function(value, flags, width /*, precision */) { return FormatConverter.prototype.convertors.ic('o', value, flags, width); }, 'x': function(value, flags, width /*, precision */) { return FormatConverter.prototype.convertors.ic('x', value, flags, width); }, 'X': function(value, flags, width /*, precision */) { return FormatConverter.prototype.convertors.ic('X', value, flags, width); }, 'f': function(value, flags, width, precision) { value = w.f(value); var sign = value < 0 ? '-' : (flags['+'] ? '+' : flags[' '] ? ' ' : ''); value = Math.abs(value).toFixed(precision); if (flags['#'] && precision === 0) { value += '.'; } var gap = w.max([0, width - value.length - sign.length]); if (flags['-']) { return sign + value + w.pad('', gap, ' '); } else if (flags['0']) { return sign + w.pad('', gap, '0') + value; } else { return w.pad('', gap, ' ') + sign + value; } }, 's': function(value, flags, width /*, precision */) { value = w.str(value); var gap = w.max([0, width - value.length]); if (flags['-']) { return value + w.pad('', gap, ' '); } else { return w.pad('', gap, ' ') + value; } }, '%': function() { this.index--; return '%'; }, '/': function() { this.index--; return '/'; }, 'ic': function(type, value, flags, width) { value = w.i(value); var sign = value < 0 ? '-' : (flags['+'] ? '+' : flags[' '] ? ' ' : ''); var prefix = flags['#'] ? { 'd': '', 'o': '0', 'x': '0x', 'X': '0X' }[type] : ''; value = Math.abs(value).toString({ 'd': 10, 'o': 8, 'x': 16, 'X': 16 }[type]); var gap = w.max([0, width - prefix.length - value.length - sign.length]); var result = ''; if (flags['-']) { result = sign + prefix + value + w.pad('', gap, ' '); } else if (flags['0']) { result = sign + prefix + w.pad('', gap, '0') + value; } else { result = w.pad('', gap, ' ') + sign + prefix + value; } if (type === 'X') { result = result.toUpperCase(); } return result; } }; return f; })(w); /** * Create a debugger function with a name. * @method * @param {string} name * @return {function} */ w.debug = (function() { /** * Hold all the debuggers. * @type {object} */ var debuggers = {}; /** * Hold the enable status for all debuggers. * @type {object} */ var enableStatus = {}; /** * Debug modes. * @type {string[]} */ var modes = []; /** * RegExp matchers for debug modes. * @type {object} */ var modeMatchers = {}; /** * Themes for styled logging. * @type {object} */ var themes = { 'tomorrow': [ 'color:#8e908c', // The first style is for message. 'color:#f5871f', // Others for debugger name and time. 'color:#8959a8', 'color:#4271ae', 'color:#718c00', 'color:#eab700', 'color:#c82829' ] }; /** * Styles for styled logging. * @type {Array} */ var styles = themes.tomorrow; /** * Current color position. * @type {number} */ var pStyle = 0; /** * Detect Chrome browser. * Enable color for Chrome browser. * @type {boolean} */ var isChrome = w.global.navigator && w.global.navigator.userAgent.toLowerCase().indexOf('chrome') !== -1; /** * Create a debugger function with a name. * @param {string} name * @return {function} */ var debug = function(name) { if (debuggers[name]) { return debuggers[name]; } var dbg = function(fmt) { // if this debugger is not enabled, do nothing. // Check it every time so we can enable or disable a debugger at runtime. // It won't be slow. if (!enableStatus[this._name]) { return; } fmt = coerce.apply(this, arguments); var curr = +new Date(); // IE8 don't have `Date.now()` var ms = curr - (this._timestamp || curr); this._timestamp = curr; if (isChrome) { fmt = '%c' + name + '%c ' + fmt + ' %c+' + humanize(ms); w.global.console.log(fmt, this._style, this._msgStyle, this._style); } else { fmt = name + ' ' + fmt + ' +' + humanize(ms); if (w.global.console && w.global.console.log) { w.global.console.log(fmt); } // Any one want to alert debug message? I think it's annoying. } }; dbg._name = name; dbg._msgStyle = styles[0]; dbg._style = style(); enableStatus[name] = debug.enabled(name); debuggers[name] = dbg.bind(dbg); return debuggers[name]; }; /** * Coerce `fmt`. */ var coerce = function(fmt) { if (fmt instanceof Error) { return fmt.stack || fmt.message; } else { return w.format.apply(this, arguments); } }; /** * Get a style for styled logging. */ var style = function() { return styles[pStyle++ % (styles.length - 1) + 1]; }; /** * Humanize the given milliseconds. * @param {number} ms number of milliseconds. * @return {string} A human readable representation. */ var humanize = function(ms) { var sec = 1000; var min = 60 * 1000; var hour = 60 * min; if (ms >= hour) { return (ms / hour).toFixed(1) + 'h'; } if (ms >= min) { return (ms / min).toFixed(1) + 'm'; } if (ms >= sec) { return (ms / sec | 0) + 's'; } return ms + 'ms'; }; /** * Enable debugger(s) with the specified name. * @param {string} name */ debug.enable = function(name) { w.each((name || '').split(/[\s,]+/), function(mode) { if (mode[0] !== '+' && mode[0] !== '-') { mode = '+' + mode; } if (!modeMatchers[mode]) { modeMatchers[mode] = new RegExp('^' + mode.substring(1).replace('*', '.*') + '$'); } var newModes = []; for (var i = 0, len = modes.length; i < len; ++i) { var m = modes[i]; if ((m === mode) || (m.indexOf('*') < 0 && m.match(modeMatchers[mode]))) { continue; } newModes.push(m); } modes = newModes; modes.push(mode); }); refreshEnableStatus(); }; /** * Enable debugger(s) with the specified name. * Call this function without an argument to disable all debug modes. * @param {?string} name */ debug.disable = function(name) { if (name === undefined) { modes = []; modeMatchers = {}; } else { var enableName = ''; w.each(name.split(/[\s,]+/), function(n) { if (n[0] === '+' || n[1] === '-') { n = n.substring(1); } enableName += ', -' + n; }); enableName = enableName.substring(2); debug.enable(enableName); } refreshEnableStatus(); }; /** * Return true if the given mode name is enabled, false otherwise. * @param {string} name * @return {boolean} */ debug.enabled = function(name) { for (var i = modes.length - 1; i >= 0; --i) { if (name.match(modeMatchers[modes[i]])) { return modes[i][0] === '+'; } } return false; }; /** * Reset the enable status of all debuggers. */ var refreshEnableStatus = function() { for (var name in debuggers) { enableStatus[name] = debug.enabled(name); } }; return debug; })(); /** * Deep copy a value (to a target). * @param {any} value * @param {?any} target * @return {any} the copied value. */ w.deepcopy = function(value, target) { if (w.isArray(value)) { target = value.slice(); } else if (w.isPlainObject(value)) { target = target || {}; for (var i in value) { if (value.hasOwnProperty(i)) { target[i] = w.deepcopy(value[i], target[i]); } } } else { target = value; } return target; }; /** * Hide specified properties of an object. * @param {Object} obj the object * @param {Array} props names of the properties */ w.hideProperties = function(obj, props) { if (!w.isFunction(Object.defineProperty)) { return; } w.each(props, function(key) { Object.defineProperty(obj, key, { enumerable: false, writable: true, configurable: true }); }); }; /** * Add a memoized property to an object. * @param {Object} obj the object * @param {String} name name of the property * @param {Function} getter getter function of the property * @param {Boolean} enumerable whether the property if enumerable */ w.memoizedProperty = function(obj, name, getter, enumerable) { if (!w.isFunction(Object.defineProperty)) { obj[name] = getter(); } var cachedValue = null; Object.defineProperty(obj, name, { get: function() { if (cachedValue === null) { cachedValue = getter(); } return cachedValue; }, enumerable: enumerable }); }; /** * Return a random integer in the range of [start, end). * If end is not passed, the range will be [0, start). * @param {number} start * @param {number} end * @return {number} */ w.randomInt = function(start, end) { if (start === undefined && end === undefined) { return 0; } else if (end === undefined) { end = start; start = 0; } start = Math.ceil(start); end = Math.ceil(end); if (start < end) { return start + Math.floor((end - start) * Math.random()); } else if (start > end) { return start - Math.floor((start - end) * Math.random()); } else { return NaN; } }; /** * Choose a random floating-point number in the range of [start, end). *

* If end is not passed, the range will be [0, start). * * @param {number} start start of the range. * @param {number} end end of the range. * @return {number} A random number in the range. */ w.randomFloat = function(start, end) { if (start === undefined && end === undefined) { return Math.random(); } else if (end === undefined) { return Math.random() * start; } else if (start < end) { return start + Math.random() * (end - start); } else if (start > end) { return start - Math.random() * (start - end); } else { return NaN; } }; /** * Return a random string. * @param {number} length Default to 8. * @param {string} allowedChars Default to "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789" */ w.randomString = function(length, allowedChars) { if (length === undefined) { length = 8; } else { length = w.i(length); length = w.max([length, 0]); } if (allowedChars === undefined) { allowedChars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'; } else { allowedChars = w.str(allowedChars); } var res = ''; var len = allowedChars.length; if (!len) { return ''; } for (var i = 0; i < length; i++) { res += allowedChars[Math.floor(Math.random() * len)]; } return res; }; if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = w; } else { exports.warmsea = w; } } else if ( typeof define === 'function' && define.amd) { define(function() { return w; }); } else { global.warmsea = w; } var compatibleKeys = ['VERSION', 'noConflict']; var overwritten = []; _.each(underscoreKeys, function(k) { if (_[k] !== w[k] && !w.inArray(compatibleKeys, k)) { overwritten.push(k); } }); if (overwritten.length) { var overwritenMsg = 'The following functions of WarmseaJS ' + 'is not fully compatible with underscore.js: '; overwritenMsg += '"' + overwritten.join('", "') + '"'; if (global.console) { var warn = global.console.warn || global.console.log || w.noop; warn.call(global.console, overwritenMsg); } } })(this, typeof exports !== 'undefined' ? exports._ : _);