5506 lines
562 KiB
JavaScript
5506 lines
562 KiB
JavaScript
|
// Underscore.js 1.6.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._;
|
|||
|
|
|||
|
// Establish the object that gets returned to break out of a loop iteration.
|
|||
|
var breaker = {};
|
|||
|
|
|||
|
// Save bytes in the minified (but not gzipped) version:
|
|||
|
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
|||
|
|
|||
|
// Create quick reference variables for speed access to core prototypes.
|
|||
|
var
|
|||
|
push = ArrayProto.push,
|
|||
|
slice = ArrayProto.slice,
|
|||
|
concat = ArrayProto.concat,
|
|||
|
toString = ObjProto.toString,
|
|||
|
hasOwnProperty = ObjProto.hasOwnProperty;
|
|||
|
|
|||
|
// All **ECMAScript 5** native function implementations that we hope to use
|
|||
|
// are declared here.
|
|||
|
var
|
|||
|
nativeForEach = ArrayProto.forEach,
|
|||
|
nativeMap = ArrayProto.map,
|
|||
|
nativeReduce = ArrayProto.reduce,
|
|||
|
nativeReduceRight = ArrayProto.reduceRight,
|
|||
|
nativeFilter = ArrayProto.filter,
|
|||
|
nativeEvery = ArrayProto.every,
|
|||
|
nativeSome = ArrayProto.some,
|
|||
|
nativeIndexOf = ArrayProto.indexOf,
|
|||
|
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
|||
|
nativeIsArray = Array.isArray,
|
|||
|
nativeKeys = Object.keys,
|
|||
|
nativeBind = FuncProto.bind;
|
|||
|
|
|||
|
// Create a safe reference to the Underscore object for use below.
|
|||
|
var _ = function(obj) {
|
|||
|
if (obj instanceof _) return obj;
|
|||
|
if (!(this instanceof _)) return new _(obj);
|
|||
|
this._wrapped = obj;
|
|||
|
};
|
|||
|
|
|||
|
// Export the Underscore object for **Node.js**, with
|
|||
|
// backwards-compatibility for the old `require()` API. If we're in
|
|||
|
// the browser, add `_` as a global object via a string identifier,
|
|||
|
// for Closure Compiler "advanced" mode.
|
|||
|
if (typeof exports !== 'undefined') {
|
|||
|
if (typeof module !== 'undefined' && module.exports) {
|
|||
|
exports = module.exports = _;
|
|||
|
}
|
|||
|
exports._ = _;
|
|||
|
} else {
|
|||
|
root._ = _;
|
|||
|
}
|
|||
|
|
|||
|
// Current version.
|
|||
|
_.VERSION = '1.6.0';
|
|||
|
|
|||
|
// Collection Functions
|
|||
|
// --------------------
|
|||
|
|
|||
|
// The cornerstone, an `each` implementation, aka `forEach`.
|
|||
|
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
|||
|
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
|||
|
var each = _.each = _.forEach = function(obj, iterator, context) {
|
|||
|
if (obj == null) return obj;
|
|||
|
if (nativeForEach && obj.forEach === nativeForEach) {
|
|||
|
obj.forEach(iterator, context);
|
|||
|
} else if (obj.length === +obj.length) {
|
|||
|
for (var i = 0, length = obj.length; i < length; i++) {
|
|||
|
if (iterator.call(context, obj[i], i, obj) === breaker) return;
|
|||
|
}
|
|||
|
} else {
|
|||
|
var keys = _.keys(obj);
|
|||
|
for (var i = 0, length = keys.length; i < length; i++) {
|
|||
|
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
|
|||
|
}
|
|||
|
}
|
|||
|
return obj;
|
|||
|
};
|
|||
|
|
|||
|
// Return the results of applying the iterator to each element.
|
|||
|
// Delegates to **ECMAScript 5**'s native `map` if available.
|
|||
|
_.map = _.collect = function(obj, iterator, context) {
|
|||
|
var results = [];
|
|||
|
if (obj == null) return results;
|
|||
|
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
results.push(iterator.call(context, value, index, list));
|
|||
|
});
|
|||
|
return results;
|
|||
|
};
|
|||
|
|
|||
|
var reduceError = 'Reduce of empty array with no initial value';
|
|||
|
|
|||
|
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
|||
|
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
|||
|
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
|||
|
var initial = arguments.length > 2;
|
|||
|
if (obj == null) obj = [];
|
|||
|
if (nativeReduce && obj.reduce === nativeReduce) {
|
|||
|
if (context) iterator = _.bind(iterator, context);
|
|||
|
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
|||
|
}
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
if (!initial) {
|
|||
|
memo = value;
|
|||
|
initial = true;
|
|||
|
} else {
|
|||
|
memo = iterator.call(context, memo, value, index, list);
|
|||
|
}
|
|||
|
});
|
|||
|
if (!initial) throw new TypeError(reduceError);
|
|||
|
return memo;
|
|||
|
};
|
|||
|
|
|||
|
// The right-associative version of reduce, also known as `foldr`.
|
|||
|
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
|||
|
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
|||
|
var initial = arguments.length > 2;
|
|||
|
if (obj == null) obj = [];
|
|||
|
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
|||
|
if (context) iterator = _.bind(iterator, context);
|
|||
|
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
|||
|
}
|
|||
|
var length = obj.length;
|
|||
|
if (length !== +length) {
|
|||
|
var keys = _.keys(obj);
|
|||
|
length = keys.length;
|
|||
|
}
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
index = keys ? keys[--length] : --length;
|
|||
|
if (!initial) {
|
|||
|
memo = obj[index];
|
|||
|
initial = true;
|
|||
|
} else {
|
|||
|
memo = iterator.call(context, memo, obj[index], index, list);
|
|||
|
}
|
|||
|
});
|
|||
|
if (!initial) throw new TypeError(reduceError);
|
|||
|
return memo;
|
|||
|
};
|
|||
|
|
|||
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
|||
|
_.find = _.detect = function(obj, predicate, context) {
|
|||
|
var result;
|
|||
|
any(obj, function(value, index, list) {
|
|||
|
if (predicate.call(context, value, index, list)) {
|
|||
|
result = value;
|
|||
|
return true;
|
|||
|
}
|
|||
|
});
|
|||
|
return result;
|
|||
|
};
|
|||
|
|
|||
|
// Return all the elements that pass a truth test.
|
|||
|
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
|||
|
// Aliased as `select`.
|
|||
|
_.filter = _.select = function(obj, predicate, context) {
|
|||
|
var results = [];
|
|||
|
if (obj == null) return results;
|
|||
|
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
if (predicate.call(context, 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, function(value, index, list) {
|
|||
|
return !predicate.call(context, value, index, list);
|
|||
|
}, context);
|
|||
|
};
|
|||
|
|
|||
|
// Determine whether all of the elements match a truth test.
|
|||
|
// Delegates to **ECMAScript 5**'s native `every` if available.
|
|||
|
// Aliased as `all`.
|
|||
|
_.every = _.all = function(obj, predicate, context) {
|
|||
|
predicate || (predicate = _.identity);
|
|||
|
var result = true;
|
|||
|
if (obj == null) return result;
|
|||
|
if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
if (!(result = result && predicate.call(context, value, index, list))) return breaker;
|
|||
|
});
|
|||
|
return !!result;
|
|||
|
};
|
|||
|
|
|||
|
// Determine if at least one element in the object matches a truth test.
|
|||
|
// Delegates to **ECMAScript 5**'s native `some` if available.
|
|||
|
// Aliased as `any`.
|
|||
|
var any = _.some = _.any = function(obj, predicate, context) {
|
|||
|
predicate || (predicate = _.identity);
|
|||
|
var result = false;
|
|||
|
if (obj == null) return result;
|
|||
|
if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
if (result || (result = predicate.call(context, value, index, list))) return breaker;
|
|||
|
});
|
|||
|
return !!result;
|
|||
|
};
|
|||
|
|
|||
|
// Determine if the array or object contains a given value (using `===`).
|
|||
|
// Aliased as `include`.
|
|||
|
_.contains = _.include = function(obj, target) {
|
|||
|
if (obj == null) return false;
|
|||
|
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
|||
|
return any(obj, function(value) {
|
|||
|
return value === target;
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
// Invoke a method (with arguments) on every item in a collection.
|
|||
|
_.invoke = function(obj, method) {
|
|||
|
var args = slice.call(arguments, 2);
|
|||
|
var isFunc = _.isFunction(method);
|
|||
|
return _.map(obj, function(value) {
|
|||
|
return (isFunc ? method : value[method]).apply(value, args);
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
// Convenience version of a common use case of `map`: fetching a property.
|
|||
|
_.pluck = function(obj, key) {
|
|||
|
return _.map(obj, _.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).
|
|||
|
// Can't optimize arrays of integers longer than 65,535 elements.
|
|||
|
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
|
|||
|
_.max = function(obj, iterator, context) {
|
|||
|
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
|
|||
|
return Math.max.apply(Math, obj);
|
|||
|
}
|
|||
|
var result = -Infinity, lastComputed = -Infinity;
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
|||
|
if (computed > lastComputed) {
|
|||
|
result = value;
|
|||
|
lastComputed = computed;
|
|||
|
}
|
|||
|
});
|
|||
|
return result;
|
|||
|
};
|
|||
|
|
|||
|
// Return the minimum element (or element-based computation).
|
|||
|
_.min = function(obj, iterator, context) {
|
|||
|
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
|
|||
|
return Math.min.apply(Math, obj);
|
|||
|
}
|
|||
|
var result = Infinity, lastComputed = Infinity;
|
|||
|
each(obj, function(value, index, list) {
|
|||
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
|||
|
if (computed < lastComputed) {
|
|||
|
result = value;
|
|||
|
lastComputed = computed;
|
|||
|
}
|
|||
|
});
|
|||
|
return result;
|
|||
|
};
|
|||
|
|
|||
|
// Shuffle an array, using the modern version of the
|
|||
|
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
|
|||
|
_.shuffle = function(obj) {
|
|||
|
var rand;
|
|||
|
var index = 0;
|
|||
|
var shuffled = [];
|
|||
|
each(obj, function(value) {
|
|||
|
rand = _.random(index++);
|
|||
|
shuffled[index - 1] = shuffled[rand];
|
|||
|
shuffled[rand] = value;
|
|||
|
});
|
|||
|
return shuffled;
|
|||
|
};
|
|||
|
|
|||
|
// Sample **n** random values from 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));
|
|||
|
};
|
|||
|
|
|||
|
// An internal function to generate lookup iterators.
|
|||
|
var lookupIterator = function(value) {
|
|||
|
if (value == null) return _.identity;
|
|||
|
if (_.isFunction(value)) return value;
|
|||
|
return _.property(value);
|
|||
|
};
|
|||
|
|
|||
|
// Sort the object's values by a criterion produced by an iterator.
|
|||
|
_.sortBy = function(obj, iterator, context) {
|
|||
|
iterator = lookupIterator(iterator);
|
|||
|
return _.pluck(_.map(obj, function(value, index, list) {
|
|||
|
return {
|
|||
|
value: value,
|
|||
|
index: index,
|
|||
|
criteria: iterator.call(context, value, index, list)
|
|||
|
};
|
|||
|
}).sort(function(left, right) {
|
|||
|
var a = left.criteria;
|
|||
|
var b = right.criteria;
|
|||
|
if (a !== b) {
|
|||
|
if (a > b || a === void 0) return 1;
|
|||
|
if (a < b || b === void 0) return -1;
|
|||
|
}
|
|||
|
return left.index - right.index;
|
|||
|
}), 'value');
|
|||
|
};
|
|||
|
|
|||
|
// An internal function used for aggregate "group by" operations.
|
|||
|
var group = function(behavior) {
|
|||
|
return function(obj, iterator, context) {
|
|||
|
var result = {};
|
|||
|
iterator = lookupIterator(iterator);
|
|||
|
each(obj, function(value, index) {
|
|||
|
var key = iterator.call(context, value, index, obj);
|
|||
|
behavior(result, key, value);
|
|||
|
});
|
|||
|
return result;
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
// Groups the object's values by a criterion. Pass either a string attribute
|
|||
|
// to group by, or a function that returns the criterion.
|
|||
|
_.groupBy = group(function(result, key, value) {
|
|||
|
_.has(result, key) ? result[key].push(value) : 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, key, value) {
|
|||
|
result[key] = value;
|
|||
|
});
|
|||
|
|
|||
|
// Counts instances of an object that group by a certain criterion. Pass
|
|||
|
// either a string attribute to count by, or a function that returns the
|
|||
|
// criterion.
|
|||
|
_.countBy = group(function(result, key) {
|
|||
|
_.has(result, key) ? result[key]++ : result[key] = 1;
|
|||
|
});
|
|||
|
|
|||
|
// Use a comparator function to figure out the smallest index at which
|
|||
|
// an object should be inserted so as to maintain order. Uses binary search.
|
|||
|
_.sortedIndex = function(array, obj, iterator, context) {
|
|||
|
iterator = lookupIterator(iterator);
|
|||
|
var value = iterator.call(context, obj);
|
|||
|
var low = 0, high = array.length;
|
|||
|
while (low < high) {
|
|||
|
var mid = (low + high) >>> 1;
|
|||
|
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
|
|||
|
}
|
|||
|
return low;
|
|||
|
};
|
|||
|
|
|||
|
// Safely create a real, live array from anything iterable.
|
|||
|
_.toArray = function(obj) {
|
|||
|
if (!obj) return [];
|
|||
|
if (_.isArray(obj)) return slice.call(obj);
|
|||
|
if (obj.length === +obj.length) return _.map(obj, _.identity);
|
|||
|
return _.values(obj);
|
|||
|
};
|
|||
|
|
|||
|
// Return the number of elements in an object.
|
|||
|
_.size = function(obj) {
|
|||
|
if (obj == null) return 0;
|
|||
|
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
|
|||
|
};
|
|||
|
|
|||
|
// Array Functions
|
|||
|
// ---------------
|
|||
|
|
|||
|
// Get the first element of an array. Passing **n** will return the first N
|
|||
|
// values in the array. Aliased as `head` and `take`. The **guard** check
|
|||
|
// allows it to work with `_.map`.
|
|||
|
_.first = _.head = _.take = function(array, n, guard) {
|
|||
|
if (array == null) return void 0;
|
|||
|
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, 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, output) {
|
|||
|
if (shallow && _.every(input, _.isArray)) {
|
|||
|
return concat.apply(output, input);
|
|||
|
}
|
|||
|
each(input, function(value) {
|
|||
|
if (_.isArray(value) || _.isArguments(value)) {
|
|||
|
shallow ? push.apply(output, value) : flatten(value, shallow, output);
|
|||
|
} else {
|
|||
|
output.push(value);
|
|||
|
}
|
|||
|
});
|
|||
|
return output;
|
|||
|
};
|
|||
|
|
|||
|
// Flatten out an array, either recursively (by default), or just one level.
|
|||
|
_.flatten = function(array, shallow) {
|
|||
|
return flatten(array, shallow, []);
|
|||
|
};
|
|||
|
|
|||
|
// Return a version of the array that does not contain the specified value(s).
|
|||
|
_.without = function(array) {
|
|||
|
return _.difference(array, slice.call(arguments, 1));
|
|||
|
};
|
|||
|
|
|||
|
// Split an array into two arrays: one whose elements all satisfy the given
|
|||
|
// predicate, and one whose elements all do not satisfy the predicate.
|
|||
|
_.partition = function(array, predicate) {
|
|||
|
var pass = [], fail = [];
|
|||
|
each(array, function(elem) {
|
|||
|
(predicate(elem) ? pass : fail).push(elem);
|
|||
|
});
|
|||
|
return [pass, fail];
|
|||
|
};
|
|||
|
|
|||
|
// Produce a duplicate-free version of the array. If the array has already
|
|||
|
// been sorted, you have the option of using a faster algorithm.
|
|||
|
// Aliased as `unique`.
|
|||
|
_.uniq = _.unique = function(array, isSorted, iterator, context) {
|
|||
|
if (_.isFunction(isSorted)) {
|
|||
|
context = iterator;
|
|||
|
iterator = isSorted;
|
|||
|
isSorted = false;
|
|||
|
}
|
|||
|
var initial = iterator ? _.map(array, iterator, context) : array;
|
|||
|
var results = [];
|
|||
|
var seen = [];
|
|||
|
each(initial, function(value, index) {
|
|||
|
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
|
|||
|
seen.push(value);
|
|||
|
results.push(array[index]);
|
|||
|
}
|
|||
|
});
|
|||
|
return results;
|
|||
|
};
|
|||
|
|
|||
|
// Produce an array that contains the union: each distinct element from all of
|
|||
|
// the passed-in arrays.
|
|||
|
_.union = function() {
|
|||
|
return _.uniq(_.flatten(arguments, true));
|
|||
|
};
|
|||
|
|
|||
|
// Produce an array that contains every item shared between all the
|
|||
|
// passed-in arrays.
|
|||
|
_.intersection = function(array) {
|
|||
|
var rest = slice.call(arguments, 1);
|
|||
|
return _.filter(_.uniq(array), function(item) {
|
|||
|
return _.every(rest, function(other) {
|
|||
|
return _.contains(other, item);
|
|||
|
});
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
// Take the difference between one array and a number of other arrays.
|
|||
|
// Only the elements present in just the first array will remain.
|
|||
|
_.difference = function(array) {
|
|||
|
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
|
|||
|
return _.filter(array, function(value){ return !_.contains(rest, value); });
|
|||
|
};
|
|||
|
|
|||
|
// Zip together multiple lists into a single array -- elements that share
|
|||
|
// an index go together.
|
|||
|
_.zip = function() {
|
|||
|
var length = _.max(_.pluck(arguments, 'length').concat(0));
|
|||
|
var results = new Array(length);
|
|||
|
for (var i = 0; i < length; i++) {
|
|||
|
results[i] = _.pluck(arguments, '' + i);
|
|||
|
}
|
|||
|
return results;
|
|||
|
};
|
|||
|
|
|||
|
// Converts lists into objects. Pass either a single array of `[key, value]`
|
|||
|
// pairs, or two parallel arrays of the same length -- one of keys, and one of
|
|||
|
// the corresponding values.
|
|||
|
_.object = function(list, values) {
|
|||
|
if (list == null) return {};
|
|||
|
var result = {};
|
|||
|
for (var i = 0, length = list.length; i < length; i++) {
|
|||
|
if (values) {
|
|||
|
result[list[i]] = values[i];
|
|||
|
} else {
|
|||
|
result[list[i][0]] = list[i][1];
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
};
|
|||
|
|
|||
|
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
|||
|
// we need this function. Return the position of the first occurrence of an
|
|||
|
// item in an array, or -1 if the item is not included in the array.
|
|||
|
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
|||
|
// If the array is large and already in sort order, pass `true`
|
|||
|
// for **isSorted** to use binary search.
|
|||
|
_.indexOf = function(array, item, isSorted) {
|
|||
|
if (array == null) return -1;
|
|||
|
var i = 0, length = array.length;
|
|||
|
if (isSorted) {
|
|||
|
if (typeof isSorted == 'number') {
|
|||
|
i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
|
|||
|
} else {
|
|||
|
i = _.sortedIndex(array, item);
|
|||
|
return array[i] === item ? i : -1;
|
|||
|
}
|
|||
|
}
|
|||
|
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
|
|||
|
for (; i < length; i++) if (array[i] === item) return i;
|
|||
|
return -1;
|
|||
|
};
|
|||
|
|
|||
|
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
|||
|
_.lastIndexOf = function(array, item, from) {
|
|||
|
if (array == null) return -1;
|
|||
|
var hasIndex = from != null;
|
|||
|
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
|
|||
|
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
|
|||
|
}
|
|||
|
var i = (hasIndex ? from : array.length);
|
|||
|
while (i--) if (array[i] === item) return i;
|
|||
|
return -1;
|
|||
|
};
|
|||
|
|
|||
|
// Generate an integer Array containing an arithmetic progression. A port of
|
|||
|
// the native Python `range()` function. See
|
|||
|
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
|||
|
_.range = function(start, stop, step) {
|
|||
|
if (arguments.length <= 1) {
|
|||
|
stop = start || 0;
|
|||
|
start = 0;
|
|||
|
}
|
|||
|
step = arguments[2] || 1;
|
|||
|
|
|||
|
var length = Math.max(Math.ceil((stop - start) / step), 0);
|
|||
|
var idx = 0;
|
|||
|
var range = new Array(length);
|
|||
|
|
|||
|
while(idx < length) {
|
|||
|
range[idx++] = start;
|
|||
|
start += step;
|
|||
|
}
|
|||
|
|
|||
|
return range;
|
|||
|
};
|
|||
|
|
|||
|
// Function (ahem) Functions
|
|||
|
// ------------------
|
|||
|
|
|||
|
// Reusable constructor function for prototype setting.
|
|||
|
var ctor = function(){};
|
|||
|
|
|||
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
|||
|
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
|||
|
// available.
|
|||
|
_.bind = function(func, context) {
|
|||
|
var args, bound;
|
|||
|
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
|||
|
if (!_.isFunction(func)) throw new TypeError;
|
|||
|
args = slice.call(arguments, 2);
|
|||
|
return bound = function() {
|
|||
|
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
|||
|
ctor.prototype = func.prototype;
|
|||
|
var self = new ctor;
|
|||
|
ctor.prototype = null;
|
|||
|
var result = func.apply(self, args.concat(slice.call(arguments)));
|
|||
|
if (Object(result) === result) return result;
|
|||
|
return self;
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
// Partially apply a function by creating a version that has had some of its
|
|||
|
// arguments pre-filled, without changing its dynamic `this` context. _ 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 funcs = slice.call(arguments, 1);
|
|||
|
if (funcs.length === 0) throw new Error('bindAll must be passed function names');
|
|||
|
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
|||
|
return obj;
|
|||
|
};
|
|||
|
|
|||
|
// Memoize an expensive function by storing its results.
|
|||
|
_.memoize = function(func, hasher) {
|
|||
|
var memo = {};
|
|||
|
hasher || (hasher = _.identity);
|
|||
|
return function() {
|
|||
|
var key = hasher.apply(this, arguments);
|
|||
|
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
// Delays a function for the given number of milliseconds, and then calls
|
|||
|
// it with the arguments supplied.
|
|||
|
_.delay = function(func, wait) {
|
|||
|
var args = slice.call(arguments, 2);
|
|||
|
return setTimeout(function(){ return func.apply(null, args); }, wait);
|
|||
|
};
|
|||
|
|
|||
|
// Defers a function, scheduling it to run after the current call stack has
|
|||
|
// cleared.
|
|||
|
_.defer = function(func) {
|
|||
|
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
|||
|
};
|
|||
|
|
|||
|
// Returns a function, that, when invoked, will only be triggered at most once
|
|||
|
// during a given window of time. Normally, the throttled function will run
|
|||
|
// as much as it can, without ever going more than once per `wait` duration;
|
|||
|
// but if you'd like to disable the execution on the leading edge, pass
|
|||
|
// `{leading: false}`. To disable execution on the trailing edge, ditto.
|
|||
|
_.throttle = function(func, wait, options) {
|
|||
|
var context, args, result;
|
|||
|
var timeout = null;
|
|||
|
var previous = 0;
|
|||
|
options || (options = {});
|
|||
|
var later = function() {
|
|||
|
previous = options.leading === false ? 0 : _.now();
|
|||
|
timeout = null;
|
|||
|
result = func.apply(context, args);
|
|||
|
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) {
|
|||
|
clearTimeout(timeout);
|
|||
|
timeout = null;
|
|||
|
previous = now;
|
|||
|
result = func.apply(context, args);
|
|||
|
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) {
|
|||
|
timeout = setTimeout(later, wait - last);
|
|||
|
} else {
|
|||
|
timeout = null;
|
|||
|
if (!immediate) {
|
|||
|
result = func.apply(context, args);
|
|||
|
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 a function that will be executed at most one time, no matter how
|
|||
|
// often you call it. Useful for lazy initialization.
|
|||
|
_.once = function(func) {
|
|||
|
var ran = false, memo;
|
|||
|
return function() {
|
|||
|
if (ran) return memo;
|
|||
|
ran = true;
|
|||
|
memo = func.apply(this, arguments);
|
|||
|
func = null;
|
|||
|
return memo;
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
// Returns the first function passed as an argument to the second,
|
|||
|
// allowing you to adjust arguments, run code before and after, and
|
|||
|
// conditionally execute the original function.
|
|||
|
_.wrap = function(func, wrapper) {
|
|||
|
return _.partial(wrapper, func);
|
|||
|
};
|
|||
|
|
|||
|
// Returns a function that is the composition of a list of functions, each
|
|||
|
// consuming the return value of the function that follows.
|
|||
|
_.compose = function() {
|
|||
|
var funcs = arguments;
|
|||
|
return function() {
|
|||
|
var args = arguments;
|
|||
|
for (var i = funcs.length - 1; i >= 0; i--) {
|
|||
|
args = [funcs[i].apply(this, args)];
|
|||
|
}
|
|||
|
return args[0];
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
// Returns a function that will only be executed after being called N times.
|
|||
|
_.after = function(times, func) {
|
|||
|
return function() {
|
|||
|
if (--times < 1) {
|
|||
|
return func.apply(this, arguments);
|
|||
|
}
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
// Object Functions
|
|||
|
// ----------------
|
|||
|
|
|||
|
// Retrieve the names of an object's properties.
|
|||
|
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
|||
|
_.keys = 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 = new Array(length);
|
|||
|
for (var i = 0; i < length; i++) {
|
|||
|
values[i] = obj[keys[i]];
|
|||
|
}
|
|||
|
return values;
|
|||
|
};
|
|||
|
|
|||
|
// Convert an object into a list of `[key, value]` pairs.
|
|||
|
_.pairs = function(obj) {
|
|||
|
var keys = _.keys(obj);
|
|||
|
var length = keys.length;
|
|||
|
var pairs = new Array(length);
|
|||
|
for (var i = 0; i < length; i++) {
|
|||
|
pairs[i] = [keys[i], obj[keys[i]]];
|
|||
|
}
|
|||
|
return pairs;
|
|||
|
};
|
|||
|
|
|||
|
// Invert the keys and values of an object. The values must be serializable.
|
|||
|
_.invert = function(obj) {
|
|||
|
var result = {};
|
|||
|
var keys = _.keys(obj);
|
|||
|
for (var i = 0, length = keys.length; i < length; i++) {
|
|||
|
result[obj[keys[i]]] = keys[i];
|
|||
|
}
|
|||
|
return result;
|
|||
|
};
|
|||
|
|
|||
|
// Return a sorted list of the function names available on the object.
|
|||
|
// Aliased as `methods`
|
|||
|
_.functions = _.methods = function(obj) {
|
|||
|
var names = [];
|
|||
|
for (var key in obj) {
|
|||
|
if (_.isFunction(obj[key])) names.push(key);
|
|||
|
}
|
|||
|
return names.sort();
|
|||
|
};
|
|||
|
|
|||
|
// Extend a given object with all the properties in passed-in object(s).
|
|||
|
_.extend = function(obj) {
|
|||
|
each(slice.call(arguments, 1), function(source) {
|
|||
|
if (source) {
|
|||
|
for (var prop in source) {
|
|||
|
obj[prop] = source[prop];
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
return obj;
|
|||
|
};
|
|||
|
|
|||
|
// Return a copy of the object only containing the whitelisted properties.
|
|||
|
_.pick = function(obj) {
|
|||
|
var copy = {};
|
|||
|
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
|||
|
each(keys, function(key) {
|
|||
|
if (key in obj) copy[key] = obj[key];
|
|||
|
});
|
|||
|
return copy;
|
|||
|
};
|
|||
|
|
|||
|
// Return a copy of the object without the blacklisted properties.
|
|||
|
_.omit = function(obj) {
|
|||
|
var copy = {};
|
|||
|
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
|||
|
for (var key in obj) {
|
|||
|
if (!_.contains(keys, key)) copy[key] = obj[key];
|
|||
|
}
|
|||
|
return copy;
|
|||
|
};
|
|||
|
|
|||
|
// Fill in a given object with default properties.
|
|||
|
_.defaults = function(obj) {
|
|||
|
each(slice.call(arguments, 1), function(source) {
|
|||
|
if (source) {
|
|||
|
for (var prop in source) {
|
|||
|
if (obj[prop] === void 0) obj[prop] = source[prop];
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
return obj;
|
|||
|
};
|
|||
|
|
|||
|
// Create a (shallow-cloned) duplicate of an object.
|
|||
|
_.clone = function(obj) {
|
|||
|
if (!_.isObject(obj)) return obj;
|
|||
|
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
|||
|
};
|
|||
|
|
|||
|
// Invokes interceptor with the obj, and then returns obj.
|
|||
|
// The primary purpose of this method is to "tap into" a method chain, in
|
|||
|
// order to perform operations on intermediate results within the chain.
|
|||
|
_.tap = function(obj, interceptor) {
|
|||
|
interceptor(obj);
|
|||
|
return obj;
|
|||
|
};
|
|||
|
|
|||
|
// Internal recursive comparison function for `isEqual`.
|
|||
|
var eq = function(a, b, aStack, bStack) {
|
|||
|
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
|||
|
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
|||
|
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
|||
|
// A strict comparison is necessary because `null == undefined`.
|
|||
|
if (a == null || b == null) return a === b;
|
|||
|
// Unwrap any wrapped objects.
|
|||
|
if (a instanceof _) a = a._wrapped;
|
|||
|
if (b instanceof _) b = b._wrapped;
|
|||
|
// Compare `[[Class]]` names.
|
|||
|
var className = toString.call(a);
|
|||
|
if (className != toString.call(b)) return false;
|
|||
|
switch (className) {
|
|||
|
// Strings, numbers, dates, and booleans are compared by value.
|
|||
|
case '[object String]':
|
|||
|
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
|||
|
// equivalent to `new String("5")`.
|
|||
|
return a == String(b);
|
|||
|
case '[object Number]':
|
|||
|
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
|||
|
// other numeric values.
|
|||
|
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
|||
|
case '[object Date]':
|
|||
|
case '[object Boolean]':
|
|||
|
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
|||
|
// millisecond representations. Note that invalid dates with millisecond representations
|
|||
|
// of `NaN` are not equivalent.
|
|||
|
return +a == +b;
|
|||
|
// RegExps are compared by their source patterns and flags.
|
|||
|
case '[object RegExp]':
|
|||
|
return a.source == b.source &&
|
|||
|
a.global == b.global &&
|
|||
|
a.multiline == b.multiline &&
|
|||
|
a.ignoreCase == b.ignoreCase;
|
|||
|
}
|
|||
|
if (typeof a != 'object' || typeof b != 'object') return false;
|
|||
|
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
|||
|
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
|||
|
var length = aStack.length;
|
|||
|
while (length--) {
|
|||
|
// Linear search. Performance is inversely proportional to the number of
|
|||
|
// unique nested structures.
|
|||
|
if (aStack[length] == a) return bStack[length] == b;
|
|||
|
}
|
|||
|
// Objects with different constructors are not equivalent, but `Object`s
|
|||
|
// from different frames are.
|
|||
|
var aCtor = a.constructor, bCtor = b.constructor;
|
|||
|
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
|
|||
|
_.isFunction(bCtor) && (bCtor instanceof bCtor))
|
|||
|
&& ('constructor' in a && 'constructor' in b)) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
// Add the first object to the stack of traversed objects.
|
|||
|
aStack.push(a);
|
|||
|
bStack.push(b);
|
|||
|
var size = 0, result = true;
|
|||
|
// Recursively compare objects and arrays.
|
|||
|
if (className == '[object Array]') {
|
|||
|
// Compare array lengths to determine if a deep comparison is necessary.
|
|||
|
size = a.length;
|
|||
|
result = size == b.length;
|
|||
|
if (result) {
|
|||
|
// Deep compare the contents, ignoring non-numeric properties.
|
|||
|
while (size--) {
|
|||
|
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
// Deep compare objects.
|
|||
|
for (var key in a) {
|
|||
|
if (_.has(a, key)) {
|
|||
|
// Count the expected number of properties.
|
|||
|
size++;
|
|||
|
// Deep compare each member.
|
|||
|
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
|
|||
|
}
|
|||
|
}
|
|||
|
// Ensure that both objects contain the same number of properties.
|
|||
|
if (result) {
|
|||
|
for (key in b) {
|
|||
|
if (_.has(b, key) && !(size--)) break;
|
|||
|
}
|
|||
|
result = !size;
|
|||
|
}
|
|||
|
}
|
|||
|
// Remove the first object from the stack of traversed objects.
|
|||
|
aStack.pop();
|
|||
|
bStack.pop();
|
|||
|
return result;
|
|||
|
};
|
|||
|
|
|||
|
// Perform a deep comparison to check if two objects are equal.
|
|||
|
_.isEqual = function(a, b) {
|
|||
|
return eq(a, b, [], []);
|
|||
|
};
|
|||
|
|
|||
|
// Is a given array, string, or object empty?
|
|||
|
// An "empty" object has no enumerable own-properties.
|
|||
|
_.isEmpty = function(obj) {
|
|||
|
if (obj == null) return true;
|
|||
|
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
|||
|
for (var key in obj) if (_.has(obj, key)) return false;
|
|||
|
return true;
|
|||
|
};
|
|||
|
|
|||
|
// Is a given value a DOM element?
|
|||
|
_.isElement = function(obj) {
|
|||
|
return !!(obj && obj.nodeType === 1);
|
|||
|
};
|
|||
|
|
|||
|
// Is a given value an array?
|
|||
|
// Delegates to ECMA5's native Array.isArray
|
|||
|
_.isArray = nativeIsArray || function(obj) {
|
|||
|
return toString.call(obj) == '[object Array]';
|
|||
|
};
|
|||
|
|
|||
|
// Is a given variable an object?
|
|||
|
_.isObject = function(obj) {
|
|||
|
return obj === Object(obj);
|
|||
|
};
|
|||
|
|
|||
|
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
|
|||
|
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
|
|||
|
_['is' + name] = function(obj) {
|
|||
|
return toString.call(obj) == '[object ' + name + ']';
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
// Define a fallback version of the method in browsers (ahem, IE), where
|
|||
|
// there isn't any inspectable "Arguments" type.
|
|||
|
if (!_.isArguments(arguments)) {
|
|||
|
_.isArguments = function(obj) {
|
|||
|
return !!(obj && _.has(obj, 'callee'));
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
// Optimize `isFunction` if appropriate.
|
|||
|
if (typeof (/./) !== 'function') {
|
|||
|
_.isFunction = function(obj) {
|
|||
|
return typeof obj === 'function';
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
// Is a given object a finite number?
|
|||
|
_.isFinite = function(obj) {
|
|||
|
return isFinite(obj) && !isNaN(parseFloat(obj));
|
|||
|
};
|
|||
|
|
|||
|
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
|||
|
_.isNaN = function(obj) {
|
|||
|
return _.isNumber(obj) && obj != +obj;
|
|||
|
};
|
|||
|
|
|||
|
// Is a given value a boolean?
|
|||
|
_.isBoolean = function(obj) {
|
|||
|
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
|||
|
};
|
|||
|
|
|||
|
// Is a given value equal to null?
|
|||
|
_.isNull = function(obj) {
|
|||
|
return obj === null;
|
|||
|
};
|
|||
|
|
|||
|
// Is a given variable undefined?
|
|||
|
_.isUndefined = function(obj) {
|
|||
|
return obj === void 0;
|
|||
|
};
|
|||
|
|
|||
|
// Shortcut function for checking if an object has a given property directly
|
|||
|
// on itself (in other words, not on a prototype).
|
|||
|
_.has = function(obj, key) {
|
|||
|
return hasOwnProperty.call(obj, key);
|
|||
|
};
|
|||
|
|
|||
|
// Utility Functions
|
|||
|
// -----------------
|
|||
|
|
|||
|
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
|||
|
// previous owner. Returns a reference to the Underscore object.
|
|||
|
_.noConflict = function() {
|
|||
|
root._ = previousUnderscore;
|
|||
|
return this;
|
|||
|
};
|
|||
|
|
|||
|
// Keep the identity function around for default iterators.
|
|||
|
_.identity = function(value) {
|
|||
|
return value;
|
|||
|
};
|
|||
|
|
|||
|
_.constant = function(value) {
|
|||
|
return function () {
|
|||
|
return value;
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
_.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) {
|
|||
|
return function(obj) {
|
|||
|
if (obj === attrs) return true; //avoid comparing an object to itself.
|
|||
|
for (var key in attrs) {
|
|||
|
if (attrs[key] !== obj[key])
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// Run a function **n** times.
|
|||
|
_.times = function(n, iterator, context) {
|
|||
|
var accum = Array(Math.max(0, n));
|
|||
|
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
|
|||
|
return accum;
|
|||
|
};
|
|||
|
|
|||
|
// Return a random integer between min and max (inclusive).
|
|||
|
_.random = function(min, max) {
|
|||
|
if (max == null) {
|
|||
|
max = min;
|
|||
|
min = 0;
|
|||
|
}
|
|||
|
return min + Math.floor(Math.random() * (max - min + 1));
|
|||
|
};
|
|||
|
|
|||
|
// 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 entityMap = {
|
|||
|
escape: {
|
|||
|
'&': '&',
|
|||
|
'<': '<',
|
|||
|
'>': '>',
|
|||
|
'"': '"',
|
|||
|
"'": '''
|
|||
|
}
|
|||
|
};
|
|||
|
entityMap.unescape = _.invert(entityMap.escape);
|
|||
|
|
|||
|
// Regexes containing the keys and values listed immediately above.
|
|||
|
var entityRegexes = {
|
|||
|
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
|
|||
|
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
|
|||
|
};
|
|||
|
|
|||
|
// Functions for escaping and unescaping strings to/from HTML interpolation.
|
|||
|
_.each(['escape', 'unescape'], function(method) {
|
|||
|
_[method] = function(string) {
|
|||
|
if (string == null) return '';
|
|||
|
return ('' + string).replace(entityRegexes[method], function(match) {
|
|||
|
return entityMap[method][match];
|
|||
|
});
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
// If the value of the named `property` is a function then invoke it with the
|
|||
|
// `object` as context; otherwise, return it.
|
|||
|
_.result = function(object, property) {
|
|||
|
if (object == null) return void 0;
|
|||
|
var value = object[property];
|
|||
|
return _.isFunction(value) ? value.call(object) : value;
|
|||
|
};
|
|||
|
|
|||
|
// Add your own custom functions to the Underscore object.
|
|||
|
_.mixin = function(obj) {
|
|||
|
each(_.functions(obj), function(name) {
|
|||
|
var func = _[name] = obj[name];
|
|||
|
_.prototype[name] = function() {
|
|||
|
var args = [this._wrapped];
|
|||
|
push.apply(args, arguments);
|
|||
|
return result.call(this, func.apply(_, args));
|
|||
|
};
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
// Generate a unique integer id (unique within the entire client session).
|
|||
|
// Useful for temporary DOM ids.
|
|||
|
var idCounter = 0;
|
|||
|
_.uniqueId = function(prefix) {
|
|||
|
var id = ++idCounter + '';
|
|||
|
return prefix ? prefix + id : id;
|
|||
|
};
|
|||
|
|
|||
|
// By default, Underscore uses ERB-style template delimiters, change the
|
|||
|
// following template settings to use alternative delimiters.
|
|||
|
_.templateSettings = {
|
|||
|
evaluate : /<%([\s\S]+?)%>/g,
|
|||
|
interpolate : /<%=([\s\S]+?)%>/g,
|
|||
|
escape : /<%-([\s\S]+?)%>/g
|
|||
|
};
|
|||
|
|
|||
|
// When customizing `templateSettings`, if you don't want to define an
|
|||
|
// interpolation, evaluation or escaping regex, we need one that is
|
|||
|
// guaranteed not to match.
|
|||
|
var noMatch = /(.)^/;
|
|||
|
|
|||
|
// Certain characters need to be escaped so that they can be put into a
|
|||
|
// string literal.
|
|||
|
var escapes = {
|
|||
|
"'": "'",
|
|||
|
'\\': '\\',
|
|||
|
'\r': 'r',
|
|||
|
'\n': 'n',
|
|||
|
'\t': 't',
|
|||
|
'\u2028': 'u2028',
|
|||
|
'\u2029': 'u2029'
|
|||
|
};
|
|||
|
|
|||
|
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
|
|||
|
|
|||
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
|||
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
|||
|
// and correctly escapes quotes within interpolated code.
|
|||
|
_.template = function(text, data, settings) {
|
|||
|
var render;
|
|||
|
settings = _.defaults({}, settings, _.templateSettings);
|
|||
|
|
|||
|
// Combine delimiters into one regular expression via alternation.
|
|||
|
var matcher = new RegExp([
|
|||
|
(settings.escape || noMatch).source,
|
|||
|
(settings.interpolate || noMatch).source,
|
|||
|
(settings.evaluate || noMatch).source
|
|||
|
].join('|') + '|$', 'g');
|
|||
|
|
|||
|
// Compile the template source, escaping string literals appropriately.
|
|||
|
var index = 0;
|
|||
|
var source = "__p+='";
|
|||
|
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
|||
|
source += text.slice(index, offset)
|
|||
|
.replace(escaper, function(match) { return '\\' + escapes[match]; });
|
|||
|
|
|||
|
if (escape) {
|
|||
|
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
|||
|
}
|
|||
|
if (interpolate) {
|
|||
|
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
|||
|
}
|
|||
|
if (evaluate) {
|
|||
|
source += "';\n" + evaluate + "\n__p+='";
|
|||
|
}
|
|||
|
index = offset + match.length;
|
|||
|
return match;
|
|||
|
});
|
|||
|
source += "';\n";
|
|||
|
|
|||
|
// If a variable is not specified, place data values in local scope.
|
|||
|
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
|||
|
|
|||
|
source = "var __t,__p='',__j=Array.prototype.join," +
|
|||
|
"print=function(){__p+=__j.call(arguments,'');};\n" +
|
|||
|
source + "return __p;\n";
|
|||
|
|
|||
|
try {
|
|||
|
render = new Function(settings.variable || 'obj', '_', source);
|
|||
|
} catch (e) {
|
|||
|
e.source = source;
|
|||
|
throw e;
|
|||
|
}
|
|||
|
|
|||
|
if (data) return render(data, _);
|
|||
|
var template = function(data) {
|
|||
|
return render.call(this, data, _);
|
|||
|
};
|
|||
|
|
|||
|
// Provide the compiled function source as a convenience for precompilation.
|
|||
|
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
|
|||
|
|
|||
|
return template;
|
|||
|
};
|
|||
|
|
|||
|
// Add a "chain" function, which will delegate to the wrapper.
|
|||
|
_.chain = function(obj) {
|
|||
|
return _(obj).chain();
|
|||
|
};
|
|||
|
|
|||
|
// OOP
|
|||
|
// ---------------
|
|||
|
// If Underscore is called as a function, it returns a wrapped object that
|
|||
|
// can be used OO-style. This wrapper holds altered versions of all the
|
|||
|
// underscore functions. Wrapped objects may be chained.
|
|||
|
|
|||
|
// Helper function to continue chaining intermediate results.
|
|||
|
var result = function(obj) {
|
|||
|
return this._chain ? _(obj).chain() : obj;
|
|||
|
};
|
|||
|
|
|||
|
// Add all of the Underscore functions to the wrapper object.
|
|||
|
_.mixin(_);
|
|||
|
|
|||
|
// Add all mutator Array functions to the wrapper.
|
|||
|
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
|||
|
var method = ArrayProto[name];
|
|||
|
_.prototype[name] = function() {
|
|||
|
var obj = this._wrapped;
|
|||
|
method.apply(obj, arguments);
|
|||
|
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
|
|||
|
return result.call(this, obj);
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
// Add all accessor Array functions to the wrapper.
|
|||
|
each(['concat', 'join', 'slice'], function(name) {
|
|||
|
var method = ArrayProto[name];
|
|||
|
_.prototype[name] = function() {
|
|||
|
return result.call(this, method.apply(this._wrapped, arguments));
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
_.extend(_.prototype, {
|
|||
|
|
|||
|
// Start chaining a wrapped Underscore object.
|
|||
|
chain: function() {
|
|||
|
this._chain = true;
|
|||
|
return this;
|
|||
|
},
|
|||
|
|
|||
|
// Extracts the result from a wrapped and chained object.
|
|||
|
value: function() {
|
|||
|
return this._wrapped;
|
|||
|
}
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
// 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);
|
|||
|
|
|||
|
/*!
|
|||
|
|
|||
|
handlebars v1.3.0
|
|||
|
|
|||
|
Copyright (C) 2011 by Yehuda Katz
|
|||
|
|
|||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|||
|
of this software and associated documentation files (the "Software"), to deal
|
|||
|
in the Software without restriction, including without limitation the rights
|
|||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
|
copies of the Software, and to permit persons to whom the Software is
|
|||
|
furnished to do so, subject to the following conditions:
|
|||
|
|
|||
|
The above copyright notice and this permission notice shall be included in
|
|||
|
all copies or substantial portions of the Software.
|
|||
|
|
|||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
|
THE SOFTWARE.
|
|||
|
|
|||
|
@license
|
|||
|
*/
|
|||
|
var Handlebars=function(){var a=function(){"use strict";function a(a){this.string=a}var b;return a.prototype.toString=function(){return""+this.string},b=a}(),b=function(a){"use strict";function b(a){return h[a]||"&"}function c(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])}function d(a){return a instanceof g?a.toString():a||0===a?(a=""+a,j.test(a)?a.replace(i,b):a):""}function e(a){return a||0===a?m(a)&&0===a.length?!0:!1:!0}var f={},g=a,h={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},i=/[&<>"'`]/g,j=/[&<>"'`]/;f.extend=c;var k=Object.prototype.toString;f.toString=k;var l=function(a){return"function"==typeof a};l(/x/)&&(l=function(a){return"function"==typeof a&&"[object Function]"===k.call(a)});var l;f.isFunction=l;var m=Array.isArray||function(a){return a&&"object"==typeof a?"[object Array]"===k.call(a):!1};return f.isArray=m,f.escapeExpression=d,f.isEmpty=e,f}(a),c=function(){"use strict";function a(a,b){var d;b&&b.firstLine&&(d=b.firstLine,a+=" - "+d+":"+b.firstColumn);for(var e=Error.prototype.constructor.call(this,a),f=0;f<c.length;f++)this[c[f]]=e[c[f]];d&&(this.lineNumber=d,this.column=b.firstColumn)}var b,c=["description","fileName","lineNumber","message","name","number","stack"];return a.prototype=new Error,b=a}(),d=function(a,b){"use strict";function c(a,b){this.helpers=a||{},this.partials=b||{},d(this)}function d(a){a.registerHelper("helperMissing",function(a){if(2===arguments.length)return void 0;throw new h("Missing helper: '"+a+"'")}),a.registerHelper("blockHelperMissing",function(b,c){var d=c.inverse||function(){},e=c.fn;return m(b)&&(b=b.call(this)),b===!0?e(this):b===!1||null==b?d(this):l(b)?b.length>0?a.helpers.each(b,c):d(this):e(b)}),a.registerHelper("each",function(a,b){var c,d=b.fn,e=b.inverse,f=0,g="";if(m(a)&&(a=a.call(this)),b.data&&(c=q(b.data)),a&&"object"==typeof a)if(l(a))for(var h=a.length;h>f;f++)c&&(c.index=f,c.first=0===f,c.last=f===a.length-1),g+=d(a[f],{data:c});else for(var i in a)a.hasOwnProperty(i)&&(c&&(c.key=i,c.index=f,c.first=0===f),g+=d(a[i],{data:c}),f++);return 0===f&&(g=e(this)),g}),a.registerHelper("if",function(a,b){return m(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||g.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})}),a.registerHelper("with",function(a,b){return m(a)&&(a=a.call(this)),g.isEmpty(a)?void 0:b.fn(a)}),a.registerHelper("log",function(b,c){var d=c.data&&null!=c.data.level?parseInt(c.data.level,10):1;a.log(d,b)})}function e(a,b){p.log(a,b)}var f={},g=a,h=b,i="1.3.0";f.VERSION=i;var j=4;f.COMPILER_REVISION=j;var k={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:">= 1.0.0"};f.REVISION_CHANGES=k;var l=g.isArray,m=g.isFunction,n=g.toString,o="[object Object]";f.HandlebarsEnvironment=c,c.prototype={constructor:c,logger:p,log:e,registerHelper:function(a,b,c){if(n.call(a)===o){if(c||b)throw new h("Arg not supported with multiple helpers");g.extend(this.helpers,a)}else c&&(b.not=c),this.helpers[a]=b},registerPartial:function(a,b){n.call(a)===o?g.extend(this.partials,a):this.partials[a]=b}};var p={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:3,log:function(a,b){if(p.level<=a){var c=p.methodMap[a];"undefined"!=typeof console&&console[c]&&console[c].call(console,b)}}};f.logger=p,f.log=e;var q=function(a){var b={};return g.extend(b,a),b};return f.createFrame=q,f}(b,c),e=function(a,b,c){"use strict";function d(a){var b=a&&a[0]||1,c=m;if(b!==c){if(c>b){var d=n[c],e=n[b];throw new l("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+d+") or downgrade your runtime to an older version ("+e+").")}throw new l("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+a[1]+").")}}function e(a,b){if(!b)throw new l("No environment passed to template");var c=function(a,c,d,e,f,g){var h=b.VM.invokePartial.a
|
|||
|
var e,f=a.opcodes;this.i=0;for(var g=f.length;this.i<g;this.i++)e=f[this.i],"DECLARE"===e.opcode?this[e.name]=e.value:this[e.opcode].apply(this,e.args),e.opcode!==this.stripNext&&(this.stripNext=!1);if(this.pushSource(""),this.stackSlot||this.inlineStack.length||this.compileStack.length)throw new i("Compile completed with content left on stack");return this.createFunctionContext(d)},preamble:function(){var a=[];if(this.isChild)a.push("");else{var b=this.namespace,c="helpers = this.merge(helpers, "+b+".helpers);";this.environment.usePartial&&(c=c+" partials = this.merge(partials, "+b+".partials);"),this.options.data&&(c+=" data = data || {};"),a.push(c)}this.environment.isSimple?a.push(""):a.push(", buffer = "+this.initializeBuffer()),this.lastContext=0,this.source=a},createFunctionContext:function(a){var b=this.stackVars.concat(this.registers.list);if(b.length>0&&(this.source[1]=this.source[1]+", "+b.join(", ")),!this.isChild)for(var c in this.context.aliases)this.context.aliases.hasOwnProperty(c)&&(this.source[1]=this.source[1]+", "+c+"="+this.context.aliases[c]);this.source[1]&&(this.source[1]="var "+this.source[1].substring(2)+";"),this.isChild||(this.source[1]+="\n"+this.context.programs.join("\n")+"\n"),this.environment.isSimple||this.pushSource("return buffer;");for(var d=this.isChild?["depth0","data"]:["Handlebars","depth0","helpers","partials","data"],e=0,f=this.environment.depths.list.length;f>e;e++)d.push("depth"+this.environment.depths.list[e]);var g=this.mergeSource();if(this.isChild||(g=this.compilerInfo()+g),a)return d.push(g),Function.apply(this,d);var i="function "+(this.name||"")+"("+d.join(",")+") {\n "+g+"}";return h("debug",i+"\n\n"),i},mergeSource:function(){for(var a,b="",c=0,d=this.source.length;d>c;c++){var e=this.source[c];e.appendToBuffer?a=a?a+"\n + "+e.content:e.content:(a&&(b+="buffer += "+a+";\n ",a=void 0),b+=e+"\n ")}return b},blockValue:function(){this.context.aliases.blockHelperMissing="helpers.blockHelperMissing";var a=["depth0"];this.setupParams(0,a),this.replaceStack(function(b){return a.splice(1,0,b),"blockHelperMissing.call("+a.join(", ")+")"})},ambiguousBlockValue:function(){this.context.aliases.blockHelperMissing="helpers.blockHelperMissing";var a=["depth0"];this.setupParams(0,a);var b=this.topStack();a.splice(1,0,b),this.pushSource("if (!"+this.lastHelper+") { "+b+" = blockHelperMissing.call("+a.join(", ")+"); }")},appendContent:function(a){this.pendingContent&&(a=this.pendingContent+a),this.stripNext&&(a=a.replace(/^\s+/,"")),this.pendingContent=a},strip:function(){this.pendingContent&&(this.pendingContent=this.pendingContent.replace(/\s+$/,"")),this.stripNext="strip"},append:function(){this.flushInline();var a=this.popStack();this.pushSource("if("+a+" || "+a+" === 0) { "+this.appendToBuffer(a)+" }"),this.environment.isSimple&&this.pushSource("else { "+this.appendToBuffer("''")+" }")},appendEscaped:function(){this.context.aliases.escapeExpression="this.escapeExpression",this.pushSource(this.appendToBuffer("escapeExpression("+this.popStack()+")"))},getContext:function(a){this.lastContext!==a&&(this.lastContext=a)},lookupOnContext:function(a){this.push(this.nameLookup("depth"+this.lastContext,a,"context"))},pushContext:function(){this.pushStackLiteral("depth"+this.lastContext)},resolvePossibleLambda:function(){this.context.aliases.functionType='"function"',this.replaceStack(function(a){return"typeof "+a+" === functionType ? "+a+".apply(depth0) : "+a})},lookup:function(a){this.replaceStack(function(b){return b+" == null || "+b+" === false ? "+b+" : "+this.nameLookup(b,a,"context")})},lookupData:function(){this.pushStackLiteral("data")},pushStringParam:function(a,b){this.pushStackLiteral("depth"+this.lastContext),this.pushString(b),"sexpr"!==b&&("string"==typeof a?this.pushString(a):this.pushStackLiteral(a))},emptyHash:function(){this.pushStackLiteral("{}"),this.options.stringParams&&(this.push("{}"),this.push("{}"))},pushHash:function(){this.hash&&this.hashes.push(this.hash),this.hash={values:[],types:[],contexts:[]}},popHash:function(){var a=this.hash;this.hash=this.hashes.
|
|||
|
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
|
|||
|
//
|
|||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
|
// you may not use this file except in compliance with the License.
|
|||
|
// You may obtain a copy of the License at
|
|||
|
//
|
|||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
//
|
|||
|
// Unless required by applicable law or agreed to in writing, software
|
|||
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
|
// See the License for the specific language governing permissions and
|
|||
|
// limitations under the License.
|
|||
|
|
|||
|
/**
|
|||
|
* @fileoverview Bootstrap for the Google JS Library (Closure).
|
|||
|
*
|
|||
|
* In uncompiled mode base.js will write out Closure's deps file, unless the
|
|||
|
* global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects to
|
|||
|
* include their own deps file(s) from different locations.
|
|||
|
*
|
|||
|
*
|
|||
|
* @provideGoog
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {boolean} Overridden to true by the compiler when --closure_pass
|
|||
|
* or --mark_as_compiled is specified.
|
|||
|
*/
|
|||
|
var COMPILED = false;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Base namespace for the Closure library. Checks to see goog is already
|
|||
|
* defined in the current scope before assigning to prevent clobbering if
|
|||
|
* base.js is loaded more than once.
|
|||
|
*
|
|||
|
* @const
|
|||
|
*/
|
|||
|
var goog = goog || {};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Reference to the global context. In most cases this will be 'window'.
|
|||
|
*/
|
|||
|
goog.global = this;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* A hook for overriding the define values in uncompiled mode.
|
|||
|
*
|
|||
|
* In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before
|
|||
|
* loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES},
|
|||
|
* {@code goog.define} will use the value instead of the default value. This
|
|||
|
* allows flags to be overwritten without compilation (this is normally
|
|||
|
* accomplished with the compiler's "define" flag).
|
|||
|
*
|
|||
|
* Example:
|
|||
|
* <pre>
|
|||
|
* var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
|
|||
|
* </pre>
|
|||
|
*
|
|||
|
* @type {Object.<string, (string|number|boolean)>|undefined}
|
|||
|
*/
|
|||
|
goog.global.CLOSURE_UNCOMPILED_DEFINES;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* A hook for overriding the define values in uncompiled or compiled mode,
|
|||
|
* like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In
|
|||
|
* uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.
|
|||
|
*
|
|||
|
* Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or
|
|||
|
* string literals or the compiler will emit an error.
|
|||
|
*
|
|||
|
* While any @define value may be set, only those set with goog.define will be
|
|||
|
* effective for uncompiled code.
|
|||
|
*
|
|||
|
* Example:
|
|||
|
* <pre>
|
|||
|
* var CLOSURE_DEFINES = {'goog.DEBUG': false};
|
|||
|
* </pre>
|
|||
|
*
|
|||
|
* @type {Object.<string, (string|number|boolean)>|undefined}
|
|||
|
*/
|
|||
|
goog.global.CLOSURE_DEFINES;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is not undefined.
|
|||
|
* WARNING: Do not use this to test if an object has a property. Use the in
|
|||
|
* operator instead.
|
|||
|
*
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is defined.
|
|||
|
*/
|
|||
|
goog.isDef = function(val) {
|
|||
|
// void 0 always evaluates to undefined and hence we do not need to depend on
|
|||
|
// the definition of the global variable named 'undefined'.
|
|||
|
return val !== void 0;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Builds an object structure for the provided namespace path, ensuring that
|
|||
|
* names that already exist are not overwritten. For example:
|
|||
|
* "a.b.c" -> a = {};a.b={};a.b.c={};
|
|||
|
* Used by goog.provide and goog.exportSymbol.
|
|||
|
* @param {string} name name of the object that this file defines.
|
|||
|
* @param {*=} opt_object the object to expose at the end of the path.
|
|||
|
* @param {Object=} opt_objectToExportTo The object to add the path to; default
|
|||
|
* is |goog.global|.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
|
|||
|
var parts = name.split('.');
|
|||
|
var cur = opt_objectToExportTo || goog.global;
|
|||
|
|
|||
|
// Internet Explorer exhibits strange behavior when throwing errors from
|
|||
|
// methods externed in this manner. See the testExportSymbolExceptions in
|
|||
|
// base_test.html for an example.
|
|||
|
if (!(parts[0] in cur) && cur.execScript) {
|
|||
|
cur.execScript('var ' + parts[0]);
|
|||
|
}
|
|||
|
|
|||
|
// Certain browsers cannot parse code in the form for((a in b); c;);
|
|||
|
// This pattern is produced by the JSCompiler when it collapses the
|
|||
|
// statement above into the conditional loop below. To prevent this from
|
|||
|
// happening, use a for-loop and reserve the init logic as below.
|
|||
|
|
|||
|
// Parentheses added to eliminate strict JS warning in Firefox.
|
|||
|
for (var part; parts.length && (part = parts.shift());) {
|
|||
|
if (!parts.length && goog.isDef(opt_object)) {
|
|||
|
// last part and we have an object; use it
|
|||
|
cur[part] = opt_object;
|
|||
|
} else if (cur[part]) {
|
|||
|
cur = cur[part];
|
|||
|
} else {
|
|||
|
cur = cur[part] = {};
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Defines a named value. In uncompiled mode, the value is retreived from
|
|||
|
* CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and
|
|||
|
* has the property specified, and otherwise used the defined defaultValue.
|
|||
|
* When compiled, the default can be overridden using compiler command-line
|
|||
|
* options.
|
|||
|
*
|
|||
|
* @param {string} name The distinguished name to provide.
|
|||
|
* @param {string|number|boolean} defaultValue
|
|||
|
*/
|
|||
|
goog.define = function(name, defaultValue) {
|
|||
|
var value = defaultValue;
|
|||
|
if (!COMPILED) {
|
|||
|
if (goog.global.CLOSURE_UNCOMPILED_DEFINES &&
|
|||
|
Object.prototype.hasOwnProperty.call(
|
|||
|
goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) {
|
|||
|
value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name];
|
|||
|
} else if (goog.global.CLOSURE_DEFINES &&
|
|||
|
Object.prototype.hasOwnProperty.call(
|
|||
|
goog.global.CLOSURE_DEFINES, name)) {
|
|||
|
value = goog.global.CLOSURE_DEFINES[name];
|
|||
|
}
|
|||
|
}
|
|||
|
goog.exportPath_(name, value);
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {boolean} DEBUG is provided as a convenience so that debugging code
|
|||
|
* that should not be included in a production js_binary can be easily stripped
|
|||
|
* by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
|
|||
|
* toString() methods should be declared inside an "if (goog.DEBUG)" conditional
|
|||
|
* because they are generally used for debugging purposes and it is difficult
|
|||
|
* for the JSCompiler to statically determine whether they are used.
|
|||
|
*/
|
|||
|
goog.DEBUG = true;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {string} LOCALE defines the locale being used for compilation. It is
|
|||
|
* used to select locale specific data to be compiled in js binary. BUILD rule
|
|||
|
* can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler
|
|||
|
* option.
|
|||
|
*
|
|||
|
* Take into account that the locale code format is important. You should use
|
|||
|
* the canonical Unicode format with hyphen as a delimiter. Language must be
|
|||
|
* lowercase, Language Script - Capitalized, Region - UPPERCASE.
|
|||
|
* There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
|
|||
|
*
|
|||
|
* See more info about locale codes here:
|
|||
|
* http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
|
|||
|
*
|
|||
|
* For language codes you should use values defined by ISO 693-1. See it here
|
|||
|
* http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
|
|||
|
* this rule: the Hebrew language. For legacy reasons the old code (iw) should
|
|||
|
* be used instead of the new code (he), see http://wiki/Main/IIISynonyms.
|
|||
|
*/
|
|||
|
goog.define('goog.LOCALE', 'en'); // default to en
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {boolean} Whether this code is running on trusted sites.
|
|||
|
*
|
|||
|
* On untrusted sites, several native functions can be defined or overridden by
|
|||
|
* external libraries like Prototype, Datejs, and JQuery and setting this flag
|
|||
|
* to false forces closure to use its own implementations when possible.
|
|||
|
*
|
|||
|
* If your JavaScript can be loaded by a third party site and you are wary about
|
|||
|
* relying on non-standard implementations, specify
|
|||
|
* "--define goog.TRUSTED_SITE=false" to the JSCompiler.
|
|||
|
*/
|
|||
|
goog.define('goog.TRUSTED_SITE', true);
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {boolean} Whether a project is expected to be running in strict mode.
|
|||
|
*
|
|||
|
* This define can be used to trigger alternate implementations compatible with
|
|||
|
* running in EcmaScript Strict mode or warn about unavailable functionality.
|
|||
|
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode
|
|||
|
*/
|
|||
|
goog.define('goog.STRICT_MODE_COMPATIBLE', false);
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Creates object stubs for a namespace. The presence of one or more
|
|||
|
* goog.provide() calls indicate that the file defines the given
|
|||
|
* objects/namespaces. Provided objects must not be null or undefined.
|
|||
|
* Build tools also scan for provide/require statements
|
|||
|
* to discern dependencies, build dependency files (see deps.js), etc.
|
|||
|
* @see goog.require
|
|||
|
* @param {string} name Namespace provided by this file in the form
|
|||
|
* "goog.package.part".
|
|||
|
*/
|
|||
|
goog.provide = function(name) {
|
|||
|
if (!COMPILED) {
|
|||
|
// Ensure that the same namespace isn't provided twice.
|
|||
|
// A goog.module/goog.provide maps a goog.require to a specific file
|
|||
|
if (goog.isProvided_(name)) {
|
|||
|
throw Error('Namespace "' + name + '" already declared.');
|
|||
|
}
|
|||
|
delete goog.implicitNamespaces_[name];
|
|||
|
|
|||
|
var namespace = name;
|
|||
|
while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
|
|||
|
if (goog.getObjectByName(namespace)) {
|
|||
|
break;
|
|||
|
}
|
|||
|
goog.implicitNamespaces_[namespace] = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
goog.exportPath_(name);
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* goog.module serves two purposes:
|
|||
|
* - marks a file that must be loaded as a module
|
|||
|
* - reserves a namespace (it can not also be goog.provided)
|
|||
|
* and has three requirements:
|
|||
|
* - goog.module may not be used in the same file as goog.provide.
|
|||
|
* - goog.module must be the first statement in the file.
|
|||
|
* - only one goog.module is allowed per file.
|
|||
|
* When a goog.module annotated file is loaded, it is loaded enclosed in
|
|||
|
* a strict function closure. This means that:
|
|||
|
* - any variable declared in a goog.module file are private to the file,
|
|||
|
* not global. Although the compiler is expected to inline the module.
|
|||
|
* - The code must obey all the rules of "strict" JavaScript.
|
|||
|
* - the file will be marked as "use strict"
|
|||
|
*
|
|||
|
* NOTE: unlike goog.provide, goog.module does not declare any symbols by
|
|||
|
* itself.
|
|||
|
*
|
|||
|
* @param {string} name Namespace provided by this file in the form
|
|||
|
* "goog.package.part", is expected but not required.
|
|||
|
*/
|
|||
|
goog.module = function(name) {
|
|||
|
if (!goog.isString(name) || !name) {
|
|||
|
throw Error('Invalid module identifier');
|
|||
|
}
|
|||
|
if (!goog.isInModuleLoader_()) {
|
|||
|
throw Error('Module ' + name + ' has been loaded incorrectly.');
|
|||
|
}
|
|||
|
if (goog.moduleLoaderState_.moduleName) {
|
|||
|
throw Error('goog.module may only be called once per module.');
|
|||
|
}
|
|||
|
|
|||
|
// Store the module name for the loader.
|
|||
|
goog.moduleLoaderState_.moduleName = name;
|
|||
|
if (!COMPILED) {
|
|||
|
// Ensure that the same namespace isn't provided twice.
|
|||
|
// A goog.module/goog.provide maps a goog.require to a specific file
|
|||
|
if (goog.isProvided_(name)) {
|
|||
|
throw Error('Namespace "' + name + '" already declared.');
|
|||
|
}
|
|||
|
delete goog.implicitNamespaces_[name];
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/** @private {{
|
|||
|
* moduleName:(string|undefined),
|
|||
|
* declareTestMethods:boolean}|null}}
|
|||
|
*/
|
|||
|
goog.moduleLoaderState_ = null;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @private
|
|||
|
* @return {boolean} Whether a goog.module is currently being initialized.
|
|||
|
*/
|
|||
|
goog.isInModuleLoader_ = function() {
|
|||
|
return goog.moduleLoaderState_ != null;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Indicate that a module's exports that are known test methods should
|
|||
|
* be copied to the global object. This makes the test methods visible to
|
|||
|
* test runners that inspect the global object.
|
|||
|
*
|
|||
|
* TODO(johnlenz): Make the test framework aware of goog.module so
|
|||
|
* that this isn't necessary. Alternately combine this with goog.setTestOnly
|
|||
|
* to minimize boiler plate.
|
|||
|
*/
|
|||
|
goog.module.declareTestMethods = function() {
|
|||
|
if (!goog.isInModuleLoader_()) {
|
|||
|
throw new Error('goog.module.declareTestMethods must be called from ' +
|
|||
|
'within a goog.module');
|
|||
|
}
|
|||
|
goog.moduleLoaderState_.declareTestMethods = true;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Marks that the current file should only be used for testing, and never for
|
|||
|
* live code in production.
|
|||
|
*
|
|||
|
* In the case of unit tests, the message may optionally be an exact namespace
|
|||
|
* for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
|
|||
|
* provide (if not explicitly defined in the code).
|
|||
|
*
|
|||
|
* @param {string=} opt_message Optional message to add to the error that's
|
|||
|
* raised when used in production code.
|
|||
|
*/
|
|||
|
goog.setTestOnly = function(opt_message) {
|
|||
|
if (COMPILED && !goog.DEBUG) {
|
|||
|
opt_message = opt_message || '';
|
|||
|
throw Error('Importing test-only code into non-debug environment' +
|
|||
|
(opt_message ? ': ' + opt_message : '.'));
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Forward declares a symbol. This is an indication to the compiler that the
|
|||
|
* symbol may be used in the source yet is not required and may not be provided
|
|||
|
* in compilation.
|
|||
|
*
|
|||
|
* The most common usage of forward declaration is code that takes a type as a
|
|||
|
* function parameter but does not need to require it. By forward declaring
|
|||
|
* instead of requiring, no hard dependency is made, and (if not required
|
|||
|
* elsewhere) the namespace may never be required and thus, not be pulled
|
|||
|
* into the JavaScript binary. If it is required elsewhere, it will be type
|
|||
|
* checked as normal.
|
|||
|
*
|
|||
|
*
|
|||
|
* @param {string} name The namespace to forward declare in the form of
|
|||
|
* "goog.package.part".
|
|||
|
*/
|
|||
|
goog.forwardDeclare = function(name) {};
|
|||
|
|
|||
|
|
|||
|
if (!COMPILED) {
|
|||
|
|
|||
|
/**
|
|||
|
* Check if the given name has been goog.provided. This will return false for
|
|||
|
* names that are available only as implicit namespaces.
|
|||
|
* @param {string} name name of the object to look for.
|
|||
|
* @return {boolean} Whether the name has been provided.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.isProvided_ = function(name) {
|
|||
|
return (name in goog.loadedModules_) ||
|
|||
|
(!goog.implicitNamespaces_[name] &&
|
|||
|
goog.isDefAndNotNull(goog.getObjectByName(name)));
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Namespaces implicitly defined by goog.provide. For example,
|
|||
|
* goog.provide('goog.events.Event') implicitly declares that 'goog' and
|
|||
|
* 'goog.events' must be namespaces.
|
|||
|
*
|
|||
|
* @type {Object.<string, (boolean|undefined)>}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.implicitNamespaces_ = {'goog.module': true};
|
|||
|
|
|||
|
// NOTE: We add goog.module as an implicit namespace as goog.module is defined
|
|||
|
// here and because the existing module package has not been moved yet out of
|
|||
|
// the goog.module namespace. This satisifies both the debug loader and
|
|||
|
// ahead-of-time dependency management.
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns an object based on its fully qualified external name. The object
|
|||
|
* is not found if null or undefined. If you are using a compilation pass that
|
|||
|
* renames property names beware that using this function will not find renamed
|
|||
|
* properties.
|
|||
|
*
|
|||
|
* @param {string} name The fully qualified name.
|
|||
|
* @param {Object=} opt_obj The object within which to look; default is
|
|||
|
* |goog.global|.
|
|||
|
* @return {?} The value (object or primitive) or, if not found, null.
|
|||
|
*/
|
|||
|
goog.getObjectByName = function(name, opt_obj) {
|
|||
|
var parts = name.split('.');
|
|||
|
var cur = opt_obj || goog.global;
|
|||
|
for (var part; part = parts.shift(); ) {
|
|||
|
if (goog.isDefAndNotNull(cur[part])) {
|
|||
|
cur = cur[part];
|
|||
|
} else {
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
return cur;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Globalizes a whole namespace, such as goog or goog.lang.
|
|||
|
*
|
|||
|
* @param {Object} obj The namespace to globalize.
|
|||
|
* @param {Object=} opt_global The object to add the properties to.
|
|||
|
* @deprecated Properties may be explicitly exported to the global scope, but
|
|||
|
* this should no longer be done in bulk.
|
|||
|
*/
|
|||
|
goog.globalize = function(obj, opt_global) {
|
|||
|
var global = opt_global || goog.global;
|
|||
|
for (var x in obj) {
|
|||
|
global[x] = obj[x];
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Adds a dependency from a file to the files it requires.
|
|||
|
* @param {string} relPath The path to the js file.
|
|||
|
* @param {Array} provides An array of strings with the names of the objects
|
|||
|
* this file provides.
|
|||
|
* @param {Array} requires An array of strings with the names of the objects
|
|||
|
* this file requires.
|
|||
|
* @param {boolean=} opt_isModule Whether this dependency must be loaded as
|
|||
|
* a module as declared by goog.module.
|
|||
|
*/
|
|||
|
goog.addDependency = function(relPath, provides, requires, opt_isModule) {
|
|||
|
if (goog.DEPENDENCIES_ENABLED) {
|
|||
|
var provide, require;
|
|||
|
var path = relPath.replace(/\\/g, '/');
|
|||
|
var deps = goog.dependencies_;
|
|||
|
for (var i = 0; provide = provides[i]; i++) {
|
|||
|
deps.nameToPath[provide] = path;
|
|||
|
deps.pathIsModule[path] = !!opt_isModule;
|
|||
|
}
|
|||
|
for (var j = 0; require = requires[j]; j++) {
|
|||
|
if (!(path in deps.requires)) {
|
|||
|
deps.requires[path] = {};
|
|||
|
}
|
|||
|
deps.requires[path][require] = true;
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// NOTE(nnaze): The debug DOM loader was included in base.js as an original way
|
|||
|
// to do "debug-mode" development. The dependency system can sometimes be
|
|||
|
// confusing, as can the debug DOM loader's asynchronous nature.
|
|||
|
//
|
|||
|
// With the DOM loader, a call to goog.require() is not blocking -- the script
|
|||
|
// will not load until some point after the current script. If a namespace is
|
|||
|
// needed at runtime, it needs to be defined in a previous script, or loaded via
|
|||
|
// require() with its registered dependencies.
|
|||
|
// User-defined namespaces may need their own deps file. See http://go/js_deps,
|
|||
|
// http://go/genjsdeps, or, externally, DepsWriter.
|
|||
|
// https://developers.google.com/closure/library/docs/depswriter
|
|||
|
//
|
|||
|
// Because of legacy clients, the DOM loader can't be easily removed from
|
|||
|
// base.js. Work is being done to make it disableable or replaceable for
|
|||
|
// different environments (DOM-less JavaScript interpreters like Rhino or V8,
|
|||
|
// for example). See bootstrap/ for more information.
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {boolean} Whether to enable the debug loader.
|
|||
|
*
|
|||
|
* If enabled, a call to goog.require() will attempt to load the namespace by
|
|||
|
* appending a script tag to the DOM (if the namespace has been registered).
|
|||
|
*
|
|||
|
* If disabled, goog.require() will simply assert that the namespace has been
|
|||
|
* provided (and depend on the fact that some outside tool correctly ordered
|
|||
|
* the script).
|
|||
|
*/
|
|||
|
goog.define('goog.ENABLE_DEBUG_LOADER', true);
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @param {string} msg
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.logToConsole_ = function(msg) {
|
|||
|
if (goog.global.console) {
|
|||
|
goog.global.console['error'](msg);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Implements a system for the dynamic resolution of dependencies that works in
|
|||
|
* parallel with the BUILD system. Note that all calls to goog.require will be
|
|||
|
* stripped by the JSCompiler when the --closure_pass option is used.
|
|||
|
* @see goog.provide
|
|||
|
* @param {string} name Namespace to include (as was given in goog.provide()) in
|
|||
|
* the form "goog.package.part".
|
|||
|
* @return {?} If called within a goog.module file, the associated namespace or
|
|||
|
* module otherwise null.
|
|||
|
*/
|
|||
|
goog.require = function(name) {
|
|||
|
|
|||
|
// If the object already exists we do not need do do anything.
|
|||
|
if (!COMPILED) {
|
|||
|
if (goog.isProvided_(name)) {
|
|||
|
if (goog.isInModuleLoader_()) {
|
|||
|
// goog.require only return a value with-in goog.module files.
|
|||
|
return name in goog.loadedModules_ ?
|
|||
|
goog.loadedModules_[name] :
|
|||
|
goog.getObjectByName(name);
|
|||
|
} else {
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (goog.ENABLE_DEBUG_LOADER) {
|
|||
|
var path = goog.getPathFromDeps_(name);
|
|||
|
if (path) {
|
|||
|
goog.included_[path] = true;
|
|||
|
goog.writeScripts_();
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var errorMessage = 'goog.require could not find: ' + name;
|
|||
|
goog.logToConsole_(errorMessage);
|
|||
|
|
|||
|
throw Error(errorMessage);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Path for included scripts.
|
|||
|
* @type {string}
|
|||
|
*/
|
|||
|
goog.basePath = '';
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* A hook for overriding the base path.
|
|||
|
* @type {string|undefined}
|
|||
|
*/
|
|||
|
goog.global.CLOSURE_BASE_PATH;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Whether to write out Closure's deps file. By default, the deps are written.
|
|||
|
* @type {boolean|undefined}
|
|||
|
*/
|
|||
|
goog.global.CLOSURE_NO_DEPS;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* A function to import a single script. This is meant to be overridden when
|
|||
|
* Closure is being run in non-HTML contexts, such as web workers. It's defined
|
|||
|
* in the global scope so that it can be set before base.js is loaded, which
|
|||
|
* allows deps.js to be imported properly.
|
|||
|
*
|
|||
|
* The function is passed the script source, which is a relative URI. It should
|
|||
|
* return true if the script was imported, false otherwise.
|
|||
|
* @type {(function(string): boolean)|undefined}
|
|||
|
*/
|
|||
|
goog.global.CLOSURE_IMPORT_SCRIPT;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Null function used for default values of callbacks, etc.
|
|||
|
* @return {void} Nothing.
|
|||
|
*/
|
|||
|
goog.nullFunction = function() {};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* The identity function. Returns its first argument.
|
|||
|
*
|
|||
|
* @param {*=} opt_returnValue The single value that will be returned.
|
|||
|
* @param {...*} var_args Optional trailing arguments. These are ignored.
|
|||
|
* @return {?} The first argument. We can't know the type -- just pass it along
|
|||
|
* without type.
|
|||
|
* @deprecated Use goog.functions.identity instead.
|
|||
|
*/
|
|||
|
goog.identityFunction = function(opt_returnValue, var_args) {
|
|||
|
return opt_returnValue;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* When defining a class Foo with an abstract method bar(), you can do:
|
|||
|
* Foo.prototype.bar = goog.abstractMethod
|
|||
|
*
|
|||
|
* Now if a subclass of Foo fails to override bar(), an error will be thrown
|
|||
|
* when bar() is invoked.
|
|||
|
*
|
|||
|
* Note: This does not take the name of the function to override as an argument
|
|||
|
* because that would make it more difficult to obfuscate our JavaScript code.
|
|||
|
*
|
|||
|
* @type {!Function}
|
|||
|
* @throws {Error} when invoked to indicate the method should be overridden.
|
|||
|
*/
|
|||
|
goog.abstractMethod = function() {
|
|||
|
throw Error('unimplemented abstract method');
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Adds a {@code getInstance} static method that always returns the same
|
|||
|
* instance object.
|
|||
|
* @param {!Function} ctor The constructor for the class to add the static
|
|||
|
* method to.
|
|||
|
*/
|
|||
|
goog.addSingletonGetter = function(ctor) {
|
|||
|
ctor.getInstance = function() {
|
|||
|
if (ctor.instance_) {
|
|||
|
return ctor.instance_;
|
|||
|
}
|
|||
|
if (goog.DEBUG) {
|
|||
|
// NOTE: JSCompiler can't optimize away Array#push.
|
|||
|
goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
|
|||
|
}
|
|||
|
return ctor.instance_ = new ctor;
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* All singleton classes that have been instantiated, for testing. Don't read
|
|||
|
* it directly, use the {@code goog.testing.singleton} module. The compiler
|
|||
|
* removes this variable if unused.
|
|||
|
* @type {!Array.<!Function>}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.instantiatedSingletons_ = [];
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {boolean} Whether to load goog.modules using {@code eval} when using
|
|||
|
* the debug loader. This provides a better debugging experience as the
|
|||
|
* source is unmodified and can be edited using Chrome Workspaces or
|
|||
|
* similiar. However in some environments the use of {@code eval} is banned
|
|||
|
* so we provide an alternative.
|
|||
|
*/
|
|||
|
goog.define('goog.LOAD_MODULE_USING_EVAL', true);
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* The registry of initialized modules:
|
|||
|
* the module identifier to module exports map.
|
|||
|
* @private @const {Object.<string, ?>}
|
|||
|
*/
|
|||
|
goog.loadedModules_ = {};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* True if goog.dependencies_ is available.
|
|||
|
* @const {boolean}
|
|||
|
*/
|
|||
|
goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
|
|||
|
|
|||
|
|
|||
|
if (goog.DEPENDENCIES_ENABLED) {
|
|||
|
/**
|
|||
|
* Object used to keep track of urls that have already been added. This record
|
|||
|
* allows the prevention of circular dependencies.
|
|||
|
* @type {Object}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.included_ = {};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* This object is used to keep track of dependencies and other data that is
|
|||
|
* used for loading scripts.
|
|||
|
* @private
|
|||
|
* @type {Object}
|
|||
|
*/
|
|||
|
goog.dependencies_ = {
|
|||
|
pathIsModule: {}, // 1 to 1
|
|||
|
nameToPath: {}, // many to 1
|
|||
|
requires: {}, // 1 to many
|
|||
|
// Used when resolving dependencies to prevent us from visiting file twice.
|
|||
|
visited: {},
|
|||
|
written: {} // Used to keep track of script files we have written.
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Tries to detect whether is in the context of an HTML document.
|
|||
|
* @return {boolean} True if it looks like HTML document.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.inHtmlDocument_ = function() {
|
|||
|
var doc = goog.global.document;
|
|||
|
return typeof doc != 'undefined' &&
|
|||
|
'write' in doc; // XULDocument misses write.
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Tries to detect the base path of base.js script that bootstraps Closure.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.findBasePath_ = function() {
|
|||
|
if (goog.global.CLOSURE_BASE_PATH) {
|
|||
|
goog.basePath = goog.global.CLOSURE_BASE_PATH;
|
|||
|
return;
|
|||
|
} else if (!goog.inHtmlDocument_()) {
|
|||
|
return;
|
|||
|
}
|
|||
|
var doc = goog.global.document;
|
|||
|
var scripts = doc.getElementsByTagName('script');
|
|||
|
// Search backwards since the current script is in almost all cases the one
|
|||
|
// that has base.js.
|
|||
|
for (var i = scripts.length - 1; i >= 0; --i) {
|
|||
|
var src = scripts[i].src;
|
|||
|
var qmark = src.lastIndexOf('?');
|
|||
|
var l = qmark == -1 ? src.length : qmark;
|
|||
|
if (src.substr(l - 7, 7) == 'base.js') {
|
|||
|
goog.basePath = src.substr(0, l - 7);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Imports a script if, and only if, that script hasn't already been imported.
|
|||
|
* (Must be called at execution time)
|
|||
|
* @param {string} src Script source.
|
|||
|
* @param {string=} opt_sourceText The optionally source text to evaluate
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.importScript_ = function(src, opt_sourceText) {
|
|||
|
var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
|
|||
|
goog.writeScriptTag_;
|
|||
|
if (importScript(src, opt_sourceText)) {
|
|||
|
goog.dependencies_.written[src] = true;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/** @const @private {boolean} */
|
|||
|
goog.IS_OLD_IE_ = goog.global.document &&
|
|||
|
goog.global.document.all && !goog.global.atob;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Given a URL initiate retrieval and execution of the module.
|
|||
|
* @param {string} src Script source URL.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.importModule_ = function(src) {
|
|||
|
// In an attempt to keep browsers from timing out loading scripts using
|
|||
|
// synchronous XHRs, put each load in its own script block.
|
|||
|
var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");';
|
|||
|
|
|||
|
if (goog.importScript_('', bootstrap)) {
|
|||
|
goog.dependencies_.written[src] = true;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/** @private {Array.<string>} */
|
|||
|
goog.queuedModules_ = [];
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Retrieve and execute a module.
|
|||
|
* @param {string} src Script source URL.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.retrieveAndExecModule_ = function(src) {
|
|||
|
var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
|
|||
|
goog.writeScriptTag_;
|
|||
|
|
|||
|
var scriptText = null;
|
|||
|
|
|||
|
var xhr = new goog.global['XMLHttpRequest']();
|
|||
|
|
|||
|
/** @this {Object} */
|
|||
|
xhr.onload = function() {
|
|||
|
scriptText = this.responseText;
|
|||
|
};
|
|||
|
xhr.open('get', src, false);
|
|||
|
xhr.send();
|
|||
|
|
|||
|
scriptText = xhr.responseText;
|
|||
|
|
|||
|
if (scriptText != null) {
|
|||
|
var execModuleScript = goog.wrapModule_(src, scriptText);
|
|||
|
var isOldIE = goog.IS_OLD_IE_;
|
|||
|
if (isOldIE) {
|
|||
|
goog.queuedModules_.push(execModuleScript);
|
|||
|
} else {
|
|||
|
importScript(src, execModuleScript);
|
|||
|
}
|
|||
|
goog.dependencies_.written[src] = true;
|
|||
|
} else {
|
|||
|
throw new Error('load of ' + src + 'failed');
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Return an appropriate module text. Suitable to insert into
|
|||
|
* a script tag (that is unescaped).
|
|||
|
* @param {string} srcUrl
|
|||
|
* @param {string} scriptText
|
|||
|
* @return {string}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.wrapModule_ = function(srcUrl, scriptText) {
|
|||
|
if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) {
|
|||
|
return '' +
|
|||
|
'goog.loadModule(function(exports) {' +
|
|||
|
'"use strict";' +
|
|||
|
scriptText +
|
|||
|
'\n' + // terminate any trailing single line comment.
|
|||
|
';return exports' +
|
|||
|
'});' +
|
|||
|
'\n//# sourceURL=' + srcUrl + '\n';
|
|||
|
} else {
|
|||
|
return '' +
|
|||
|
'goog.loadModule(' +
|
|||
|
goog.global.JSON.stringify(
|
|||
|
scriptText + '\n//# sourceURL=' + srcUrl + '\n') +
|
|||
|
');';
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Load any deferred goog.module loads.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.loadQueuedModules_ = function() {
|
|||
|
var count = goog.queuedModules_.length;
|
|||
|
if (count > 0) {
|
|||
|
var queue = goog.queuedModules_;
|
|||
|
goog.queuedModules_ = [];
|
|||
|
for (var i = 0; i < count; i++) {
|
|||
|
var entry = queue[i];
|
|||
|
goog.globalEval(entry);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @param {function(?):?|string} moduleDef The module definition.
|
|||
|
*/
|
|||
|
goog.loadModule = function(moduleDef) {
|
|||
|
// NOTE: we allow function definitions to be either in the from
|
|||
|
// of a string to eval (which keeps the original source intact) or
|
|||
|
// in a eval forbidden environment (CSP) we allow a function definition
|
|||
|
// which in its body must call {@code goog.module}, and return the exports
|
|||
|
// of the module.
|
|||
|
try {
|
|||
|
goog.moduleLoaderState_ = {
|
|||
|
moduleName: undefined, declareTestMethods: false};
|
|||
|
var exports;
|
|||
|
if (goog.isFunction(moduleDef)) {
|
|||
|
exports = moduleDef.call(goog.global, {});
|
|||
|
} else if (goog.isString(moduleDef)) {
|
|||
|
exports = goog.loadModuleFromSource_.call(goog.global, moduleDef);
|
|||
|
} else {
|
|||
|
throw Error('Invalid module definition');
|
|||
|
}
|
|||
|
|
|||
|
if (Object.seal) {
|
|||
|
Object.seal(exports);
|
|||
|
}
|
|||
|
var moduleName = goog.moduleLoaderState_.moduleName;
|
|||
|
if (!goog.isString(moduleName) || !moduleName) {
|
|||
|
throw Error('Invalid module name \"' + moduleName + '\"');
|
|||
|
}
|
|||
|
|
|||
|
goog.loadedModules_[moduleName] = exports;
|
|||
|
if (goog.moduleLoaderState_.declareTestMethods) {
|
|||
|
for (var entry in exports) {
|
|||
|
if (entry.indexOf('test', 0) === 0 ||
|
|||
|
entry == 'tearDown' ||
|
|||
|
entry == 'setup') {
|
|||
|
goog.global[entry] = exports[entry];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
} finally {
|
|||
|
goog.moduleLoaderState_ = null;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @private @const {function(string):?}
|
|||
|
*/
|
|||
|
goog.loadModuleFromSource_ = function() {
|
|||
|
// NOTE: we avoid declaring parameters or local variables here to avoid
|
|||
|
// masking globals or leaking values into the module definition.
|
|||
|
'use strict';
|
|||
|
var exports = {};
|
|||
|
eval(arguments[0]);
|
|||
|
return exports;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* The default implementation of the import function. Writes a script tag to
|
|||
|
* import the script.
|
|||
|
*
|
|||
|
* @param {string} src The script url.
|
|||
|
* @param {string=} opt_sourceText The optionally source text to evaluate
|
|||
|
* @return {boolean} True if the script was imported, false otherwise.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.writeScriptTag_ = function(src, opt_sourceText) {
|
|||
|
if (goog.inHtmlDocument_()) {
|
|||
|
var doc = goog.global.document;
|
|||
|
|
|||
|
// If the user tries to require a new symbol after document load,
|
|||
|
// something has gone terribly wrong. Doing a document.write would
|
|||
|
// wipe out the page.
|
|||
|
if (doc.readyState == 'complete') {
|
|||
|
// Certain test frameworks load base.js multiple times, which tries
|
|||
|
// to write deps.js each time. If that happens, just fail silently.
|
|||
|
// These frameworks wipe the page between each load of base.js, so this
|
|||
|
// is OK.
|
|||
|
var isDeps = /\bdeps.js$/.test(src);
|
|||
|
if (isDeps) {
|
|||
|
return false;
|
|||
|
} else {
|
|||
|
throw Error('Cannot write "' + src + '" after document load');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var isOldIE = goog.IS_OLD_IE_;
|
|||
|
|
|||
|
if (opt_sourceText === undefined) {
|
|||
|
if (!isOldIE) {
|
|||
|
doc.write(
|
|||
|
'<script type="text/javascript" src="' +
|
|||
|
src + '"></' + 'script>');
|
|||
|
} else {
|
|||
|
var state = " onreadystatechange='goog.onScriptLoad_(this, " +
|
|||
|
++goog.lastNonModuleScriptIndex_ + ")' ";
|
|||
|
doc.write(
|
|||
|
'<script type="text/javascript" src="' +
|
|||
|
src + '"' + state + '></' + 'script>');
|
|||
|
}
|
|||
|
} else {
|
|||
|
doc.write(
|
|||
|
'<script type="text/javascript">' +
|
|||
|
opt_sourceText +
|
|||
|
'</' + 'script>');
|
|||
|
}
|
|||
|
return true;
|
|||
|
} else {
|
|||
|
return false;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/** @private {number} */
|
|||
|
goog.lastNonModuleScriptIndex_ = 0;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* A readystatechange handler for legacy IE
|
|||
|
* @param {HTMLScriptElement} script
|
|||
|
* @param {number} scriptIndex
|
|||
|
* @return {boolean}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.onScriptLoad_ = function(script, scriptIndex) {
|
|||
|
// for now load the modules when we reach the last script,
|
|||
|
// later allow more inter-mingling.
|
|||
|
if (script.readyState == 'complete' &&
|
|||
|
goog.lastNonModuleScriptIndex_ == scriptIndex) {
|
|||
|
goog.loadQueuedModules_();
|
|||
|
}
|
|||
|
return true;
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Resolves dependencies based on the dependencies added using addDependency
|
|||
|
* and calls importScript_ in the correct order.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.writeScripts_ = function() {
|
|||
|
// The scripts we need to write this time.
|
|||
|
var scripts = [];
|
|||
|
var seenScript = {};
|
|||
|
var deps = goog.dependencies_;
|
|||
|
|
|||
|
function visitNode(path) {
|
|||
|
if (path in deps.written) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// We have already visited this one. We can get here if we have cyclic
|
|||
|
// dependencies.
|
|||
|
if (path in deps.visited) {
|
|||
|
if (!(path in seenScript)) {
|
|||
|
seenScript[path] = true;
|
|||
|
scripts.push(path);
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
deps.visited[path] = true;
|
|||
|
|
|||
|
if (path in deps.requires) {
|
|||
|
for (var requireName in deps.requires[path]) {
|
|||
|
// If the required name is defined, we assume that it was already
|
|||
|
// bootstrapped by other means.
|
|||
|
if (!goog.isProvided_(requireName)) {
|
|||
|
if (requireName in deps.nameToPath) {
|
|||
|
visitNode(deps.nameToPath[requireName]);
|
|||
|
} else {
|
|||
|
throw Error('Undefined nameToPath for ' + requireName);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!(path in seenScript)) {
|
|||
|
seenScript[path] = true;
|
|||
|
scripts.push(path);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
for (var path in goog.included_) {
|
|||
|
if (!deps.written[path]) {
|
|||
|
visitNode(path);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// record that we are going to load all these scripts.
|
|||
|
for (var i = 0; i < scripts.length; i++) {
|
|||
|
var path = scripts[i];
|
|||
|
goog.dependencies_.written[path] = true;
|
|||
|
}
|
|||
|
|
|||
|
// If a module is loaded synchronously then we need to
|
|||
|
// clear the current inModuleLoader value, and restore it when we are
|
|||
|
// done loading the current "requires".
|
|||
|
var moduleState = goog.moduleLoaderState_;
|
|||
|
goog.moduleLoaderState_ = null;
|
|||
|
|
|||
|
var loadingModule = false;
|
|||
|
for (var i = 0; i < scripts.length; i++) {
|
|||
|
var path = scripts[i];
|
|||
|
if (path) {
|
|||
|
if (!deps.pathIsModule[path]) {
|
|||
|
goog.importScript_(goog.basePath + path);
|
|||
|
} else {
|
|||
|
loadingModule = true;
|
|||
|
goog.importModule_(goog.basePath + path);
|
|||
|
}
|
|||
|
} else {
|
|||
|
goog.moduleLoaderState_ = moduleState;
|
|||
|
throw Error('Undefined script input');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// restore the current "module loading state"
|
|||
|
goog.moduleLoaderState_ = moduleState;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Looks at the dependency rules and tries to determine the script file that
|
|||
|
* fulfills a particular rule.
|
|||
|
* @param {string} rule In the form goog.namespace.Class or project.script.
|
|||
|
* @return {?string} Url corresponding to the rule, or null.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.getPathFromDeps_ = function(rule) {
|
|||
|
if (rule in goog.dependencies_.nameToPath) {
|
|||
|
return goog.dependencies_.nameToPath[rule];
|
|||
|
} else {
|
|||
|
return null;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
goog.findBasePath_();
|
|||
|
|
|||
|
// Allow projects to manage the deps files themselves.
|
|||
|
if (!goog.global.CLOSURE_NO_DEPS) {
|
|||
|
goog.importScript_(goog.basePath + 'deps.js');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//==============================================================================
|
|||
|
// Language Enhancements
|
|||
|
//==============================================================================
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* This is a "fixed" version of the typeof operator. It differs from the typeof
|
|||
|
* operator in such a way that null returns 'null' and arrays return 'array'.
|
|||
|
* @param {*} value The value to get the type of.
|
|||
|
* @return {string} The name of the type.
|
|||
|
*/
|
|||
|
goog.typeOf = function(value) {
|
|||
|
var s = typeof value;
|
|||
|
if (s == 'object') {
|
|||
|
if (value) {
|
|||
|
// Check these first, so we can avoid calling Object.prototype.toString if
|
|||
|
// possible.
|
|||
|
//
|
|||
|
// IE improperly marshals tyepof across execution contexts, but a
|
|||
|
// cross-context object will still return false for "instanceof Object".
|
|||
|
if (value instanceof Array) {
|
|||
|
return 'array';
|
|||
|
} else if (value instanceof Object) {
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
// HACK: In order to use an Object prototype method on the arbitrary
|
|||
|
// value, the compiler requires the value be cast to type Object,
|
|||
|
// even though the ECMA spec explicitly allows it.
|
|||
|
var className = Object.prototype.toString.call(
|
|||
|
/** @type {Object} */ (value));
|
|||
|
// In Firefox 3.6, attempting to access iframe window objects' length
|
|||
|
// property throws an NS_ERROR_FAILURE, so we need to special-case it
|
|||
|
// here.
|
|||
|
if (className == '[object Window]') {
|
|||
|
return 'object';
|
|||
|
}
|
|||
|
|
|||
|
// We cannot always use constructor == Array or instanceof Array because
|
|||
|
// different frames have different Array objects. In IE6, if the iframe
|
|||
|
// where the array was created is destroyed, the array loses its
|
|||
|
// prototype. Then dereferencing val.splice here throws an exception, so
|
|||
|
// we can't use goog.isFunction. Calling typeof directly returns 'unknown'
|
|||
|
// so that will work. In this case, this function will return false and
|
|||
|
// most array functions will still work because the array is still
|
|||
|
// array-like (supports length and []) even though it has lost its
|
|||
|
// prototype.
|
|||
|
// Mark Miller noticed that Object.prototype.toString
|
|||
|
// allows access to the unforgeable [[Class]] property.
|
|||
|
// 15.2.4.2 Object.prototype.toString ( )
|
|||
|
// When the toString method is called, the following steps are taken:
|
|||
|
// 1. Get the [[Class]] property of this object.
|
|||
|
// 2. Compute a string value by concatenating the three strings
|
|||
|
// "[object ", Result(1), and "]".
|
|||
|
// 3. Return Result(2).
|
|||
|
// and this behavior survives the destruction of the execution context.
|
|||
|
if ((className == '[object Array]' ||
|
|||
|
// In IE all non value types are wrapped as objects across window
|
|||
|
// boundaries (not iframe though) so we have to do object detection
|
|||
|
// for this edge case.
|
|||
|
typeof value.length == 'number' &&
|
|||
|
typeof value.splice != 'undefined' &&
|
|||
|
typeof value.propertyIsEnumerable != 'undefined' &&
|
|||
|
!value.propertyIsEnumerable('splice')
|
|||
|
|
|||
|
)) {
|
|||
|
return 'array';
|
|||
|
}
|
|||
|
// HACK: There is still an array case that fails.
|
|||
|
// function ArrayImpostor() {}
|
|||
|
// ArrayImpostor.prototype = [];
|
|||
|
// var impostor = new ArrayImpostor;
|
|||
|
// this can be fixed by getting rid of the fast path
|
|||
|
// (value instanceof Array) and solely relying on
|
|||
|
// (value && Object.prototype.toString.vall(value) === '[object Array]')
|
|||
|
// but that would require many more function calls and is not warranted
|
|||
|
// unless closure code is receiving objects from untrusted sources.
|
|||
|
|
|||
|
// IE in cross-window calls does not correctly marshal the function type
|
|||
|
// (it appears just as an object) so we cannot use just typeof val ==
|
|||
|
// 'function'. However, if the object has a call property, it is a
|
|||
|
// function.
|
|||
|
if ((className == '[object Function]' ||
|
|||
|
typeof value.call != 'undefined' &&
|
|||
|
typeof value.propertyIsEnumerable != 'undefined' &&
|
|||
|
!value.propertyIsEnumerable('call'))) {
|
|||
|
return 'function';
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
return 'null';
|
|||
|
}
|
|||
|
|
|||
|
} else if (s == 'function' && typeof value.call == 'undefined') {
|
|||
|
// In Safari typeof nodeList returns 'function', and on Firefox typeof
|
|||
|
// behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We
|
|||
|
// would like to return object for those and we can detect an invalid
|
|||
|
// function by making sure that the function object has a call method.
|
|||
|
return 'object';
|
|||
|
}
|
|||
|
return s;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is null.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is null.
|
|||
|
*/
|
|||
|
goog.isNull = function(val) {
|
|||
|
return val === null;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is defined and not null.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is defined and not null.
|
|||
|
*/
|
|||
|
goog.isDefAndNotNull = function(val) {
|
|||
|
// Note that undefined == null.
|
|||
|
return val != null;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is an array.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is an array.
|
|||
|
*/
|
|||
|
goog.isArray = function(val) {
|
|||
|
return goog.typeOf(val) == 'array';
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the object looks like an array. To qualify as array like
|
|||
|
* the value needs to be either a NodeList or an object with a Number length
|
|||
|
* property.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is an array.
|
|||
|
*/
|
|||
|
goog.isArrayLike = function(val) {
|
|||
|
var type = goog.typeOf(val);
|
|||
|
return type == 'array' || type == 'object' && typeof val.length == 'number';
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the object looks like a Date. To qualify as Date-like the
|
|||
|
* value needs to be an object and have a getFullYear() function.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is a like a Date.
|
|||
|
*/
|
|||
|
goog.isDateLike = function(val) {
|
|||
|
return goog.isObject(val) && typeof val.getFullYear == 'function';
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is a string.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is a string.
|
|||
|
*/
|
|||
|
goog.isString = function(val) {
|
|||
|
return typeof val == 'string';
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is a boolean.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is boolean.
|
|||
|
*/
|
|||
|
goog.isBoolean = function(val) {
|
|||
|
return typeof val == 'boolean';
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is a number.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is a number.
|
|||
|
*/
|
|||
|
goog.isNumber = function(val) {
|
|||
|
return typeof val == 'number';
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is a function.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is a function.
|
|||
|
*/
|
|||
|
goog.isFunction = function(val) {
|
|||
|
return goog.typeOf(val) == 'function';
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Returns true if the specified value is an object. This includes arrays and
|
|||
|
* functions.
|
|||
|
* @param {?} val Variable to test.
|
|||
|
* @return {boolean} Whether variable is an object.
|
|||
|
*/
|
|||
|
goog.isObject = function(val) {
|
|||
|
var type = typeof val;
|
|||
|
return type == 'object' && val != null || type == 'function';
|
|||
|
// return Object(val) === val also works, but is slower, especially if val is
|
|||
|
// not an object.
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Gets a unique ID for an object. This mutates the object so that further calls
|
|||
|
* with the same object as a parameter returns the same value. The unique ID is
|
|||
|
* guaranteed to be unique across the current session amongst objects that are
|
|||
|
* passed into {@code getUid}. There is no guarantee that the ID is unique or
|
|||
|
* consistent across sessions. It is unsafe to generate unique ID for function
|
|||
|
* prototypes.
|
|||
|
*
|
|||
|
* @param {Object} obj The object to get the unique ID for.
|
|||
|
* @return {number} The unique ID for the object.
|
|||
|
*/
|
|||
|
goog.getUid = function(obj) {
|
|||
|
// TODO(arv): Make the type stricter, do not accept null.
|
|||
|
|
|||
|
// In Opera window.hasOwnProperty exists but always returns false so we avoid
|
|||
|
// using it. As a consequence the unique ID generated for BaseClass.prototype
|
|||
|
// and SubClass.prototype will be the same.
|
|||
|
return obj[goog.UID_PROPERTY_] ||
|
|||
|
(obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Whether the given object is alreay assigned a unique ID.
|
|||
|
*
|
|||
|
* This does not modify the object.
|
|||
|
*
|
|||
|
* @param {Object} obj The object to check.
|
|||
|
* @return {boolean} Whether there an assigned unique id for the object.
|
|||
|
*/
|
|||
|
goog.hasUid = function(obj) {
|
|||
|
return !!obj[goog.UID_PROPERTY_];
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Removes the unique ID from an object. This is useful if the object was
|
|||
|
* previously mutated using {@code goog.getUid} in which case the mutation is
|
|||
|
* undone.
|
|||
|
* @param {Object} obj The object to remove the unique ID field from.
|
|||
|
*/
|
|||
|
goog.removeUid = function(obj) {
|
|||
|
// TODO(arv): Make the type stricter, do not accept null.
|
|||
|
|
|||
|
// In IE, DOM nodes are not instances of Object and throw an exception if we
|
|||
|
// try to delete. Instead we try to use removeAttribute.
|
|||
|
if ('removeAttribute' in obj) {
|
|||
|
obj.removeAttribute(goog.UID_PROPERTY_);
|
|||
|
}
|
|||
|
/** @preserveTry */
|
|||
|
try {
|
|||
|
delete obj[goog.UID_PROPERTY_];
|
|||
|
} catch (ex) {
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Name for unique ID property. Initialized in a way to help avoid collisions
|
|||
|
* with other closure JavaScript on the same page.
|
|||
|
* @type {string}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Counter for UID.
|
|||
|
* @type {number}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.uidCounter_ = 0;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Adds a hash code field to an object. The hash code is unique for the
|
|||
|
* given object.
|
|||
|
* @param {Object} obj The object to get the hash code for.
|
|||
|
* @return {number} The hash code for the object.
|
|||
|
* @deprecated Use goog.getUid instead.
|
|||
|
*/
|
|||
|
goog.getHashCode = goog.getUid;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Removes the hash code field from an object.
|
|||
|
* @param {Object} obj The object to remove the field from.
|
|||
|
* @deprecated Use goog.removeUid instead.
|
|||
|
*/
|
|||
|
goog.removeHashCode = goog.removeUid;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Clones a value. The input may be an Object, Array, or basic type. Objects and
|
|||
|
* arrays will be cloned recursively.
|
|||
|
*
|
|||
|
* WARNINGS:
|
|||
|
* <code>goog.cloneObject</code> does not detect reference loops. Objects that
|
|||
|
* refer to themselves will cause infinite recursion.
|
|||
|
*
|
|||
|
* <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
|
|||
|
* UIDs created by <code>getUid</code> into cloned results.
|
|||
|
*
|
|||
|
* @param {*} obj The value to clone.
|
|||
|
* @return {*} A clone of the input value.
|
|||
|
* @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
|
|||
|
*/
|
|||
|
goog.cloneObject = function(obj) {
|
|||
|
var type = goog.typeOf(obj);
|
|||
|
if (type == 'object' || type == 'array') {
|
|||
|
if (obj.clone) {
|
|||
|
return obj.clone();
|
|||
|
}
|
|||
|
var clone = type == 'array' ? [] : {};
|
|||
|
for (var key in obj) {
|
|||
|
clone[key] = goog.cloneObject(obj[key]);
|
|||
|
}
|
|||
|
return clone;
|
|||
|
}
|
|||
|
|
|||
|
return obj;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* A native implementation of goog.bind.
|
|||
|
* @param {Function} fn A function to partially apply.
|
|||
|
* @param {Object|undefined} selfObj Specifies the object which this should
|
|||
|
* point to when the function is run.
|
|||
|
* @param {...*} var_args Additional arguments that are partially applied to the
|
|||
|
* function.
|
|||
|
* @return {!Function} A partially-applied form of the function bind() was
|
|||
|
* invoked as a method of.
|
|||
|
* @private
|
|||
|
* @suppress {deprecated} The compiler thinks that Function.prototype.bind is
|
|||
|
* deprecated because some people have declared a pure-JS version.
|
|||
|
* Only the pure-JS version is truly deprecated.
|
|||
|
*/
|
|||
|
goog.bindNative_ = function(fn, selfObj, var_args) {
|
|||
|
return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* A pure-JS implementation of goog.bind.
|
|||
|
* @param {Function} fn A function to partially apply.
|
|||
|
* @param {Object|undefined} selfObj Specifies the object which this should
|
|||
|
* point to when the function is run.
|
|||
|
* @param {...*} var_args Additional arguments that are partially applied to the
|
|||
|
* function.
|
|||
|
* @return {!Function} A partially-applied form of the function bind() was
|
|||
|
* invoked as a method of.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.bindJs_ = function(fn, selfObj, var_args) {
|
|||
|
if (!fn) {
|
|||
|
throw new Error();
|
|||
|
}
|
|||
|
|
|||
|
if (arguments.length > 2) {
|
|||
|
var boundArgs = Array.prototype.slice.call(arguments, 2);
|
|||
|
return function() {
|
|||
|
// Prepend the bound arguments to the current arguments.
|
|||
|
var newArgs = Array.prototype.slice.call(arguments);
|
|||
|
Array.prototype.unshift.apply(newArgs, boundArgs);
|
|||
|
return fn.apply(selfObj, newArgs);
|
|||
|
};
|
|||
|
|
|||
|
} else {
|
|||
|
return function() {
|
|||
|
return fn.apply(selfObj, arguments);
|
|||
|
};
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Partially applies this function to a particular 'this object' and zero or
|
|||
|
* more arguments. The result is a new function with some arguments of the first
|
|||
|
* function pre-filled and the value of this 'pre-specified'.
|
|||
|
*
|
|||
|
* Remaining arguments specified at call-time are appended to the pre-specified
|
|||
|
* ones.
|
|||
|
*
|
|||
|
* Also see: {@link #partial}.
|
|||
|
*
|
|||
|
* Usage:
|
|||
|
* <pre>var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2');
|
|||
|
* barMethBound('arg3', 'arg4');</pre>
|
|||
|
*
|
|||
|
* @param {?function(this:T, ...)} fn A function to partially apply.
|
|||
|
* @param {T} selfObj Specifies the object which this should point to when the
|
|||
|
* function is run.
|
|||
|
* @param {...*} var_args Additional arguments that are partially applied to the
|
|||
|
* function.
|
|||
|
* @return {!Function} A partially-applied form of the function bind() was
|
|||
|
* invoked as a method of.
|
|||
|
* @template T
|
|||
|
* @suppress {deprecated} See above.
|
|||
|
*/
|
|||
|
goog.bind = function(fn, selfObj, var_args) {
|
|||
|
// TODO(nicksantos): narrow the type signature.
|
|||
|
if (Function.prototype.bind &&
|
|||
|
// NOTE(nicksantos): Somebody pulled base.js into the default Chrome
|
|||
|
// extension environment. This means that for Chrome extensions, they get
|
|||
|
// the implementation of Function.prototype.bind that calls goog.bind
|
|||
|
// instead of the native one. Even worse, we don't want to introduce a
|
|||
|
// circular dependency between goog.bind and Function.prototype.bind, so
|
|||
|
// we have to hack this to make sure it works correctly.
|
|||
|
Function.prototype.bind.toString().indexOf('native code') != -1) {
|
|||
|
goog.bind = goog.bindNative_;
|
|||
|
} else {
|
|||
|
goog.bind = goog.bindJs_;
|
|||
|
}
|
|||
|
return goog.bind.apply(null, arguments);
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Like bind(), except that a 'this object' is not required. Useful when the
|
|||
|
* target function is already bound.
|
|||
|
*
|
|||
|
* Usage:
|
|||
|
* var g = partial(f, arg1, arg2);
|
|||
|
* g(arg3, arg4);
|
|||
|
*
|
|||
|
* @param {Function} fn A function to partially apply.
|
|||
|
* @param {...*} var_args Additional arguments that are partially applied to fn.
|
|||
|
* @return {!Function} A partially-applied form of the function bind() was
|
|||
|
* invoked as a method of.
|
|||
|
*/
|
|||
|
goog.partial = function(fn, var_args) {
|
|||
|
var args = Array.prototype.slice.call(arguments, 1);
|
|||
|
return function() {
|
|||
|
// Clone the array (with slice()) and append additional arguments
|
|||
|
// to the existing arguments.
|
|||
|
var newArgs = args.slice();
|
|||
|
newArgs.push.apply(newArgs, arguments);
|
|||
|
return fn.apply(this, newArgs);
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Copies all the members of a source object to a target object. This method
|
|||
|
* does not work on all browsers for all objects that contain keys such as
|
|||
|
* toString or hasOwnProperty. Use goog.object.extend for this purpose.
|
|||
|
* @param {Object} target Target.
|
|||
|
* @param {Object} source Source.
|
|||
|
*/
|
|||
|
goog.mixin = function(target, source) {
|
|||
|
for (var x in source) {
|
|||
|
target[x] = source[x];
|
|||
|
}
|
|||
|
|
|||
|
// For IE7 or lower, the for-in-loop does not contain any properties that are
|
|||
|
// not enumerable on the prototype object (for example, isPrototypeOf from
|
|||
|
// Object.prototype) but also it will not include 'replace' on objects that
|
|||
|
// extend String and change 'replace' (not that it is common for anyone to
|
|||
|
// extend anything except Object).
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @return {number} An integer value representing the number of milliseconds
|
|||
|
* between midnight, January 1, 1970 and the current time.
|
|||
|
*/
|
|||
|
goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
|
|||
|
// Unary plus operator converts its operand to a number which in the case of
|
|||
|
// a date is done by calling getTime().
|
|||
|
return +new Date();
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Evals JavaScript in the global scope. In IE this uses execScript, other
|
|||
|
* browsers use goog.global.eval. If goog.global.eval does not evaluate in the
|
|||
|
* global scope (for example, in Safari), appends a script tag instead.
|
|||
|
* Throws an exception if neither execScript or eval is defined.
|
|||
|
* @param {string} script JavaScript string.
|
|||
|
*/
|
|||
|
goog.globalEval = function(script) {
|
|||
|
if (goog.global.execScript) {
|
|||
|
goog.global.execScript(script, 'JavaScript');
|
|||
|
} else if (goog.global.eval) {
|
|||
|
// Test to see if eval works
|
|||
|
if (goog.evalWorksForGlobals_ == null) {
|
|||
|
goog.global.eval('var _et_ = 1;');
|
|||
|
if (typeof goog.global['_et_'] != 'undefined') {
|
|||
|
delete goog.global['_et_'];
|
|||
|
goog.evalWorksForGlobals_ = true;
|
|||
|
} else {
|
|||
|
goog.evalWorksForGlobals_ = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (goog.evalWorksForGlobals_) {
|
|||
|
goog.global.eval(script);
|
|||
|
} else {
|
|||
|
var doc = goog.global.document;
|
|||
|
var scriptElt = doc.createElement('script');
|
|||
|
scriptElt.type = 'text/javascript';
|
|||
|
scriptElt.defer = false;
|
|||
|
// Note(user): can't use .innerHTML since "t('<test>')" will fail and
|
|||
|
// .text doesn't work in Safari 2. Therefore we append a text node.
|
|||
|
scriptElt.appendChild(doc.createTextNode(script));
|
|||
|
doc.body.appendChild(scriptElt);
|
|||
|
doc.body.removeChild(scriptElt);
|
|||
|
}
|
|||
|
} else {
|
|||
|
throw Error('goog.globalEval not available');
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Indicates whether or not we can call 'eval' directly to eval code in the
|
|||
|
* global scope. Set to a Boolean by the first call to goog.globalEval (which
|
|||
|
* empirically tests whether eval works for globals). @see goog.globalEval
|
|||
|
* @type {?boolean}
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.evalWorksForGlobals_ = null;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Optional map of CSS class names to obfuscated names used with
|
|||
|
* goog.getCssName().
|
|||
|
* @type {Object|undefined}
|
|||
|
* @private
|
|||
|
* @see goog.setCssNameMapping
|
|||
|
*/
|
|||
|
goog.cssNameMapping_;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Optional obfuscation style for CSS class names. Should be set to either
|
|||
|
* 'BY_WHOLE' or 'BY_PART' if defined.
|
|||
|
* @type {string|undefined}
|
|||
|
* @private
|
|||
|
* @see goog.setCssNameMapping
|
|||
|
*/
|
|||
|
goog.cssNameMappingStyle_;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Handles strings that are intended to be used as CSS class names.
|
|||
|
*
|
|||
|
* This function works in tandem with @see goog.setCssNameMapping.
|
|||
|
*
|
|||
|
* Without any mapping set, the arguments are simple joined with a hyphen and
|
|||
|
* passed through unaltered.
|
|||
|
*
|
|||
|
* When there is a mapping, there are two possible styles in which these
|
|||
|
* mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
|
|||
|
* of the passed in css name is rewritten according to the map. In the BY_WHOLE
|
|||
|
* style, the full css name is looked up in the map directly. If a rewrite is
|
|||
|
* not specified by the map, the compiler will output a warning.
|
|||
|
*
|
|||
|
* When the mapping is passed to the compiler, it will replace calls to
|
|||
|
* goog.getCssName with the strings from the mapping, e.g.
|
|||
|
* var x = goog.getCssName('foo');
|
|||
|
* var y = goog.getCssName(this.baseClass, 'active');
|
|||
|
* becomes:
|
|||
|
* var x= 'foo';
|
|||
|
* var y = this.baseClass + '-active';
|
|||
|
*
|
|||
|
* If one argument is passed it will be processed, if two are passed only the
|
|||
|
* modifier will be processed, as it is assumed the first argument was generated
|
|||
|
* as a result of calling goog.getCssName.
|
|||
|
*
|
|||
|
* @param {string} className The class name.
|
|||
|
* @param {string=} opt_modifier A modifier to be appended to the class name.
|
|||
|
* @return {string} The class name or the concatenation of the class name and
|
|||
|
* the modifier.
|
|||
|
*/
|
|||
|
goog.getCssName = function(className, opt_modifier) {
|
|||
|
var getMapping = function(cssName) {
|
|||
|
return goog.cssNameMapping_[cssName] || cssName;
|
|||
|
};
|
|||
|
|
|||
|
var renameByParts = function(cssName) {
|
|||
|
// Remap all the parts individually.
|
|||
|
var parts = cssName.split('-');
|
|||
|
var mapped = [];
|
|||
|
for (var i = 0; i < parts.length; i++) {
|
|||
|
mapped.push(getMapping(parts[i]));
|
|||
|
}
|
|||
|
return mapped.join('-');
|
|||
|
};
|
|||
|
|
|||
|
var rename;
|
|||
|
if (goog.cssNameMapping_) {
|
|||
|
rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ?
|
|||
|
getMapping : renameByParts;
|
|||
|
} else {
|
|||
|
rename = function(a) {
|
|||
|
return a;
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
if (opt_modifier) {
|
|||
|
return className + '-' + rename(opt_modifier);
|
|||
|
} else {
|
|||
|
return rename(className);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Sets the map to check when returning a value from goog.getCssName(). Example:
|
|||
|
* <pre>
|
|||
|
* goog.setCssNameMapping({
|
|||
|
* "goog": "a",
|
|||
|
* "disabled": "b",
|
|||
|
* });
|
|||
|
*
|
|||
|
* var x = goog.getCssName('goog');
|
|||
|
* // The following evaluates to: "a a-b".
|
|||
|
* goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
|
|||
|
* </pre>
|
|||
|
* When declared as a map of string literals to string literals, the JSCompiler
|
|||
|
* will replace all calls to goog.getCssName() using the supplied map if the
|
|||
|
* --closure_pass flag is set.
|
|||
|
*
|
|||
|
* @param {!Object} mapping A map of strings to strings where keys are possible
|
|||
|
* arguments to goog.getCssName() and values are the corresponding values
|
|||
|
* that should be returned.
|
|||
|
* @param {string=} opt_style The style of css name mapping. There are two valid
|
|||
|
* options: 'BY_PART', and 'BY_WHOLE'.
|
|||
|
* @see goog.getCssName for a description.
|
|||
|
*/
|
|||
|
goog.setCssNameMapping = function(mapping, opt_style) {
|
|||
|
goog.cssNameMapping_ = mapping;
|
|||
|
goog.cssNameMappingStyle_ = opt_style;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* To use CSS renaming in compiled mode, one of the input files should have a
|
|||
|
* call to goog.setCssNameMapping() with an object literal that the JSCompiler
|
|||
|
* can extract and use to replace all calls to goog.getCssName(). In uncompiled
|
|||
|
* mode, JavaScript code should be loaded before this base.js file that declares
|
|||
|
* a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
|
|||
|
* to ensure that the mapping is loaded before any calls to goog.getCssName()
|
|||
|
* are made in uncompiled mode.
|
|||
|
*
|
|||
|
* A hook for overriding the CSS name mapping.
|
|||
|
* @type {Object|undefined}
|
|||
|
*/
|
|||
|
goog.global.CLOSURE_CSS_NAME_MAPPING;
|
|||
|
|
|||
|
|
|||
|
if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
|
|||
|
// This does not call goog.setCssNameMapping() because the JSCompiler
|
|||
|
// requires that goog.setCssNameMapping() be called with an object literal.
|
|||
|
goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Gets a localized message.
|
|||
|
*
|
|||
|
* This function is a compiler primitive. If you give the compiler a localized
|
|||
|
* message bundle, it will replace the string at compile-time with a localized
|
|||
|
* version, and expand goog.getMsg call to a concatenated string.
|
|||
|
*
|
|||
|
* Messages must be initialized in the form:
|
|||
|
* <code>
|
|||
|
* var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
|
|||
|
* </code>
|
|||
|
*
|
|||
|
* @param {string} str Translatable string, places holders in the form {$foo}.
|
|||
|
* @param {Object=} opt_values Map of place holder name to value.
|
|||
|
* @return {string} message with placeholders filled.
|
|||
|
*/
|
|||
|
goog.getMsg = function(str, opt_values) {
|
|||
|
if (opt_values) {
|
|||
|
str = str.replace(/\{\$([^}]+)}/g, function(match, key) {
|
|||
|
return key in opt_values ? opt_values[key] : match;
|
|||
|
});
|
|||
|
}
|
|||
|
return str;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Gets a localized message. If the message does not have a translation, gives a
|
|||
|
* fallback message.
|
|||
|
*
|
|||
|
* This is useful when introducing a new message that has not yet been
|
|||
|
* translated into all languages.
|
|||
|
*
|
|||
|
* This function is a compiler primitive. Must be used in the form:
|
|||
|
* <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
|
|||
|
* where MSG_A and MSG_B were initialized with goog.getMsg.
|
|||
|
*
|
|||
|
* @param {string} a The preferred message.
|
|||
|
* @param {string} b The fallback message.
|
|||
|
* @return {string} The best translated message.
|
|||
|
*/
|
|||
|
goog.getMsgWithFallback = function(a, b) {
|
|||
|
return a;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Exposes an unobfuscated global namespace path for the given object.
|
|||
|
* Note that fields of the exported object *will* be obfuscated, unless they are
|
|||
|
* exported in turn via this function or goog.exportProperty.
|
|||
|
*
|
|||
|
* Also handy for making public items that are defined in anonymous closures.
|
|||
|
*
|
|||
|
* ex. goog.exportSymbol('public.path.Foo', Foo);
|
|||
|
*
|
|||
|
* ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
|
|||
|
* public.path.Foo.staticFunction();
|
|||
|
*
|
|||
|
* ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
|
|||
|
* Foo.prototype.myMethod);
|
|||
|
* new public.path.Foo().myMethod();
|
|||
|
*
|
|||
|
* @param {string} publicPath Unobfuscated name to export.
|
|||
|
* @param {*} object Object the name should point to.
|
|||
|
* @param {Object=} opt_objectToExportTo The object to add the path to; default
|
|||
|
* is goog.global.
|
|||
|
*/
|
|||
|
goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
|
|||
|
goog.exportPath_(publicPath, object, opt_objectToExportTo);
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Exports a property unobfuscated into the object's namespace.
|
|||
|
* ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
|
|||
|
* ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
|
|||
|
* @param {Object} object Object whose static property is being exported.
|
|||
|
* @param {string} publicName Unobfuscated name to export.
|
|||
|
* @param {*} symbol Object the name should point to.
|
|||
|
*/
|
|||
|
goog.exportProperty = function(object, publicName, symbol) {
|
|||
|
object[publicName] = symbol;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Inherit the prototype methods from one constructor into another.
|
|||
|
*
|
|||
|
* Usage:
|
|||
|
* <pre>
|
|||
|
* function ParentClass(a, b) { }
|
|||
|
* ParentClass.prototype.foo = function(a) { };
|
|||
|
*
|
|||
|
* function ChildClass(a, b, c) {
|
|||
|
* ChildClass.base(this, 'constructor', a, b);
|
|||
|
* }
|
|||
|
* goog.inherits(ChildClass, ParentClass);
|
|||
|
*
|
|||
|
* var child = new ChildClass('a', 'b', 'see');
|
|||
|
* child.foo(); // This works.
|
|||
|
* </pre>
|
|||
|
*
|
|||
|
* @param {Function} childCtor Child class.
|
|||
|
* @param {Function} parentCtor Parent class.
|
|||
|
*/
|
|||
|
goog.inherits = function(childCtor, parentCtor) {
|
|||
|
/** @constructor */
|
|||
|
function tempCtor() {};
|
|||
|
tempCtor.prototype = parentCtor.prototype;
|
|||
|
childCtor.superClass_ = parentCtor.prototype;
|
|||
|
childCtor.prototype = new tempCtor();
|
|||
|
/** @override */
|
|||
|
childCtor.prototype.constructor = childCtor;
|
|||
|
|
|||
|
/**
|
|||
|
* Calls superclass constructor/method.
|
|||
|
*
|
|||
|
* This function is only available if you use goog.inherits to
|
|||
|
* express inheritance relationships between classes.
|
|||
|
*
|
|||
|
* NOTE: This is a replacement for goog.base and for superClass_
|
|||
|
* property defined in childCtor.
|
|||
|
*
|
|||
|
* @param {!Object} me Should always be "this".
|
|||
|
* @param {string} methodName The method name to call. Calling
|
|||
|
* superclass constructor can be done with the special string
|
|||
|
* 'constructor'.
|
|||
|
* @param {...*} var_args The arguments to pass to superclass
|
|||
|
* method/constructor.
|
|||
|
* @return {*} The return value of the superclass method/constructor.
|
|||
|
*/
|
|||
|
childCtor.base = function(me, methodName, var_args) {
|
|||
|
var args = Array.prototype.slice.call(arguments, 2);
|
|||
|
return parentCtor.prototype[methodName].apply(me, args);
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Call up to the superclass.
|
|||
|
*
|
|||
|
* If this is called from a constructor, then this calls the superclass
|
|||
|
* constructor with arguments 1-N.
|
|||
|
*
|
|||
|
* If this is called from a prototype method, then you must pass the name of the
|
|||
|
* method as the second argument to this function. If you do not, you will get a
|
|||
|
* runtime error. This calls the superclass' method with arguments 2-N.
|
|||
|
*
|
|||
|
* This function only works if you use goog.inherits to express inheritance
|
|||
|
* relationships between your classes.
|
|||
|
*
|
|||
|
* This function is a compiler primitive. At compile-time, the compiler will do
|
|||
|
* macro expansion to remove a lot of the extra overhead that this function
|
|||
|
* introduces. The compiler will also enforce a lot of the assumptions that this
|
|||
|
* function makes, and treat it as a compiler error if you break them.
|
|||
|
*
|
|||
|
* @param {!Object} me Should always be "this".
|
|||
|
* @param {*=} opt_methodName The method name if calling a super method.
|
|||
|
* @param {...*} var_args The rest of the arguments.
|
|||
|
* @return {*} The return value of the superclass method.
|
|||
|
* @suppress {es5Strict} This method can not be used in strict mode, but
|
|||
|
* all Closure Library consumers must depend on this file.
|
|||
|
*/
|
|||
|
goog.base = function(me, opt_methodName, var_args) {
|
|||
|
var caller = arguments.callee.caller;
|
|||
|
|
|||
|
if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) {
|
|||
|
throw Error('arguments.caller not defined. goog.base() cannot be used ' +
|
|||
|
'with strict mode code. See ' +
|
|||
|
'http://www.ecma-international.org/ecma-262/5.1/#sec-C');
|
|||
|
}
|
|||
|
|
|||
|
if (caller.superClass_) {
|
|||
|
// This is a constructor. Call the superclass constructor.
|
|||
|
return caller.superClass_.constructor.apply(
|
|||
|
me, Array.prototype.slice.call(arguments, 1));
|
|||
|
}
|
|||
|
|
|||
|
var args = Array.prototype.slice.call(arguments, 2);
|
|||
|
var foundCaller = false;
|
|||
|
for (var ctor = me.constructor;
|
|||
|
ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
|
|||
|
if (ctor.prototype[opt_methodName] === caller) {
|
|||
|
foundCaller = true;
|
|||
|
} else if (foundCaller) {
|
|||
|
return ctor.prototype[opt_methodName].apply(me, args);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// If we did not find the caller in the prototype chain, then one of two
|
|||
|
// things happened:
|
|||
|
// 1) The caller is an instance method.
|
|||
|
// 2) This method was not called by the right caller.
|
|||
|
if (me[opt_methodName] === caller) {
|
|||
|
return me.constructor.prototype[opt_methodName].apply(me, args);
|
|||
|
} else {
|
|||
|
throw Error(
|
|||
|
'goog.base called from a method of one name ' +
|
|||
|
'to a method of a different name');
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Allow for aliasing within scope functions. This function exists for
|
|||
|
* uncompiled code - in compiled code the calls will be inlined and the aliases
|
|||
|
* applied. In uncompiled code the function is simply run since the aliases as
|
|||
|
* written are valid JavaScript.
|
|||
|
*
|
|||
|
*
|
|||
|
* @param {function()} fn Function to call. This function can contain aliases
|
|||
|
* to namespaces (e.g. "var dom = goog.dom") or classes
|
|||
|
* (e.g. "var Timer = goog.Timer").
|
|||
|
*/
|
|||
|
goog.scope = function(fn) {
|
|||
|
fn.call(goog.global);
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* To support uncompiled, strict mode bundles that use eval to divide source
|
|||
|
* like so:
|
|||
|
* eval('someSource;//# sourceUrl sourcefile.js');
|
|||
|
* We need to export the globally defined symbols "goog" and "COMPILED".
|
|||
|
* Exporting "goog" breaks the compiler optimizations, so we required that
|
|||
|
* be defined externally.
|
|||
|
* NOTE: We don't use goog.exportSymbol here because we don't want to trigger
|
|||
|
* extern generation when that compiler option is enabled.
|
|||
|
*/
|
|||
|
if (!COMPILED) {
|
|||
|
goog.global['COMPILED'] = COMPILED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//==============================================================================
|
|||
|
// goog.defineClass implementation
|
|||
|
//==============================================================================
|
|||
|
|
|||
|
/**
|
|||
|
* Creates a restricted form of a Closure "class":
|
|||
|
* - from the compiler's perspective, the instance returned from the
|
|||
|
* constructor is sealed (no new properties may be added). This enables
|
|||
|
* better checks.
|
|||
|
* - the compiler will rewrite this definition to a form that is optimal
|
|||
|
* for type checking and optimization (initially this will be a more
|
|||
|
* traditional form).
|
|||
|
*
|
|||
|
* @param {Function} superClass The superclass, Object or null.
|
|||
|
* @param {goog.defineClass.ClassDescriptor} def
|
|||
|
* An object literal describing the
|
|||
|
* the class. It may have the following properties:
|
|||
|
* "constructor": the constructor function
|
|||
|
* "statics": an object literal containing methods to add to the constructor
|
|||
|
* as "static" methods or a function that will receive the constructor
|
|||
|
* function as its only parameter to which static properties can
|
|||
|
* be added.
|
|||
|
* all other properties are added to the prototype.
|
|||
|
* @return {!Function} The class constructor.
|
|||
|
*/
|
|||
|
goog.defineClass = function(superClass, def) {
|
|||
|
// TODO(johnlenz): consider making the superClass an optional parameter.
|
|||
|
var constructor = def.constructor;
|
|||
|
var statics = def.statics;
|
|||
|
// Wrap the constructor prior to setting up the prototype and static methods.
|
|||
|
if (!constructor || constructor == Object.prototype.constructor) {
|
|||
|
constructor = function() {
|
|||
|
throw Error('cannot instantiate an interface (no constructor defined).');
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);
|
|||
|
if (superClass) {
|
|||
|
goog.inherits(cls, superClass);
|
|||
|
}
|
|||
|
|
|||
|
// Remove all the properties that should not be copied to the prototype.
|
|||
|
delete def.constructor;
|
|||
|
delete def.statics;
|
|||
|
|
|||
|
goog.defineClass.applyProperties_(cls.prototype, def);
|
|||
|
if (statics != null) {
|
|||
|
if (statics instanceof Function) {
|
|||
|
statics(cls);
|
|||
|
} else {
|
|||
|
goog.defineClass.applyProperties_(cls, statics);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return cls;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @typedef {
|
|||
|
* !Object|
|
|||
|
* {constructor:!Function}|
|
|||
|
* {constructor:!Function, statics:(Object|function(Function):void)}}
|
|||
|
*/
|
|||
|
goog.defineClass.ClassDescriptor;
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* @define {boolean} Whether the instances returned by
|
|||
|
* goog.defineClass should be sealed when possible.
|
|||
|
*/
|
|||
|
goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is
|
|||
|
* defined, this function will wrap the constructor in a function that seals the
|
|||
|
* results of the provided constructor function.
|
|||
|
*
|
|||
|
* @param {!Function} ctr The constructor whose results maybe be sealed.
|
|||
|
* @param {Function} superClass The superclass constructor.
|
|||
|
* @return {!Function} The replacement constructor.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.defineClass.createSealingConstructor_ = function(ctr, superClass) {
|
|||
|
if (goog.defineClass.SEAL_CLASS_INSTANCES &&
|
|||
|
Object.seal instanceof Function) {
|
|||
|
// Don't seal subclasses of unsealable-tagged legacy classes.
|
|||
|
if (superClass && superClass.prototype &&
|
|||
|
superClass.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]) {
|
|||
|
return ctr;
|
|||
|
}
|
|||
|
/** @this {*} */
|
|||
|
var wrappedCtr = function() {
|
|||
|
// Don't seal an instance of a subclass when it calls the constructor of
|
|||
|
// its super class as there is most likely still setup to do.
|
|||
|
var instance = ctr.apply(this, arguments) || this;
|
|||
|
instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];
|
|||
|
if (this.constructor === wrappedCtr) {
|
|||
|
Object.seal(instance);
|
|||
|
}
|
|||
|
return instance;
|
|||
|
};
|
|||
|
return wrappedCtr;
|
|||
|
}
|
|||
|
return ctr;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
// TODO(johnlenz): share these values with the goog.object
|
|||
|
/**
|
|||
|
* The names of the fields that are defined on Object.prototype.
|
|||
|
* @type {!Array.<string>}
|
|||
|
* @private
|
|||
|
* @const
|
|||
|
*/
|
|||
|
goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [
|
|||
|
'constructor',
|
|||
|
'hasOwnProperty',
|
|||
|
'isPrototypeOf',
|
|||
|
'propertyIsEnumerable',
|
|||
|
'toLocaleString',
|
|||
|
'toString',
|
|||
|
'valueOf'
|
|||
|
];
|
|||
|
|
|||
|
|
|||
|
// TODO(johnlenz): share this function with the goog.object
|
|||
|
/**
|
|||
|
* @param {!Object} target The object to add properties to.
|
|||
|
* @param {!Object} source The object to copy properites from.
|
|||
|
* @private
|
|||
|
*/
|
|||
|
goog.defineClass.applyProperties_ = function(target, source) {
|
|||
|
// TODO(johnlenz): update this to support ES5 getters/setters
|
|||
|
|
|||
|
var key;
|
|||
|
for (key in source) {
|
|||
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|||
|
target[key] = source[key];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// For IE the for-in-loop does not contain any properties that are not
|
|||
|
// enumerable on the prototype object (for example isPrototypeOf from
|
|||
|
// Object.prototype) and it will also not include 'replace' on objects that
|
|||
|
// extend String and change 'replace' (not that it is common for anyone to
|
|||
|
// extend anything except Object).
|
|||
|
for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
|
|||
|
key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];
|
|||
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|||
|
target[key] = source[key];
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Sealing classes breaks the older idiom of assigning properties on the
|
|||
|
* prototype rather than in the constructor. As such, goog.defineClass
|
|||
|
* must not seal subclasses of these old-style classes until they are fixed.
|
|||
|
* Until then, this marks a class as "broken", instructing defineClass
|
|||
|
* not to seal subclasses.
|
|||
|
* @param {!Function} ctr The legacy constructor to tag as unsealable.
|
|||
|
*/
|
|||
|
goog.tagUnsealableClass = function(ctr) {
|
|||
|
if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {
|
|||
|
ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Name for unsealable tag property.
|
|||
|
* @const @private {string}
|
|||
|
*/
|
|||
|
goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable';
|
|||
|
|
|||
|
/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
|
|||
|
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"num
|
|||
|
},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new
|
|||
|
},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.par
|
|||
|
//# sourceMappingURL=jquery.min.map
|
|||
|
/*!
|
|||
|
* Bootstrap v3.2.0 (http://getbootstrap.com)
|
|||
|
* Copyright 2011-2014 Twitter, Inc.
|
|||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|||
|
*/
|
|||
|
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(thi
|
|||
|
/* build: `node build.js modules=ALL exclude=gestures,cufon,json minifier=uglifyjs` *//*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */var fabric=fabric||{version:"1.4.11"};typeof exports!="undefined"&&(exports.fabric=fabric),typeof document!="undefined"&&typeof window!="undefined"?(fabric.document=document,fabric.window=window):(fabric.document=require("jsdom").jsdom("<!DOCTYPE html><html><head></head><body></body></html>"),fabric.window=fabric.document.createWindow()),fabric.isTouchSupported="ontouchstart"in fabric.document.documentElement,fabric.isLikelyNode=typeof Buffer!="undefined"&&typeof window=="undefined",fabric.SHARED_ATTRIBUTES=["display","transform","fill","fill-opacity","fill-rule","opacity","stroke","stroke-dasharray","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width"],fabric.DPI=96,function(){function e(e,t){if(!this.__eventListeners[e])return;t?fabric.util.removeFromArray(this.__eventListeners[e],t):this.__eventListeners[e].length=0}function t(e,t){this.__eventListeners||(this.__eventListeners={});if(arguments.length===1)for(var n in e)this.on(n,e[n]);else this.__eventListeners[e]||(this.__eventListeners[e]=[]),this.__eventListeners[e].push(t);return this}function n(t,n){if(!this.__eventListeners)return;if(arguments.length===0)this.__eventListeners={};else if(arguments.length===1&&typeof arguments[0]=="object")for(var r in t)e.call(this,r,t[r]);else e.call(this,t,n);return this}function r(e,t){if(!this.__eventListeners)return;var n=this.__eventListeners[e];if(!n)return;for(var r=0,i=n.length;r<i;r++)n[r].call(this,t||{});return this}fabric.Observable={observe:t,stopObserving:n,fire:r,on:t,off:n,trigger:r}}(),fabric.Collection={add:function(){this._objects.push.apply(this._objects,arguments);for(var e=0,t=arguments.length;e<t;e++)this._onObjectAdded(arguments[e]);return this.renderOnAddRemove&&this.renderAll(),this},insertAt:function(e,t,n){var r=this.getObjects();return n?r[t]=e:r.splice(t,0,e),this._onObjectAdded(e),this.renderOnAddRemove&&this.renderAll(),this},remove:function(){var e=this.getObjects(),t;for(var n=0,r=arguments.length;n<r;n++)t=e.indexOf(arguments[n]),t!==-1&&(e.splice(t,1),this._onObjectRemoved(arguments[n]));return this.renderOnAddRemove&&this.renderAll(),this},forEachObject:function(e,t){var n=this.getObjects(),r=n.length;while(r--)e.call(t,n[r],r,n);return this},getObjects:function(e){return typeof e=="undefined"?this._objects:this._objects.filter(function(t){return t.type===e})},item:function(e){return this.getObjects()[e]},isEmpty:function(){return this.getObjects().length===0},size:function(){return this.getObjects().length},contains:function(e){return this.getObjects().indexOf(e)>-1},complexity:function(){return this.getObjects().reduce(function(e,t){return e+=t.complexity?t.complexity():0,e},0)}},function(e){var t=Math.sqrt,n=Math.atan2,r=Math.PI/180;fabric.util={removeFromArray:function(e,t){var n=e.indexOf(t);return n!==-1&&e.splice(n,1),e},getRandomInt:function(e,t){return Math.floor(Math.random()*(t-e+1))+e},degreesToRadians:function(e){return e*r},radiansToDegrees:function(e){return e/r},rotatePoint:function(e,t,n){var r=Math.sin(n),i=Math.cos(n);e.subtractEquals(t);var s=e.x*i-e.y*r,o=e.x*r+e.y*i;return(new fabric.Point(s,o)).addEquals(t)},transformPoint:function(e,t,n){return n?new fabric.Point(t[0]*e.x+t[1]*e.y,t[2]*e.x+t[3]*e.y):new fabric.Point(t[0]*e.x+t[1]*e.y+t[4],t[2]*e.x+t[3]*e.y+t[5])},invertTransform:function(e){var t=e.slice(),n=1/(e[0]*e[3]-e[1]*e[2]);t=[n*e[3],-n*e[1],-n*e[2],n*e[0],0,0];var r=fabric.util.transformPoint({x:e[4],y:e[5]},t);return t[4]=-r.x,t[5]=-r.y,t},toFixed:function(e,t){return parseFloat(Number(e).toFixed(t))},parseUnit:function(e){var t=/\D{0,2}$/.exec(e),n=parseFloat(e);switch(t[0]){case"mm":return n*fabric.DPI/25.4;case"cm":return n*fabric.DPI/2.54;case"in":return n*fabric.DPI;case"pt":return n*fabric.DPI/72;case"pc":return n*fabric.DPI/72*12;default:return n}},falseFunction:function(){return!1},getKlass:function(e,t){return e=fabric.util.string.camelize(e.charAt(0).toUp
|
|||
|
:"get",onComplete:i})})},loadSVGFromString:function(e,n,r){e=e.trim();var i;if(typeof DOMParser!="undefined"){var s=new DOMParser;s&&s.parseFromString&&(i=s.parseFromString(e,"text/xml"))}else t.window.ActiveXObject&&(i=new ActiveXObject("Microsoft.XMLDOM"),i.async="false",i.loadXML(e.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,"")));t.parseSVGDocument(i.documentElement,function(e,t){n(e,t)},r)},createSVGFontFacesMarkup:function(e){var t="";for(var n=0,r=e.length;n<r;n++){if(e[n].type!=="text"||!e[n].path)continue;t+=["@font-face {","font-family: ",e[n].fontFamily,"; ","src: url('",e[n].path,"')","}"].join("")}return t&&(t=['<style type="text/css">',"<![CDATA[",t,"]]>","</style>"].join("")),t},createSVGRefElementsMarkup:function(e){var t=[];return T(t,e,"backgroundColor"),T(t,e,"overlayColor"),t.join("")}})}(typeof exports!="undefined"?exports:this),fabric.ElementsParser=function(e,t,n,r){this.elements=e,this.callback=t,this.options=n,this.reviver=r},fabric.ElementsParser.prototype.parse=function(){this.instances=new Array(this.elements.length),this.numElements=this.elements.length,this.createObjects()},fabric.ElementsParser.prototype.createObjects=function(){for(var e=0,t=this.elements.length;e<t;e++)(function(e,t){setTimeout(function(){e.createObject(e.elements[t],t)},0)})(this,e)},fabric.ElementsParser.prototype.createObject=function(e,t){var n=fabric[fabric.util.string.capitalize(e.tagName)];if(n&&n.fromElement)try{this._createObject(n,e,t)}catch(r){fabric.log(r)}else this.checkIfDone()},fabric.ElementsParser.prototype._createObject=function(e,t,n){if(e.async)e.fromElement(t,this.createCallback(n,t),this.options);else{var r=e.fromElement(t,this.options);this.resolveGradient(r,"fill"),this.resolveGradient(r,"stroke"),this.reviver&&this.reviver(t,r),this.instances[n]=r,this.checkIfDone()}},fabric.ElementsParser.prototype.createCallback=function(e,t){var n=this;return function(r){n.resolveGradient(r,"fill"),n.resolveGradient(r,"stroke"),n.reviver&&n.reviver(t,r),n.instances[e]=r,n.checkIfDone()}},fabric.ElementsParser.prototype.resolveGradient=function(e,t){var n=e.get(t);if(!/^url\(/.test(n))return;var r=n.slice(5,n.length-1);fabric.gradientDefs[r]&&e.set(t,fabric.Gradient.fromElement(fabric.gradientDefs[r],e))},fabric.ElementsParser.prototype.checkIfDone=function(){--this.numElements===0&&(this.instances=this.instances.filter(function(e){return e!=null}),this.callback(this.instances))},function(e){"use strict";function n(e,t){this.x=e,this.y=t}var t=e.fabric||(e.fabric={});if(t.Point){t.warn("fabric.Point is already defined");return}t.Point=n,n.prototype={constructor:n,add:function(e){return new n(this.x+e.x,this.y+e.y)},addEquals:function(e){return this.x+=e.x,this.y+=e.y,this},scalarAdd:function(e){return new n(this.x+e,this.y+e)},scalarAddEquals:function(e){return this.x+=e,this.y+=e,this},subtract:function(e){return new n(this.x-e.x,this.y-e.y)},subtractEquals:function(e){return this.x-=e.x,this.y-=e.y,this},scalarSubtract:function(e){return new n(this.x-e,this.y-e)},scalarSubtractEquals:function(e){return this.x-=e,this.y-=e,this},multiply:function(e){return new n(this.x*e,this.y*e)},multiplyEquals:function(e){return this.x*=e,this.y*=e,this},divide:function(e){return new n(this.x/e,this.y/e)},divideEquals:function(e){return this.x/=e,this.y/=e,this},eq:function(e){return this.x===e.x&&this.y===e.y},lt:function(e){return this.x<e.x&&this.y<e.y},lte:function(e){return this.x<=e.x&&this.y<=e.y},gt:function(e){return this.x>e.x&&this.y>e.y},gte:function(e){return this.x>=e.x&&this.y>=e.y},lerp:function(e,t){return new n(this.x+(e.x-this.x)*t,this.y+(e.y-this.y)*t)},distanceFrom:function(e){var t=this.x-e.x,n=this.y-e.y;return Math.sqrt(t*t+n*n)},midPointFrom:function(e){return new n(this.x+(e.x-this.x)/2,this.y+(e.y-this.y)/2)},min:function(e){return new n(Math.min(this.x,e.x),Math.min(this.y,e.y))},max:function(e){return new n(Math.max(this.x,e.x),Math.max(this.y,e.y))},toString:function(){return this.x+","+this.y},setXY:function(e,t){this.x=e,this.y=t},setFromPoint:function(e){this.x=e.x,this.y=e.y},swap:functio
|
|||
|
()},onMouseUp:function(){this._finalizeAndAddPath()},_prepareForDrawing:function(e){var t=new fabric.Point(e.x,e.y);this._reset(),this._addPoint(t),this.canvas.contextTop.moveTo(t.x,t.y)},_addPoint:function(e){this._points.push(e)},_reset:function(){this._points.length=0,this._setBrushStyles(),this._setShadow()},_captureDrawingPath:function(e){var t=new fabric.Point(e.x,e.y);this._addPoint(t)},_render:function(){var e=this.canvas.contextTop,t=this.canvas.viewportTransform,n=this._points[0],r=this._points[1];e.save(),e.transform(t[0],t[1],t[2],t[3],t[4],t[5]),e.beginPath(),this._points.length===2&&n.x===r.x&&n.y===r.y&&(n.x-=.5,r.x+=.5),e.moveTo(n.x,n.y);for(var i=1,s=this._points.length;i<s;i++){var o=n.midPointFrom(r);e.quadraticCurveTo(n.x,n.y,o.x,o.y),n=this._points[i],r=this._points[i+1]}e.lineTo(n.x,n.y),e.stroke(),e.restore()},_getSVGPathData:function(){return this.box=this.getPathBoundingBox(this._points),this.convertPointsToSVGPath(this._points,this.box.minX,this.box.minY)},getPathBoundingBox:function(n){var r=[],i=[],s=n[0],o=n[1],u=s;for(var a=1,f=n.length;a<f;a++){var l=s.midPointFrom(o);r.push(u.x),r.push(l.x),i.push(u.y),i.push(l.y),s=n[a],o=n[a+1],u=l}return r.push(s.x),i.push(s.y),{minX:e(r),minY:e(i),maxX:t(r),maxY:t(i)}},convertPointsToSVGPath:function(e,t,n){var r=[],i=new fabric.Point(e[0].x-t,e[0].y-n),s=new fabric.Point(e[1].x-t,e[1].y-n);r.push("M ",e[0].x-t," ",e[0].y-n," ");for(var o=1,u=e.length;o<u;o++){var a=i.midPointFrom(s);r.push("Q ",i.x," ",i.y," ",a.x," ",a.y," "),i=new fabric.Point(e[o].x-t,e[o].y-n),o+1<e.length&&(s=new fabric.Point(e[o+1].x-t,e[o+1].y-n))}return r.push("L ",i.x," ",i.y," "),r},createPath:function(e){var t=new fabric.Path(e);return t.fill=null,t.stroke=this.color,t.strokeWidth=this.width,t.strokeLineCap=this.strokeLineCap,t.strokeLineJoin=this.strokeLineJoin,this.shadow&&(this.shadow.affectStroke=!0,t.setShadow(this.shadow)),t},_finalizeAndAddPath:function(){var e=this.canvas.contextTop;e.closePath();var t=this._getSVGPathData().join("");if(t==="M 0 0 Q 0 0 0 0 L 0 0"){this.canvas.renderAll();return}var n=this.box.minX+(this.box.maxX-this.box.minX)/2,r=this.box.minY+(this.box.maxY-this.box.minY)/2;this.canvas.contextTop.arc(n,r,3,0,Math.PI*2,!1);var i=this.createPath(t);i.set({left:n,top:r,originX:"center",originY:"center"}),this.canvas.add(i),i.setCoords(),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderAll(),this.canvas.fire("path:created",{path:i})}})}(),fabric.CircleBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,initialize:function(e){this.canvas=e,this.points=[]},drawDot:function(e){var t=this.addPoint(e),n=this.canvas.contextTop,r=this.canvas.viewportTransform;n.save(),n.transform(r[0],r[1],r[2],r[3],r[4],r[5]),n.fillStyle=t.fill,n.beginPath(),n.arc(t.x,t.y,t.radius,0,Math.PI*2,!1),n.closePath(),n.fill(),n.restore()},onMouseDown:function(e){this.points.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.drawDot(e)},onMouseMove:function(e){this.drawDot(e)},onMouseUp:function(){var e=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;var t=[];for(var n=0,r=this.points.length;n<r;n++){var i=this.points[n],s=new fabric.Circle({radius:i.radius,left:i.x,top:i.y,originX:"center",originY:"center",fill:i.fill});this.shadow&&s.setShadow(this.shadow),t.push(s)}var o=new fabric.Group(t,{originX:"center",originY:"center"});o.canvas=this.canvas,this.canvas.add(o),this.canvas.fire("path:created",{path:o}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=e,this.canvas.renderAll()},addPoint:function(e){var t=new fabric.Point(e.x,e.y),n=fabric.util.getRandomInt(Math.max(0,this.width-20),this.width+20)/2,r=(new fabric.Color(this.color)).setAlpha(fabric.util.getRandomInt(0,100)/100).toRgba();return t.radius=n,t.fill=r,this.points.push(t),t}}),fabric.SprayBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,density:20,dotWidth:1,dotWidthVariance:1,randomOpacity:!1,optimizeOverlapping:!0,initialize:function(e){this.canvas=e,this.sprayChunks=[]
|
|||
|
.object.extend(fabric.StaticCanvas.prototype,{loadFromDatalessJSON:function(e,t,n){return this.loadFromJSON(e,t,n)},loadFromJSON:function(e,t,n){if(!e)return;var r=typeof e=="string"?JSON.parse(e):e;this.clear();var i=this;return this._enlivenObjects(r.objects,function(){i._setBgOverlay(r,t)},n),this},_setBgOverlay:function(e,t){var n=this,r={backgroundColor:!1,overlayColor:!1,backgroundImage:!1,overlayImage:!1};if(!e.backgroundImage&&!e.overlayImage&&!e.background&&!e.overlay){t&&t();return}var i=function(){r.backgroundImage&&r.overlayImage&&r.backgroundColor&&r.overlayColor&&(n.renderAll(),t&&t())};this.__setBgOverlay("backgroundImage",e.backgroundImage,r,i),this.__setBgOverlay("overlayImage",e.overlayImage,r,i),this.__setBgOverlay("backgroundColor",e.background,r,i),this.__setBgOverlay("overlayColor",e.overlay,r,i),i()},__setBgOverlay:function(e,t,n,r){var i=this;if(!t){n[e]=!0;return}e==="backgroundImage"||e==="overlayImage"?fabric.Image.fromObject(t,function(t){i[e]=t,n[e]=!0,r&&r()}):this["set"+fabric.util.string.capitalize(e,!0)](t,function(){n[e]=!0,r&&r()})},_enlivenObjects:function(e,t,n){var r=this;if(!e||e.length===0){t&&t();return}var i=this.renderOnAddRemove;this.renderOnAddRemove=!1,fabric.util.enlivenObjects(e,function(e){e.forEach(function(e,t){r.insertAt(e,t,!0)}),r.renderOnAddRemove=i,t&&t()},null,n)},_toDataURL:function(e,t){this.clone(function(n){t(n.toDataURL(e))})},_toDataURLWithMultiplier:function(e,t,n){this.clone(function(r){n(r.toDataURLWithMultiplier(e,t))})},clone:function(e,t){var n=JSON.stringify(this.toJSON(t));this.cloneWithoutData(function(t){t.loadFromJSON(n,function(){e&&e(t)})})},cloneWithoutData:function(e){var t=fabric.document.createElement("canvas");t.width=this.getWidth(),t.height=this.getHeight();var n=new fabric.Canvas(t);n.clipTo=this.clipTo,this.backgroundImage?(n.setBackgroundImage(this.backgroundImage.src,function(){n.renderAll(),e&&e(n)}),n.backgroundImageOpacity=this.backgroundImageOpacity,n.backgroundImageStretch=this.backgroundImageStretch):e&&e(n)}}),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend,r=t.util.toFixed,i=t.util.string.capitalize,s=t.util.degreesToRadians,o=t.StaticCanvas.supports("setLineDash");if(t.Object)return;t.Object=t.util.createClass({type:"object",originX:"left",originY:"top",top:0,left:0,width:0,height:0,scaleX:1,scaleY:1,flipX:!1,flipY:!1,opacity:1,angle:0,cornerSize:12,transparentCorners:!0,hoverCursor:null,padding:0,borderColor:"rgba(102,153,255,0.75)",cornerColor:"rgba(102,153,255,0.5)",centeredScaling:!1,centeredRotation:!0,fill:"rgb(0,0,0)",fillRule:"source-over",backgroundColor:"",stroke:null,strokeWidth:1,strokeDashArray:null,strokeLineCap:"butt",strokeLineJoin:"miter",strokeMiterLimit:10,shadow:null,borderOpacityWhenMoving:.4,borderScaleFactor:1,transformMatrix:null,minScaleLimit:.01,selectable:!0,evented:!0,visible:!0,hasControls:!0,hasBorders:!0,hasRotatingPoint:!0,rotatingPointOffset:40,perPixelTargetFind:!1,includeDefaultValues:!0,clipTo:null,lockMovementX:!1,lockMovementY:!1,lockRotation:!1,lockScalingX:!1,lockScalingY:!1,lockUniScaling:!1,lockScalingFlip:!1,stateProperties:"top left width height scaleX scaleY flipX flipY originX originY transformMatrix stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit angle opacity fill fillRule shadow clipTo visible backgroundColor".split(" "),initialize:function(e){e&&this.setOptions(e)},_initGradient:function(e){e.fill&&e.fill.colorStops&&!(e.fill instanceof t.Gradient)&&this.set("fill",new t.Gradient(e.fill))},_initPattern:function(e){e.fill&&e.fill.source&&!(e.fill instanceof t.Pattern)&&this.set("fill",new t.Pattern(e.fill)),e.stroke&&e.stroke.source&&!(e.stroke instanceof t.Pattern)&&this.set("stroke",new t.Pattern(e.stroke))},_initClipping:function(e){if(!e.clipTo||typeof e.clipTo!="string")return;var n=t.util.getFunctionBody(e.clipTo);typeof n!="undefined"&&(this.clipTo=new Function("ctx",n))},setOptions:function(e){for(var t in e)this.set(t,e[t]);this._initGradient(e),this._initPattern(e),this._initClipping(e)},transform:function
|
|||
|
this.radius),t.push("<circle ",'cx="'+n+'" cy="'+r+'" ','r="',this.radius,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),e?e(t.join("")):t.join("")},_render:function(e,t){e.beginPath(),e.arc(t?this.left+this.radius:0,t?this.top+this.radius:0,this.radius,0,n,!1),this._renderFill(e),this._renderStroke(e)},getRadiusX:function(){return this.get("radius")*this.get("scaleX")},getRadiusY:function(){return this.get("radius")*this.get("scaleY")},setRadius:function(e){this.radius=e,this.set("width",e*2).set("height",e*2)},complexity:function(){return 1}}),t.Circle.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("cx cy r".split(" ")),t.Circle.fromElement=function(e,n){n||(n={});var s=t.parseAttributes(e,t.Circle.ATTRIBUTE_NAMES);if(!i(s))throw new Error("value of `r` attribute is required and can not be negative");s.left=s.left||0,s.top=s.top||0;var o=new t.Circle(r(s,n));return o.left-=o.radius,o.top-=o.radius,o},t.Circle.fromObject=function(e){return new t.Circle(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={});if(t.Triangle){t.warn("fabric.Triangle is already defined");return}t.Triangle=t.util.createClass(t.Object,{type:"triangle",initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("width",e.width||100).set("height",e.height||100)},_render:function(e){var t=this.width/2,n=this.height/2;e.beginPath(),e.moveTo(-t,n),e.lineTo(0,-n),e.lineTo(t,n),e.closePath(),this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var n=this.width/2,r=this.height/2;e.beginPath(),t.util.drawDashedLine(e,-n,r,0,-r,this.strokeDashArray),t.util.drawDashedLine(e,0,-r,n,r,this.strokeDashArray),t.util.drawDashedLine(e,n,r,-n,r,this.strokeDashArray),e.closePath()},toSVG:function(e){var t=this._createBaseSVGMarkup(),n=this.width/2,r=this.height/2,i=[-n+" "+r,"0 "+ -r,n+" "+r].join(",");return t.push("<polygon ",'points="',i,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),'"/>'),e?e(t.join("")):t.join("")},complexity:function(){return 1}}),t.Triangle.fromObject=function(e){return new t.Triangle(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=Math.PI*2,r=t.util.object.extend;if(t.Ellipse){t.warn("fabric.Ellipse is already defined.");return}t.Ellipse=t.util.createClass(t.Object,{type:"ellipse",rx:0,ry:0,initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("rx",e.rx||0),this.set("ry",e.ry||0),this.set("width",this.get("rx")*2),this.set("height",this.get("ry")*2)},toObject:function(e){return r(this.callSuper("toObject",e),{rx:this.get("rx"),ry:this.get("ry")})},toSVG:function(e){var t=this._createBaseSVGMarkup(),n=0,r=0;return this.group&&(n=this.left+this.rx,r=this.top+this.ry),t.push("<ellipse ",'cx="',n,'" cy="',r,'" ','rx="',this.rx,'" ry="',this.ry,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'"/>\n'),e?e(t.join("")):t.join("")},_render:function(e,t){e.beginPath(),e.save(),e.transform(1,0,0,this.ry/this.rx,0,0),e.arc(t?this.left+this.rx:0,t?(this.top+this.ry)*this.rx/this.ry:0,this.rx,0,n,!1),e.restore(),this._renderFill(e),this._renderStroke(e)},complexity:function(){return 1}}),t.Ellipse.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("cx cy rx ry".split(" ")),t.Ellipse.fromElement=function(e,n){n||(n={});var i=t.parseAttributes(e,t.Ellipse.ATTRIBUTE_NAMES);i.left=i.left||0,i.top=i.top||0;var s=new t.Ellipse(r(i,n));return s.top-=s.ry,s.left-=s.rx,s},t.Ellipse.fromObject=function(e){return new t.Ellipse(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;if(t.Rect){console.warn("fabric.Rect is already defined");return}var r=t.Object.prototype.stateProperties.concat();r.push("rx","ry","x","y"),t.Rect=t.util.createClass(t.Object,{stateProperties:r,type:"rect",rx:0,ry:0,strokeDashArray:null,initialize:function(e){e=e||{},this.callSuper("initialize",e),this._initRxRy()},_initRxRy:function(){th
|
|||
|
e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.Mask=t.util.createClass(t.Image.filters.BaseFilter,{type:"Mask",initialize:function(e){e=e||{},this.mask=e.mask,this.channel=[0,1,2,3].indexOf(e.channel)>-1?e.channel:0},applyTo:function(e){if(!this.mask)return;var n=e.getContext("2d"),r=n.getImageData(0,0,e.width,e.height),i=r.data,s=this.mask.getElement(),o=t.util.createCanvasElement(),u=this.channel,a,f=r.width*r.height*4;o.width=s.width,o.height=s.height,o.getContext("2d").drawImage(s,0,0,s.width,s.height);var l=o.getContext("2d").getImageData(0,0,s.width,s.height),c=l.data;for(a=0;a<f;a+=4)i[a+3]=c[a+u];n.putImageData(r,0,0)},toObject:function(){return n(this.callSuper("toObject"),{mask:this.mask.toObject(),channel:this.channel})}}),t.Image.filters.Mask.fromObject=function(e,n){t.util.loadImage(e.mask.src,function(r){e.mask=new t.Image(r,e.mask),n&&n(new t.Image.filters.Mask(e))})},t.Image.filters.Mask.async=!0}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.Noise=t.util.createClass(t.Image.filters.BaseFilter,{type:"Noise",initialize:function(e){e=e||{},this.noise=e.noise||0},applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=this.noise,s;for(var o=0,u=r.length;o<u;o+=4)s=(.5-Math.random())*i,r[o]+=s,r[o+1]+=s,r[o+2]+=s;t.putImageData(n,0,0)},toObject:function(){return n(this.callSuper("toObject"),{noise:this.noise})}}),t.Image.filters.Noise.fromObject=function(e){return new t.Image.filters.Noise(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.Pixelate=t.util.createClass(t.Image.filters.BaseFilter,{type:"Pixelate",initialize:function(e){e=e||{},this.blocksize=e.blocksize||4},applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=n.height,s=n.width,o,u,a,f,l,c,h;for(u=0;u<i;u+=this.blocksize)for(a=0;a<s;a+=this.blocksize){o=u*4*s+a*4,f=r[o],l=r[o+1],c=r[o+2],h=r[o+3];for(var p=u,d=u+this.blocksize;p<d;p++)for(var v=a,m=a+this.blocksize;v<m;v++)o=p*4*s+v*4,r[o]=f,r[o+1]=l,r[o+2]=c,r[o+3]=h}t.putImageData(n,0,0)},toObject:function(){return n(this.callSuper("toObject"),{blocksize:this.blocksize})}}),t.Image.filters.Pixelate.fromObject=function(e){return new t.Image.filters.Pixelate(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.RemoveWhite=t.util.createClass(t.Image.filters.BaseFilter,{type:"RemoveWhite",initialize:function(e){e=e||{},this.threshold=e.threshold||30,this.distance=e.distance||20},applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=this.threshold,s=this.distance,o=255-i,u=Math.abs,a,f,l;for(var c=0,h=r.length;c<h;c+=4)a=r[c],f=r[c+1],l=r[c+2],a>o&&f>o&&l>o&&u(a-f)<s&&u(a-l)<s&&u(f-l)<s&&(r[c+3]=1);t.putImageData(n,0,0)},toObject:function(){return n(this.callSuper("toObject"),{threshold:this.threshold,distance:this.distance})}}),t.Image.filters.RemoveWhite.fromObject=function(e){return new t.Image.filters.RemoveWhite(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={});t.Image.filters.Sepia=t.util.createClass(t.Image.filters.BaseFilter,{type:"Sepia",applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=r.length,s,o;for(s=0;s<i;s+=4)o=.3*r[s]+.59*r[s+1]+.11*r[s+2],r[s]=o+100,r[s+1]=o+50,r[s+2]=o+255;t.putImageData(n,0,0)}}),t.Image.filters.Sepia.fromObject=function(){return new t.Image.filters.Sepia}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={});t.Image.filters.Sepia2=t.util.createClass(t.Image.filters.BaseFilter,{type:"Sepia2",applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=r.length,s,o,u,a;for(s=0;s<i;s+=4)o=r[s],u=r[s+1],a=r[s+2],r[s]=(o*.393+u*.769+a*.189)/1.351,r[s+1]=(o*.349+u*.686+a*.168)/1.203,r[s+2]=(o*.27
|
|||
|
n))&&n>-1)t++,n--;return e-t},findWordBoundaryRight:function(e){var t=0,n=e;if(this._reSpace.test(this.text.charAt(n)))while(this._reSpace.test(this.text.charAt(n)))t++,n++;while(/\S/.test(this.text.charAt(n))&&n<this.text.length)t++,n++;return e+t},findLineBoundaryLeft:function(e){var t=0,n=e-1;while(!/\n/.test(this.text.charAt(n))&&n>-1)t++,n--;return e-t},findLineBoundaryRight:function(e){var t=0,n=e;while(!/\n/.test(this.text.charAt(n))&&n<this.text.length)t++,n++;return e+t},getNumNewLinesInSelectedText:function(){var e=this.getSelectedText(),t=0;for(var n=0,r=e.split(""),i=r.length;n<i;n++)r[n]==="\n"&&t++;return t},searchWordBoundary:function(e,t){var n=this._reSpace.test(this.text.charAt(e))?e-1:e,r=this.text.charAt(n),i=/[ \n\.,;!\?\-]/;while(!i.test(r)&&n>0&&n<this.text.length)n+=t,r=this.text.charAt(n);return i.test(r)&&r!=="\n"&&(n+=t===1?0:1),n},selectWord:function(e){var t=this.searchWordBoundary(e,-1),n=this.searchWordBoundary(e,1);this.setSelectionStart(t),this.setSelectionEnd(n),this.initDelayedCursor(!0)},selectLine:function(e){var t=this.findLineBoundaryLeft(e),n=this.findLineBoundaryRight(e);this.setSelectionStart(t),this.setSelectionEnd(n),this.initDelayedCursor(!0)},enterEditing:function(){if(this.isEditing||!this.editable)return;return this.exitEditingOnOthers(),this.isEditing=!0,this.initHiddenTextarea(),this._updateTextarea(),this._saveEditingProps(),this._setEditingProps(),this._tick(),this.canvas&&this.canvas.renderAll(),this.fire("editing:entered"),this.canvas&&this.canvas.fire("text:editing:entered",{target:this}),this},exitEditingOnOthers:function(){fabric.IText.instances.forEach(function(e){e.selected=!1,e.isEditing&&e.exitEditing()},this)},_setEditingProps:function(){this.hoverCursor="text",this.canvas&&(this.canvas.defaultCursor=this.canvas.moveCursor="text"),this.borderColor=this.editingBorderColor,this.hasControls=this.selectable=!1,this.lockMovementX=this.lockMovementY=!0},_updateTextarea:function(){if(!this.hiddenTextarea)return;this.hiddenTextarea.value=this.text,this.hiddenTextarea.selectionStart=this.selectionStart},_saveEditingProps:function(){this._savedProps={hasControls:this.hasControls,borderColor:this.borderColor,lockMovementX:this.lockMovementX,lockMovementY:this.lockMovementY,hoverCursor:this.hoverCursor,defaultCursor:this.canvas&&this.canvas.defaultCursor,moveCursor:this.canvas&&this.canvas.moveCursor}},_restoreEditingProps:function(){if(!this._savedProps)return;this.hoverCursor=this._savedProps.overCursor,this.hasControls=this._savedProps.hasControls,this.borderColor=this._savedProps.borderColor,this.lockMovementX=this._savedProps.lockMovementX,this.lockMovementY=this._savedProps.lockMovementY,this.canvas&&(this.canvas.defaultCursor=this._savedProps.defaultCursor,this.canvas.moveCursor=this._savedProps.moveCursor)},exitEditing:function(){return this.selected=!1,this.isEditing=!1,this.selectable=!0,this.selectionEnd=this.selectionStart,this.hiddenTextarea&&this.canvas&&this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea),this.hiddenTextarea=null,this.abortCursorAnimation(),this._restoreEditingProps(),this._currentCursorOpacity=0,this.fire("editing:exited"),this.canvas&&this.canvas.fire("text:editing:exited",{target:this}),this},_removeExtraneousStyles:function(){var e=this.text.split(this._reNewline);for(var t in this.styles)e[t]||delete this.styles[t]},_removeCharsFromTo:function(e,t){var n=t;while(n!==e){var r=this.get2DCursorLocation(n).charIndex;n--;var i=this.get2DCursorLocation(n).charIndex,s=i>r;s?this.removeStyleObject(s,n+1):this.removeStyleObject(this.get2DCursorLocation(n).charIndex===0,n)}this.text=this.text.slice(0,e)+this.text.slice(t)},insertChars:function(e){var t=this.text.slice(this.selectionStart,this.selectionStart+1)==="\n";this.text=this.text.slice(0,this.selectionStart)+e+this.text.slice(this.selectionEnd),this.selectionStart===this.selectionEnd&&this.insertStyleObjects(e,t,this.copiedStyles),this.selectionStart+=e.length,this.selectionEnd=this.selectionStart,this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("cha
|
|||
|
/*!
|
|||
|
* Bootstrap-select v1.6.2 (http://silviomoreto.github.io/bootstrap-select/)
|
|||
|
*
|
|||
|
* Copyright 2013-2014 bootstrap-select
|
|||
|
* Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
|
|||
|
*/
|
|||
|
!function(a){"use strict";function b(b,d){var e=arguments,b=e[0],d=e[1];[].shift.apply(e);var f,g=this.each(function(){var g=a(this);if(g.is("select")){var h=g.data("selectpicker"),i="object"==typeof b&&b;if(h){if(i)for(var j in i)i.hasOwnProperty(j)&&(h.options[j]=i[j])}else{var k=a.extend({},c.DEFAULTS,a.fn.selectpicker.defaults,g.data(),i);g.data("selectpicker",h=new c(this,k,d))}"string"==typeof b&&(f=h[b]instanceof Function?h[b].apply(h,e):h.options[b])}});return"undefined"!=typeof f?f:g}a.expr[":"].icontains=function(b,c,d){return a(b).text().toUpperCase().indexOf(d[3].toUpperCase())>=0};var c=function(b,d,e){e&&(e.stopPropagation(),e.preventDefault()),this.$element=a(b),this.$newElement=null,this.$button=null,this.$menu=null,this.$lis=null,this.options=d,null===this.options.title&&(this.options.title=this.$element.attr("title")),this.val=c.prototype.val,this.render=c.prototype.render,this.refresh=c.prototype.refresh,this.setStyle=c.prototype.setStyle,this.selectAll=c.prototype.selectAll,this.deselectAll=c.prototype.deselectAll,this.destroy=c.prototype.remove,this.remove=c.prototype.remove,this.show=c.prototype.show,this.hide=c.prototype.hide,this.init()};c.VERSION="1.6.2",c.DEFAULTS={noneSelectedText:"Nothing selected",noneResultsText:"No results match",countSelectedText:"{0} of {1} selected",maxOptionsText:["Limit reached ({n} {var} max)","Group limit reached ({n} {var} max)",["items","item"]],multipleSeparator:", ",style:"btn-default",size:"auto",title:null,selectedTextFormat:"values",width:!1,container:!1,hideDisabled:!1,showSubtext:!1,showIcon:!0,showContent:!0,dropupAuto:!0,header:!1,liveSearch:!1,actionsBox:!1,iconBase:"glyphicon",tickIcon:"glyphicon-ok",maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1},c.prototype={constructor:c,init:function(){var b=this,c=this.$element.attr("id");this.$element.hide(),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$newElement=this.createView(),this.$element.after(this.$newElement),this.$menu=this.$newElement.find("> .dropdown-menu"),this.$button=this.$newElement.find("> button"),this.$searchbox=this.$newElement.find("input"),this.options.dropdownAlignRight&&this.$menu.addClass("pull-right"),"undefined"!=typeof c&&(this.$button.attr("data-id",c),a('label[for="'+c+'"]').click(function(a){a.preventDefault(),b.$button.focus()})),this.checkDisabled(),this.clickListener(),this.options.liveSearch&&this.liveSearchListener(),this.render(),this.liHeight(),this.setStyle(),this.setWidth(),this.options.container&&this.selectPosition(),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile()},createDropdown:function(){var b=this.multiple?" show-tick":"",c=this.$element.parent().hasClass("input-group")?" input-group-btn":"",d=this.autofocus?" autofocus":"",e=this.options.header?'<div class="popover-title"><button type="button" class="close" aria-hidden="true">×</button>'+this.options.header+"</div>":"",f=this.options.liveSearch?'<div class="bootstrap-select-searchbox"><input type="text" class="input-block-level form-control" autocomplete="off" /></div>':"",g=this.options.actionsBox?'<div class="bs-actionsbox"><div class="btn-group btn-block"><button class="actions-btn bs-select-all btn btn-sm btn-default">Select All</button><button class="actions-btn bs-deselect-all btn btn-sm btn-default">Deselect All</button></div></div>':"",h='<div class="btn-group bootstrap-select'+b+c+'"><button type="button" class="btn dropdown-toggle selectpicker" data-toggle="dropdown"'+d+'><span class="filter-option pull-left"></span> <span class="caret"></span></button><div class="dropdown-menu open">'+e+f+g+'<ul class="dropdown-menu inner selectpicker" role="menu"></ul></div></div>';return a(h)},createView:function(){var a=this.createDropdown(),b=this.createLi();return a.find("ul").append(b),a},reloadLi:function(){this.destroyLi();var a=this.createLi();this.$menu.find("ul").append(a)},destroyLi:function(){this.$menu.find("li").remove()},createLi:function(){var b=this,c=[],d="",e=0;return this.$el
|
|||
|
//# sourceMappingURL=bootstrap-select.js.map
|
|||
|
// TinyColor v1.0.0
|
|||
|
// https://github.com/bgrins/TinyColor
|
|||
|
// 2014-06-13, Brian Grinstead, MIT License
|
|||
|
|
|||
|
(function() {
|
|||
|
|
|||
|
var trimLeft = /^[\s,#]+/,
|
|||
|
trimRight = /\s+$/,
|
|||
|
tinyCounter = 0,
|
|||
|
math = Math,
|
|||
|
mathRound = math.round,
|
|||
|
mathMin = math.min,
|
|||
|
mathMax = math.max,
|
|||
|
mathRandom = math.random;
|
|||
|
|
|||
|
var tinycolor = function tinycolor (color, opts) {
|
|||
|
|
|||
|
color = (color) ? color : '';
|
|||
|
opts = opts || { };
|
|||
|
|
|||
|
// If input is already a tinycolor, return itself
|
|||
|
if (color instanceof tinycolor) {
|
|||
|
return color;
|
|||
|
}
|
|||
|
// If we are called as a function, call using new instead
|
|||
|
if (!(this instanceof tinycolor)) {
|
|||
|
return new tinycolor(color, opts);
|
|||
|
}
|
|||
|
|
|||
|
var rgb = inputToRGB(color);
|
|||
|
this._r = rgb.r,
|
|||
|
this._g = rgb.g,
|
|||
|
this._b = rgb.b,
|
|||
|
this._a = rgb.a,
|
|||
|
this._roundA = mathRound(100*this._a) / 100,
|
|||
|
this._format = opts.format || rgb.format;
|
|||
|
this._gradientType = opts.gradientType;
|
|||
|
|
|||
|
// Don't let the range of [0,255] come back in [0,1].
|
|||
|
// Potentially lose a little bit of precision here, but will fix issues where
|
|||
|
// .5 gets interpreted as half of the total, instead of half of 1
|
|||
|
// If it was supposed to be 128, this was already taken care of by `inputToRgb`
|
|||
|
if (this._r < 1) { this._r = mathRound(this._r); }
|
|||
|
if (this._g < 1) { this._g = mathRound(this._g); }
|
|||
|
if (this._b < 1) { this._b = mathRound(this._b); }
|
|||
|
|
|||
|
this._ok = rgb.ok;
|
|||
|
this._tc_id = tinyCounter++;
|
|||
|
};
|
|||
|
|
|||
|
tinycolor.prototype = {
|
|||
|
isDark: function() {
|
|||
|
return this.getBrightness() < 128;
|
|||
|
},
|
|||
|
isLight: function() {
|
|||
|
return !this.isDark();
|
|||
|
},
|
|||
|
isValid: function() {
|
|||
|
return this._ok;
|
|||
|
},
|
|||
|
getFormat: function() {
|
|||
|
return this._format;
|
|||
|
},
|
|||
|
getAlpha: function() {
|
|||
|
return this._a;
|
|||
|
},
|
|||
|
getBrightness: function() {
|
|||
|
var rgb = this.toRgb();
|
|||
|
return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
|||
|
},
|
|||
|
setAlpha: function(value) {
|
|||
|
this._a = boundAlpha(value);
|
|||
|
this._roundA = mathRound(100*this._a) / 100;
|
|||
|
return this;
|
|||
|
},
|
|||
|
toHsv: function() {
|
|||
|
var hsv = rgbToHsv(this._r, this._g, this._b);
|
|||
|
return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
|
|||
|
},
|
|||
|
toHsvString: function() {
|
|||
|
var hsv = rgbToHsv(this._r, this._g, this._b);
|
|||
|
var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
|
|||
|
return (this._a == 1) ?
|
|||
|
"hsv(" + h + ", " + s + "%, " + v + "%)" :
|
|||
|
"hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
|
|||
|
},
|
|||
|
toHsl: function() {
|
|||
|
var hsl = rgbToHsl(this._r, this._g, this._b);
|
|||
|
return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
|
|||
|
},
|
|||
|
toHslString: function() {
|
|||
|
var hsl = rgbToHsl(this._r, this._g, this._b);
|
|||
|
var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
|
|||
|
return (this._a == 1) ?
|
|||
|
"hsl(" + h + ", " + s + "%, " + l + "%)" :
|
|||
|
"hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
|
|||
|
},
|
|||
|
toHex: function(allow3Char) {
|
|||
|
return rgbToHex(this._r, this._g, this._b, allow3Char);
|
|||
|
},
|
|||
|
toHexString: function(allow3Char) {
|
|||
|
return '#' + this.toHex(allow3Char);
|
|||
|
},
|
|||
|
toHex8: function() {
|
|||
|
return rgbaToHex(this._r, this._g, this._b, this._a);
|
|||
|
},
|
|||
|
toHex8String: function() {
|
|||
|
return '#' + this.toHex8();
|
|||
|
},
|
|||
|
toRgb: function() {
|
|||
|
return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
|
|||
|
},
|
|||
|
toRgbString: function() {
|
|||
|
return (this._a == 1) ?
|
|||
|
"rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
|
|||
|
"rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
|
|||
|
},
|
|||
|
toPercentageRgb: function() {
|
|||
|
return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
|
|||
|
},
|
|||
|
toPercentageRgbString: function() {
|
|||
|
return (this._a == 1) ?
|
|||
|
"rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
|
|||
|
"rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
|
|||
|
},
|
|||
|
toName: function() {
|
|||
|
if (this._a === 0) {
|
|||
|
return "transparent";
|
|||
|
}
|
|||
|
|
|||
|
if (this._a < 1) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
|
|||
|
},
|
|||
|
toFilter: function(secondColor) {
|
|||
|
var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
|
|||
|
var secondHex8String = hex8String;
|
|||
|
var gradientType = this._gradientType ? "GradientType = 1, " : "";
|
|||
|
|
|||
|
if (secondColor) {
|
|||
|
var s = tinycolor(secondColor);
|
|||
|
secondHex8String = s.toHex8String();
|
|||
|
}
|
|||
|
|
|||
|
return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
|
|||
|
},
|
|||
|
toString: function(format) {
|
|||
|
var formatSet = !!format;
|
|||
|
format = format || this._format;
|
|||
|
|
|||
|
var formattedString = false;
|
|||
|
var hasAlpha = this._a < 1 && this._a >= 0;
|
|||
|
var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
|
|||
|
|
|||
|
if (needsAlphaFormat) {
|
|||
|
// Special case for "transparent", all other non-alpha formats
|
|||
|
// will return rgba when there is transparency.
|
|||
|
if (format === "name" && this._a === 0) {
|
|||
|
return this.toName();
|
|||
|
}
|
|||
|
return this.toRgbString();
|
|||
|
}
|
|||
|
if (format === "rgb") {
|
|||
|
formattedString = this.toRgbString();
|
|||
|
}
|
|||
|
if (format === "prgb") {
|
|||
|
formattedString = this.toPercentageRgbString();
|
|||
|
}
|
|||
|
if (format === "hex" || format === "hex6") {
|
|||
|
formattedString = this.toHexString();
|
|||
|
}
|
|||
|
if (format === "hex3") {
|
|||
|
formattedString = this.toHexString(true);
|
|||
|
}
|
|||
|
if (format === "hex8") {
|
|||
|
formattedString = this.toHex8String();
|
|||
|
}
|
|||
|
if (format === "name") {
|
|||
|
formattedString = this.toName();
|
|||
|
}
|
|||
|
if (format === "hsl") {
|
|||
|
formattedString = this.toHslString();
|
|||
|
}
|
|||
|
if (format === "hsv") {
|
|||
|
formattedString = this.toHsvString();
|
|||
|
}
|
|||
|
|
|||
|
return formattedString || this.toHexString();
|
|||
|
},
|
|||
|
|
|||
|
_applyModification: function(fn, args) {
|
|||
|
var color = fn.apply(null, [this].concat([].slice.call(args)));
|
|||
|
this._r = color._r;
|
|||
|
this._g = color._g;
|
|||
|
this._b = color._b;
|
|||
|
this.setAlpha(color._a);
|
|||
|
return this;
|
|||
|
},
|
|||
|
lighten: function() {
|
|||
|
return this._applyModification(lighten, arguments);
|
|||
|
},
|
|||
|
brighten: function() {
|
|||
|
return this._applyModification(brighten, arguments);
|
|||
|
},
|
|||
|
darken: function() {
|
|||
|
return this._applyModification(darken, arguments);
|
|||
|
},
|
|||
|
desaturate: function() {
|
|||
|
return this._applyModification(desaturate, arguments);
|
|||
|
},
|
|||
|
saturate: function() {
|
|||
|
return this._applyModification(saturate, arguments);
|
|||
|
},
|
|||
|
greyscale: function() {
|
|||
|
return this._applyModification(greyscale, arguments);
|
|||
|
},
|
|||
|
spin: function() {
|
|||
|
return this._applyModification(spin, arguments);
|
|||
|
},
|
|||
|
|
|||
|
_applyCombination: function(fn, args) {
|
|||
|
return fn.apply(null, [this].concat([].slice.call(args)));
|
|||
|
},
|
|||
|
analogous: function() {
|
|||
|
return this._applyCombination(analogous, arguments);
|
|||
|
},
|
|||
|
complement: function() {
|
|||
|
return this._applyCombination(complement, arguments);
|
|||
|
},
|
|||
|
monochromatic: function() {
|
|||
|
return this._applyCombination(monochromatic, arguments);
|
|||
|
},
|
|||
|
splitcomplement: function() {
|
|||
|
return this._applyCombination(splitcomplement, arguments);
|
|||
|
},
|
|||
|
triad: function() {
|
|||
|
return this._applyCombination(triad, arguments);
|
|||
|
},
|
|||
|
tetrad: function() {
|
|||
|
return this._applyCombination(tetrad, arguments);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// If input is an object, force 1 into "1.0" to handle ratios properly
|
|||
|
// String input requires "1.0" as input, so 1 will be treated as 1
|
|||
|
tinycolor.fromRatio = function(color, opts) {
|
|||
|
if (typeof color == "object") {
|
|||
|
var newColor = {};
|
|||
|
for (var i in color) {
|
|||
|
if (color.hasOwnProperty(i)) {
|
|||
|
if (i === "a") {
|
|||
|
newColor[i] = color[i];
|
|||
|
}
|
|||
|
else {
|
|||
|
newColor[i] = convertToPercentage(color[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
color = newColor;
|
|||
|
}
|
|||
|
|
|||
|
return tinycolor(color, opts);
|
|||
|
};
|
|||
|
|
|||
|
// Given a string or object, convert that input to RGB
|
|||
|
// Possible string inputs:
|
|||
|
//
|
|||
|
// "red"
|
|||
|
// "#f00" or "f00"
|
|||
|
// "#ff0000" or "ff0000"
|
|||
|
// "#ff000000" or "ff000000"
|
|||
|
// "rgb 255 0 0" or "rgb (255, 0, 0)"
|
|||
|
// "rgb 1.0 0 0" or "rgb (1, 0, 0)"
|
|||
|
// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
|
|||
|
// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
|
|||
|
// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
|
|||
|
// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
|
|||
|
// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
|
|||
|
//
|
|||
|
function inputToRGB(color) {
|
|||
|
|
|||
|
var rgb = { r: 0, g: 0, b: 0 };
|
|||
|
var a = 1;
|
|||
|
var ok = false;
|
|||
|
var format = false;
|
|||
|
|
|||
|
if (typeof color == "string") {
|
|||
|
color = stringInputToObject(color);
|
|||
|
}
|
|||
|
|
|||
|
if (typeof color == "object") {
|
|||
|
if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
|
|||
|
rgb = rgbToRgb(color.r, color.g, color.b);
|
|||
|
ok = true;
|
|||
|
format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
|
|||
|
}
|
|||
|
else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
|
|||
|
color.s = convertToPercentage(color.s);
|
|||
|
color.v = convertToPercentage(color.v);
|
|||
|
rgb = hsvToRgb(color.h, color.s, color.v);
|
|||
|
ok = true;
|
|||
|
format = "hsv";
|
|||
|
}
|
|||
|
else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
|
|||
|
color.s = convertToPercentage(color.s);
|
|||
|
color.l = convertToPercentage(color.l);
|
|||
|
rgb = hslToRgb(color.h, color.s, color.l);
|
|||
|
ok = true;
|
|||
|
format = "hsl";
|
|||
|
}
|
|||
|
|
|||
|
if (color.hasOwnProperty("a")) {
|
|||
|
a = color.a;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
a = boundAlpha(a);
|
|||
|
|
|||
|
return {
|
|||
|
ok: ok,
|
|||
|
format: color.format || format,
|
|||
|
r: mathMin(255, mathMax(rgb.r, 0)),
|
|||
|
g: mathMin(255, mathMax(rgb.g, 0)),
|
|||
|
b: mathMin(255, mathMax(rgb.b, 0)),
|
|||
|
a: a
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Conversion Functions
|
|||
|
// --------------------
|
|||
|
|
|||
|
// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
|
|||
|
// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
|
|||
|
|
|||
|
// `rgbToRgb`
|
|||
|
// Handle bounds / percentage checking to conform to CSS color spec
|
|||
|
// <http://www.w3.org/TR/css3-color/>
|
|||
|
// *Assumes:* r, g, b in [0, 255] or [0, 1]
|
|||
|
// *Returns:* { r, g, b } in [0, 255]
|
|||
|
function rgbToRgb(r, g, b){
|
|||
|
return {
|
|||
|
r: bound01(r, 255) * 255,
|
|||
|
g: bound01(g, 255) * 255,
|
|||
|
b: bound01(b, 255) * 255
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
// `rgbToHsl`
|
|||
|
// Converts an RGB color value to HSL.
|
|||
|
// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
|
|||
|
// *Returns:* { h, s, l } in [0,1]
|
|||
|
function rgbToHsl(r, g, b) {
|
|||
|
|
|||
|
r = bound01(r, 255);
|
|||
|
g = bound01(g, 255);
|
|||
|
b = bound01(b, 255);
|
|||
|
|
|||
|
var max = mathMax(r, g, b), min = mathMin(r, g, b);
|
|||
|
var h, s, l = (max + min) / 2;
|
|||
|
|
|||
|
if(max == min) {
|
|||
|
h = s = 0; // achromatic
|
|||
|
}
|
|||
|
else {
|
|||
|
var d = max - min;
|
|||
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|||
|
switch(max) {
|
|||
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
|||
|
case g: h = (b - r) / d + 2; break;
|
|||
|
case b: h = (r - g) / d + 4; break;
|
|||
|
}
|
|||
|
|
|||
|
h /= 6;
|
|||
|
}
|
|||
|
|
|||
|
return { h: h, s: s, l: l };
|
|||
|
}
|
|||
|
|
|||
|
// `hslToRgb`
|
|||
|
// Converts an HSL color value to RGB.
|
|||
|
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
|
|||
|
// *Returns:* { r, g, b } in the set [0, 255]
|
|||
|
function hslToRgb(h, s, l) {
|
|||
|
var r, g, b;
|
|||
|
|
|||
|
h = bound01(h, 360);
|
|||
|
s = bound01(s, 100);
|
|||
|
l = bound01(l, 100);
|
|||
|
|
|||
|
function hue2rgb(p, q, t) {
|
|||
|
if(t < 0) t += 1;
|
|||
|
if(t > 1) t -= 1;
|
|||
|
if(t < 1/6) return p + (q - p) * 6 * t;
|
|||
|
if(t < 1/2) return q;
|
|||
|
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
|||
|
return p;
|
|||
|
}
|
|||
|
|
|||
|
if(s === 0) {
|
|||
|
r = g = b = l; // achromatic
|
|||
|
}
|
|||
|
else {
|
|||
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|||
|
var p = 2 * l - q;
|
|||
|
r = hue2rgb(p, q, h + 1/3);
|
|||
|
g = hue2rgb(p, q, h);
|
|||
|
b = hue2rgb(p, q, h - 1/3);
|
|||
|
}
|
|||
|
|
|||
|
return { r: r * 255, g: g * 255, b: b * 255 };
|
|||
|
}
|
|||
|
|
|||
|
// `rgbToHsv`
|
|||
|
// Converts an RGB color value to HSV
|
|||
|
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
|
|||
|
// *Returns:* { h, s, v } in [0,1]
|
|||
|
function rgbToHsv(r, g, b) {
|
|||
|
|
|||
|
r = bound01(r, 255);
|
|||
|
g = bound01(g, 255);
|
|||
|
b = bound01(b, 255);
|
|||
|
|
|||
|
var max = mathMax(r, g, b), min = mathMin(r, g, b);
|
|||
|
var h, s, v = max;
|
|||
|
|
|||
|
var d = max - min;
|
|||
|
s = max === 0 ? 0 : d / max;
|
|||
|
|
|||
|
if(max == min) {
|
|||
|
h = 0; // achromatic
|
|||
|
}
|
|||
|
else {
|
|||
|
switch(max) {
|
|||
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
|||
|
case g: h = (b - r) / d + 2; break;
|
|||
|
case b: h = (r - g) / d + 4; break;
|
|||
|
}
|
|||
|
h /= 6;
|
|||
|
}
|
|||
|
return { h: h, s: s, v: v };
|
|||
|
}
|
|||
|
|
|||
|
// `hsvToRgb`
|
|||
|
// Converts an HSV color value to RGB.
|
|||
|
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
|
|||
|
// *Returns:* { r, g, b } in the set [0, 255]
|
|||
|
function hsvToRgb(h, s, v) {
|
|||
|
|
|||
|
h = bound01(h, 360) * 6;
|
|||
|
s = bound01(s, 100);
|
|||
|
v = bound01(v, 100);
|
|||
|
|
|||
|
var i = math.floor(h),
|
|||
|
f = h - i,
|
|||
|
p = v * (1 - s),
|
|||
|
q = v * (1 - f * s),
|
|||
|
t = v * (1 - (1 - f) * s),
|
|||
|
mod = i % 6,
|
|||
|
r = [v, q, p, p, t, v][mod],
|
|||
|
g = [t, v, v, q, p, p][mod],
|
|||
|
b = [p, p, t, v, v, q][mod];
|
|||
|
|
|||
|
return { r: r * 255, g: g * 255, b: b * 255 };
|
|||
|
}
|
|||
|
|
|||
|
// `rgbToHex`
|
|||
|
// Converts an RGB color to hex
|
|||
|
// Assumes r, g, and b are contained in the set [0, 255]
|
|||
|
// Returns a 3 or 6 character hex
|
|||
|
function rgbToHex(r, g, b, allow3Char) {
|
|||
|
|
|||
|
var hex = [
|
|||
|
pad2(mathRound(r).toString(16)),
|
|||
|
pad2(mathRound(g).toString(16)),
|
|||
|
pad2(mathRound(b).toString(16))
|
|||
|
];
|
|||
|
|
|||
|
// Return a 3 character hex if possible
|
|||
|
if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
|
|||
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
|
|||
|
}
|
|||
|
|
|||
|
return hex.join("");
|
|||
|
}
|
|||
|
// `rgbaToHex`
|
|||
|
// Converts an RGBA color plus alpha transparency to hex
|
|||
|
// Assumes r, g, b and a are contained in the set [0, 255]
|
|||
|
// Returns an 8 character hex
|
|||
|
function rgbaToHex(r, g, b, a) {
|
|||
|
|
|||
|
var hex = [
|
|||
|
pad2(convertDecimalToHex(a)),
|
|||
|
pad2(mathRound(r).toString(16)),
|
|||
|
pad2(mathRound(g).toString(16)),
|
|||
|
pad2(mathRound(b).toString(16))
|
|||
|
];
|
|||
|
|
|||
|
return hex.join("");
|
|||
|
}
|
|||
|
|
|||
|
// `equals`
|
|||
|
// Can be called with any tinycolor input
|
|||
|
tinycolor.equals = function (color1, color2) {
|
|||
|
if (!color1 || !color2) { return false; }
|
|||
|
return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
|
|||
|
};
|
|||
|
tinycolor.random = function() {
|
|||
|
return tinycolor.fromRatio({
|
|||
|
r: mathRandom(),
|
|||
|
g: mathRandom(),
|
|||
|
b: mathRandom()
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
// Modification Functions
|
|||
|
// ----------------------
|
|||
|
// Thanks to less.js for some of the basics here
|
|||
|
// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
|
|||
|
|
|||
|
function desaturate(color, amount) {
|
|||
|
amount = (amount === 0) ? 0 : (amount || 10);
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
hsl.s -= amount / 100;
|
|||
|
hsl.s = clamp01(hsl.s);
|
|||
|
return tinycolor(hsl);
|
|||
|
}
|
|||
|
|
|||
|
function saturate(color, amount) {
|
|||
|
amount = (amount === 0) ? 0 : (amount || 10);
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
hsl.s += amount / 100;
|
|||
|
hsl.s = clamp01(hsl.s);
|
|||
|
return tinycolor(hsl);
|
|||
|
}
|
|||
|
|
|||
|
function greyscale(color) {
|
|||
|
return tinycolor(color).desaturate(100);
|
|||
|
}
|
|||
|
|
|||
|
function lighten (color, amount) {
|
|||
|
amount = (amount === 0) ? 0 : (amount || 10);
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
hsl.l += amount / 100;
|
|||
|
hsl.l = clamp01(hsl.l);
|
|||
|
return tinycolor(hsl);
|
|||
|
}
|
|||
|
|
|||
|
function brighten(color, amount) {
|
|||
|
amount = (amount === 0) ? 0 : (amount || 10);
|
|||
|
var rgb = tinycolor(color).toRgb();
|
|||
|
rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
|
|||
|
rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
|
|||
|
rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
|
|||
|
return tinycolor(rgb);
|
|||
|
}
|
|||
|
|
|||
|
function darken (color, amount) {
|
|||
|
amount = (amount === 0) ? 0 : (amount || 10);
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
hsl.l -= amount / 100;
|
|||
|
hsl.l = clamp01(hsl.l);
|
|||
|
return tinycolor(hsl);
|
|||
|
}
|
|||
|
|
|||
|
// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
|
|||
|
// Values outside of this range will be wrapped into this range.
|
|||
|
function spin(color, amount) {
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
var hue = (mathRound(hsl.h) + amount) % 360;
|
|||
|
hsl.h = hue < 0 ? 360 + hue : hue;
|
|||
|
return tinycolor(hsl);
|
|||
|
}
|
|||
|
|
|||
|
// Combination Functions
|
|||
|
// ---------------------
|
|||
|
// Thanks to jQuery xColor for some of the ideas behind these
|
|||
|
// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
|
|||
|
|
|||
|
function complement(color) {
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
hsl.h = (hsl.h + 180) % 360;
|
|||
|
return tinycolor(hsl);
|
|||
|
}
|
|||
|
|
|||
|
function triad(color) {
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
var h = hsl.h;
|
|||
|
return [
|
|||
|
tinycolor(color),
|
|||
|
tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
|
|||
|
tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
|
|||
|
];
|
|||
|
}
|
|||
|
|
|||
|
function tetrad(color) {
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
var h = hsl.h;
|
|||
|
return [
|
|||
|
tinycolor(color),
|
|||
|
tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
|
|||
|
tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
|
|||
|
tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
|
|||
|
];
|
|||
|
}
|
|||
|
|
|||
|
function splitcomplement(color) {
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
var h = hsl.h;
|
|||
|
return [
|
|||
|
tinycolor(color),
|
|||
|
tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
|
|||
|
tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
|
|||
|
];
|
|||
|
}
|
|||
|
|
|||
|
function analogous(color, results, slices) {
|
|||
|
results = results || 6;
|
|||
|
slices = slices || 30;
|
|||
|
|
|||
|
var hsl = tinycolor(color).toHsl();
|
|||
|
var part = 360 / slices;
|
|||
|
var ret = [tinycolor(color)];
|
|||
|
|
|||
|
for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
|
|||
|
hsl.h = (hsl.h + part) % 360;
|
|||
|
ret.push(tinycolor(hsl));
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
function monochromatic(color, results) {
|
|||
|
results = results || 6;
|
|||
|
var hsv = tinycolor(color).toHsv();
|
|||
|
var h = hsv.h, s = hsv.s, v = hsv.v;
|
|||
|
var ret = [];
|
|||
|
var modification = 1 / results;
|
|||
|
|
|||
|
while (results--) {
|
|||
|
ret.push(tinycolor({ h: h, s: s, v: v}));
|
|||
|
v = (v + modification) % 1;
|
|||
|
}
|
|||
|
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
// Utility Functions
|
|||
|
// ---------------------
|
|||
|
|
|||
|
tinycolor.mix = function(color1, color2, amount) {
|
|||
|
amount = (amount === 0) ? 0 : (amount || 50);
|
|||
|
|
|||
|
var rgb1 = tinycolor(color1).toRgb();
|
|||
|
var rgb2 = tinycolor(color2).toRgb();
|
|||
|
|
|||
|
var p = amount / 100;
|
|||
|
var w = p * 2 - 1;
|
|||
|
var a = rgb2.a - rgb1.a;
|
|||
|
|
|||
|
var w1;
|
|||
|
|
|||
|
if (w * a == -1) {
|
|||
|
w1 = w;
|
|||
|
} else {
|
|||
|
w1 = (w + a) / (1 + w * a);
|
|||
|
}
|
|||
|
|
|||
|
w1 = (w1 + 1) / 2;
|
|||
|
|
|||
|
var w2 = 1 - w1;
|
|||
|
|
|||
|
var rgba = {
|
|||
|
r: rgb2.r * w1 + rgb1.r * w2,
|
|||
|
g: rgb2.g * w1 + rgb1.g * w2,
|
|||
|
b: rgb2.b * w1 + rgb1.b * w2,
|
|||
|
a: rgb2.a * p + rgb1.a * (1 - p)
|
|||
|
};
|
|||
|
|
|||
|
return tinycolor(rgba);
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
// Readability Functions
|
|||
|
// ---------------------
|
|||
|
// <http://www.w3.org/TR/AERT#color-contrast>
|
|||
|
|
|||
|
// `readability`
|
|||
|
// Analyze the 2 colors and returns an object with the following properties:
|
|||
|
// `brightness`: difference in brightness between the two colors
|
|||
|
// `color`: difference in color/hue between the two colors
|
|||
|
tinycolor.readability = function(color1, color2) {
|
|||
|
var c1 = tinycolor(color1);
|
|||
|
var c2 = tinycolor(color2);
|
|||
|
var rgb1 = c1.toRgb();
|
|||
|
var rgb2 = c2.toRgb();
|
|||
|
var brightnessA = c1.getBrightness();
|
|||
|
var brightnessB = c2.getBrightness();
|
|||
|
var colorDiff = (
|
|||
|
Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +
|
|||
|
Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +
|
|||
|
Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)
|
|||
|
);
|
|||
|
|
|||
|
return {
|
|||
|
brightness: Math.abs(brightnessA - brightnessB),
|
|||
|
color: colorDiff
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
// `readable`
|
|||
|
// http://www.w3.org/TR/AERT#color-contrast
|
|||
|
// Ensure that foreground and background color combinations provide sufficient contrast.
|
|||
|
// *Example*
|
|||
|
// tinycolor.isReadable("#000", "#111") => false
|
|||
|
tinycolor.isReadable = function(color1, color2) {
|
|||
|
var readability = tinycolor.readability(color1, color2);
|
|||
|
return readability.brightness > 125 && readability.color > 500;
|
|||
|
};
|
|||
|
|
|||
|
// `mostReadable`
|
|||
|
// Given a base color and a list of possible foreground or background
|
|||
|
// colors for that base, returns the most readable color.
|
|||
|
// *Example*
|
|||
|
// tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
|
|||
|
tinycolor.mostReadable = function(baseColor, colorList) {
|
|||
|
var bestColor = null;
|
|||
|
var bestScore = 0;
|
|||
|
var bestIsReadable = false;
|
|||
|
for (var i=0; i < colorList.length; i++) {
|
|||
|
|
|||
|
// We normalize both around the "acceptable" breaking point,
|
|||
|
// but rank brightness constrast higher than hue.
|
|||
|
|
|||
|
var readability = tinycolor.readability(baseColor, colorList[i]);
|
|||
|
var readable = readability.brightness > 125 && readability.color > 500;
|
|||
|
var score = 3 * (readability.brightness / 125) + (readability.color / 500);
|
|||
|
|
|||
|
if ((readable && ! bestIsReadable) ||
|
|||
|
(readable && bestIsReadable && score > bestScore) ||
|
|||
|
((! readable) && (! bestIsReadable) && score > bestScore)) {
|
|||
|
bestIsReadable = readable;
|
|||
|
bestScore = score;
|
|||
|
bestColor = tinycolor(colorList[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
return bestColor;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
// Big List of Colors
|
|||
|
// ------------------
|
|||
|
// <http://www.w3.org/TR/css3-color/#svg-color>
|
|||
|
var names = tinycolor.names = {
|
|||
|
aliceblue: "f0f8ff",
|
|||
|
antiquewhite: "faebd7",
|
|||
|
aqua: "0ff",
|
|||
|
aquamarine: "7fffd4",
|
|||
|
azure: "f0ffff",
|
|||
|
beige: "f5f5dc",
|
|||
|
bisque: "ffe4c4",
|
|||
|
black: "000",
|
|||
|
blanchedalmond: "ffebcd",
|
|||
|
blue: "00f",
|
|||
|
blueviolet: "8a2be2",
|
|||
|
brown: "a52a2a",
|
|||
|
burlywood: "deb887",
|
|||
|
burntsienna: "ea7e5d",
|
|||
|
cadetblue: "5f9ea0",
|
|||
|
chartreuse: "7fff00",
|
|||
|
chocolate: "d2691e",
|
|||
|
coral: "ff7f50",
|
|||
|
cornflowerblue: "6495ed",
|
|||
|
cornsilk: "fff8dc",
|
|||
|
crimson: "dc143c",
|
|||
|
cyan: "0ff",
|
|||
|
darkblue: "00008b",
|
|||
|
darkcyan: "008b8b",
|
|||
|
darkgoldenrod: "b8860b",
|
|||
|
darkgray: "a9a9a9",
|
|||
|
darkgreen: "006400",
|
|||
|
darkgrey: "a9a9a9",
|
|||
|
darkkhaki: "bdb76b",
|
|||
|
darkmagenta: "8b008b",
|
|||
|
darkolivegreen: "556b2f",
|
|||
|
darkorange: "ff8c00",
|
|||
|
darkorchid: "9932cc",
|
|||
|
darkred: "8b0000",
|
|||
|
darksalmon: "e9967a",
|
|||
|
darkseagreen: "8fbc8f",
|
|||
|
darkslateblue: "483d8b",
|
|||
|
darkslategray: "2f4f4f",
|
|||
|
darkslategrey: "2f4f4f",
|
|||
|
darkturquoise: "00ced1",
|
|||
|
darkviolet: "9400d3",
|
|||
|
deeppink: "ff1493",
|
|||
|
deepskyblue: "00bfff",
|
|||
|
dimgray: "696969",
|
|||
|
dimgrey: "696969",
|
|||
|
dodgerblue: "1e90ff",
|
|||
|
firebrick: "b22222",
|
|||
|
floralwhite: "fffaf0",
|
|||
|
forestgreen: "228b22",
|
|||
|
fuchsia: "f0f",
|
|||
|
gainsboro: "dcdcdc",
|
|||
|
ghostwhite: "f8f8ff",
|
|||
|
gold: "ffd700",
|
|||
|
goldenrod: "daa520",
|
|||
|
gray: "808080",
|
|||
|
green: "008000",
|
|||
|
greenyellow: "adff2f",
|
|||
|
grey: "808080",
|
|||
|
honeydew: "f0fff0",
|
|||
|
hotpink: "ff69b4",
|
|||
|
indianred: "cd5c5c",
|
|||
|
indigo: "4b0082",
|
|||
|
ivory: "fffff0",
|
|||
|
khaki: "f0e68c",
|
|||
|
lavender: "e6e6fa",
|
|||
|
lavenderblush: "fff0f5",
|
|||
|
lawngreen: "7cfc00",
|
|||
|
lemonchiffon: "fffacd",
|
|||
|
lightblue: "add8e6",
|
|||
|
lightcoral: "f08080",
|
|||
|
lightcyan: "e0ffff",
|
|||
|
lightgoldenrodyellow: "fafad2",
|
|||
|
lightgray: "d3d3d3",
|
|||
|
lightgreen: "90ee90",
|
|||
|
lightgrey: "d3d3d3",
|
|||
|
lightpink: "ffb6c1",
|
|||
|
lightsalmon: "ffa07a",
|
|||
|
lightseagreen: "20b2aa",
|
|||
|
lightskyblue: "87cefa",
|
|||
|
lightslategray: "789",
|
|||
|
lightslategrey: "789",
|
|||
|
lightsteelblue: "b0c4de",
|
|||
|
lightyellow: "ffffe0",
|
|||
|
lime: "0f0",
|
|||
|
limegreen: "32cd32",
|
|||
|
linen: "faf0e6",
|
|||
|
magenta: "f0f",
|
|||
|
maroon: "800000",
|
|||
|
mediumaquamarine: "66cdaa",
|
|||
|
mediumblue: "0000cd",
|
|||
|
mediumorchid: "ba55d3",
|
|||
|
mediumpurple: "9370db",
|
|||
|
mediumseagreen: "3cb371",
|
|||
|
mediumslateblue: "7b68ee",
|
|||
|
mediumspringgreen: "00fa9a",
|
|||
|
mediumturquoise: "48d1cc",
|
|||
|
mediumvioletred: "c71585",
|
|||
|
midnightblue: "191970",
|
|||
|
mintcream: "f5fffa",
|
|||
|
mistyrose: "ffe4e1",
|
|||
|
moccasin: "ffe4b5",
|
|||
|
navajowhite: "ffdead",
|
|||
|
navy: "000080",
|
|||
|
oldlace: "fdf5e6",
|
|||
|
olive: "808000",
|
|||
|
olivedrab: "6b8e23",
|
|||
|
orange: "ffa500",
|
|||
|
orangered: "ff4500",
|
|||
|
orchid: "da70d6",
|
|||
|
palegoldenrod: "eee8aa",
|
|||
|
palegreen: "98fb98",
|
|||
|
paleturquoise: "afeeee",
|
|||
|
palevioletred: "db7093",
|
|||
|
papayawhip: "ffefd5",
|
|||
|
peachpuff: "ffdab9",
|
|||
|
peru: "cd853f",
|
|||
|
pink: "ffc0cb",
|
|||
|
plum: "dda0dd",
|
|||
|
powderblue: "b0e0e6",
|
|||
|
purple: "800080",
|
|||
|
red: "f00",
|
|||
|
rosybrown: "bc8f8f",
|
|||
|
royalblue: "4169e1",
|
|||
|
saddlebrown: "8b4513",
|
|||
|
salmon: "fa8072",
|
|||
|
sandybrown: "f4a460",
|
|||
|
seagreen: "2e8b57",
|
|||
|
seashell: "fff5ee",
|
|||
|
sienna: "a0522d",
|
|||
|
silver: "c0c0c0",
|
|||
|
skyblue: "87ceeb",
|
|||
|
slateblue: "6a5acd",
|
|||
|
slategray: "708090",
|
|||
|
slategrey: "708090",
|
|||
|
snow: "fffafa",
|
|||
|
springgreen: "00ff7f",
|
|||
|
steelblue: "4682b4",
|
|||
|
tan: "d2b48c",
|
|||
|
teal: "008080",
|
|||
|
thistle: "d8bfd8",
|
|||
|
tomato: "ff6347",
|
|||
|
turquoise: "40e0d0",
|
|||
|
violet: "ee82ee",
|
|||
|
wheat: "f5deb3",
|
|||
|
white: "fff",
|
|||
|
whitesmoke: "f5f5f5",
|
|||
|
yellow: "ff0",
|
|||
|
yellowgreen: "9acd32"
|
|||
|
};
|
|||
|
|
|||
|
// Make it easy to access colors via `hexNames[hex]`
|
|||
|
var hexNames = tinycolor.hexNames = flip(names);
|
|||
|
|
|||
|
|
|||
|
// Utilities
|
|||
|
// ---------
|
|||
|
|
|||
|
// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
|
|||
|
function flip(o) {
|
|||
|
var flipped = { };
|
|||
|
for (var i in o) {
|
|||
|
if (o.hasOwnProperty(i)) {
|
|||
|
flipped[o[i]] = i;
|
|||
|
}
|
|||
|
}
|
|||
|
return flipped;
|
|||
|
}
|
|||
|
|
|||
|
// Return a valid alpha value [0,1] with all invalid values being set to 1
|
|||
|
function boundAlpha(a) {
|
|||
|
a = parseFloat(a);
|
|||
|
|
|||
|
if (isNaN(a) || a < 0 || a > 1) {
|
|||
|
a = 1;
|
|||
|
}
|
|||
|
|
|||
|
return a;
|
|||
|
}
|
|||
|
|
|||
|
// Take input from [0, n] and return it as [0, 1]
|
|||
|
function bound01(n, max) {
|
|||
|
if (isOnePointZero(n)) { n = "100%"; }
|
|||
|
|
|||
|
var processPercent = isPercentage(n);
|
|||
|
n = mathMin(max, mathMax(0, parseFloat(n)));
|
|||
|
|
|||
|
// Automatically convert percentage into number
|
|||
|
if (processPercent) {
|
|||
|
n = parseInt(n * max, 10) / 100;
|
|||
|
}
|
|||
|
|
|||
|
// Handle floating point rounding errors
|
|||
|
if ((math.abs(n - max) < 0.000001)) {
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
// Convert into [0, 1] range if it isn't already
|
|||
|
return (n % max) / parseFloat(max);
|
|||
|
}
|
|||
|
|
|||
|
// Force a number between 0 and 1
|
|||
|
function clamp01(val) {
|
|||
|
return mathMin(1, mathMax(0, val));
|
|||
|
}
|
|||
|
|
|||
|
// Parse a base-16 hex value into a base-10 integer
|
|||
|
function parseIntFromHex(val) {
|
|||
|
return parseInt(val, 16);
|
|||
|
}
|
|||
|
|
|||
|
// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
|
|||
|
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
|
|||
|
function isOnePointZero(n) {
|
|||
|
return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
|
|||
|
}
|
|||
|
|
|||
|
// Check to see if string passed in is a percentage
|
|||
|
function isPercentage(n) {
|
|||
|
return typeof n === "string" && n.indexOf('%') != -1;
|
|||
|
}
|
|||
|
|
|||
|
// Force a hex value to have 2 characters
|
|||
|
function pad2(c) {
|
|||
|
return c.length == 1 ? '0' + c : '' + c;
|
|||
|
}
|
|||
|
|
|||
|
// Replace a decimal with it's percentage value
|
|||
|
function convertToPercentage(n) {
|
|||
|
if (n <= 1) {
|
|||
|
n = (n * 100) + "%";
|
|||
|
}
|
|||
|
|
|||
|
return n;
|
|||
|
}
|
|||
|
|
|||
|
// Converts a decimal to a hex value
|
|||
|
function convertDecimalToHex(d) {
|
|||
|
return Math.round(parseFloat(d) * 255).toString(16);
|
|||
|
}
|
|||
|
// Converts a hex value to a decimal
|
|||
|
function convertHexToDecimal(h) {
|
|||
|
return (parseIntFromHex(h) / 255);
|
|||
|
}
|
|||
|
|
|||
|
var matchers = (function() {
|
|||
|
|
|||
|
// <http://www.w3.org/TR/css3-values/#integers>
|
|||
|
var CSS_INTEGER = "[-\\+]?\\d+%?";
|
|||
|
|
|||
|
// <http://www.w3.org/TR/css3-values/#number-value>
|
|||
|
var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
|
|||
|
|
|||
|
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
|
|||
|
var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
|
|||
|
|
|||
|
// Actual matching.
|
|||
|
// Parentheses and commas are optional, but not required.
|
|||
|
// Whitespace can take the place of commas or opening paren
|
|||
|
var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
|
|||
|
var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
|
|||
|
|
|||
|
return {
|
|||
|
rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
|
|||
|
rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
|
|||
|
hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
|
|||
|
hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
|
|||
|
hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
|
|||
|
hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
|||
|
hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
|||
|
hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
|
|||
|
};
|
|||
|
})();
|
|||
|
|
|||
|
// `stringInputToObject`
|
|||
|
// Permissive string parsing. Take in a number of formats, and output an object
|
|||
|
// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
|
|||
|
function stringInputToObject(color) {
|
|||
|
|
|||
|
color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
|
|||
|
var named = false;
|
|||
|
if (names[color]) {
|
|||
|
color = names[color];
|
|||
|
named = true;
|
|||
|
}
|
|||
|
else if (color == 'transparent') {
|
|||
|
return { r: 0, g: 0, b: 0, a: 0, format: "name" };
|
|||
|
}
|
|||
|
|
|||
|
// Try to match string input using regular expressions.
|
|||
|
// Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
|
|||
|
// Just return an object and let the conversion functions handle that.
|
|||
|
// This way the result will be the same whether the tinycolor is initialized with string or object.
|
|||
|
var match;
|
|||
|
if ((match = matchers.rgb.exec(color))) {
|
|||
|
return { r: match[1], g: match[2], b: match[3] };
|
|||
|
}
|
|||
|
if ((match = matchers.rgba.exec(color))) {
|
|||
|
return { r: match[1], g: match[2], b: match[3], a: match[4] };
|
|||
|
}
|
|||
|
if ((match = matchers.hsl.exec(color))) {
|
|||
|
return { h: match[1], s: match[2], l: match[3] };
|
|||
|
}
|
|||
|
if ((match = matchers.hsla.exec(color))) {
|
|||
|
return { h: match[1], s: match[2], l: match[3], a: match[4] };
|
|||
|
}
|
|||
|
if ((match = matchers.hsv.exec(color))) {
|
|||
|
return { h: match[1], s: match[2], v: match[3] };
|
|||
|
}
|
|||
|
if ((match = matchers.hex8.exec(color))) {
|
|||
|
return {
|
|||
|
a: convertHexToDecimal(match[1]),
|
|||
|
r: parseIntFromHex(match[2]),
|
|||
|
g: parseIntFromHex(match[3]),
|
|||
|
b: parseIntFromHex(match[4]),
|
|||
|
format: named ? "name" : "hex8"
|
|||
|
};
|
|||
|
}
|
|||
|
if ((match = matchers.hex6.exec(color))) {
|
|||
|
return {
|
|||
|
r: parseIntFromHex(match[1]),
|
|||
|
g: parseIntFromHex(match[2]),
|
|||
|
b: parseIntFromHex(match[3]),
|
|||
|
format: named ? "name" : "hex"
|
|||
|
};
|
|||
|
}
|
|||
|
if ((match = matchers.hex3.exec(color))) {
|
|||
|
return {
|
|||
|
r: parseIntFromHex(match[1] + '' + match[1]),
|
|||
|
g: parseIntFromHex(match[2] + '' + match[2]),
|
|||
|
b: parseIntFromHex(match[3] + '' + match[3]),
|
|||
|
format: named ? "name" : "hex"
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// Node: Export function
|
|||
|
if (typeof module !== "undefined" && module.exports) {
|
|||
|
module.exports = tinycolor;
|
|||
|
}
|
|||
|
// AMD/requirejs: Define the module
|
|||
|
else if (typeof define === 'function' && define.amd) {
|
|||
|
define(function () {return tinycolor;});
|
|||
|
}
|
|||
|
// Browser: Expose to window
|
|||
|
else {
|
|||
|
window.tinycolor = tinycolor;
|
|||
|
}
|
|||
|
|
|||
|
})();
|
|||
|
|
|||
|
'use strict';
|
|||
|
|
|||
|
|
|||
|
(function(hscd) {
|
|||
|
var ctx = {};
|
|||
|
|
|||
|
function onAdjust(name, value) {
|
|||
|
ctx.searchParams[name] = value;
|
|||
|
|
|||
|
var query = {
|
|||
|
searchParams: ctx.searchParams,
|
|||
|
searchRange: ctx.searchRange,
|
|||
|
minScore: ctx.minScore,
|
|||
|
hintSteps: ctx.hintSteps,
|
|||
|
maxResults: ctx.maxResults
|
|||
|
};
|
|||
|
|
|||
|
$.getJSON('/node/search', query, function(results) {
|
|||
|
var hintData = {};
|
|||
|
for (var keyword in results.columns) {
|
|||
|
hintData[keyword] = results.columns[keyword].hints;
|
|||
|
}
|
|||
|
|
|||
|
ctx.grapher.setColumnHints(hintData);
|
|||
|
outputResults(results.items, results.count);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function onSearch() {
|
|||
|
var keywords = $('#keywordsToSearch').val() || [];
|
|||
|
var searchParams = {};
|
|||
|
|
|||
|
for (var i = 0, count = keywords.length; i < count; ++i) {
|
|||
|
searchParams[keywords[i]] = 1.0;
|
|||
|
}
|
|||
|
|
|||
|
var query = {
|
|||
|
searchParams: searchParams,
|
|||
|
searchRange: { min: -1.0, max: 1.0 },
|
|||
|
minScore: parseFloat($('#minScore').val()),
|
|||
|
hintSteps: parseInt($('#hintSteps').val()),
|
|||
|
maxResults: parseInt($('#maxResults').val())
|
|||
|
};
|
|||
|
|
|||
|
$.getJSON('/node/search', query, function(results) {
|
|||
|
ctx.searchParams = query.searchParams;
|
|||
|
ctx.searchRange = query.searchRange;
|
|||
|
ctx.minScore = query.minScore;
|
|||
|
ctx.hintSteps = query.hintSteps;
|
|||
|
ctx.maxResults = query.maxResults;
|
|||
|
|
|||
|
ctx.grapher = new Grapher('grapher', ctx.searchRange, 150, true, true);
|
|||
|
ctx.grapher.setColumns(results.columns);
|
|||
|
ctx.grapher.setValueChangedListener(onAdjust);
|
|||
|
|
|||
|
outputResults(results.items, results.count);
|
|||
|
|
|||
|
$('#query').text(keywords.join(', '));
|
|||
|
$('#useLocalScale').click(function() {
|
|||
|
var useLocalScale = $('#useLocalScale').is(':checked');
|
|||
|
ctx.grapher.setUseLocalScale(useLocalScale);
|
|||
|
});
|
|||
|
$('#useRelativeScale').click(function() {
|
|||
|
var useRelativeScale = $('#useRelativeScale').is(':checked');
|
|||
|
ctx.grapher.setUseRelativeScale(useRelativeScale);
|
|||
|
});
|
|||
|
$('#input').fadeOut(function() {
|
|||
|
$('#output').fadeIn();
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function onLearn() {
|
|||
|
$('#learnKeyword').prop('disabled', true);
|
|||
|
|
|||
|
$('#learnError').slideUp(function() {
|
|||
|
var query = {
|
|||
|
keyword: $('#keywordToLearn').val(),
|
|||
|
params: ctx.searchParams
|
|||
|
};
|
|||
|
|
|||
|
$.getJSON('/node/addKeyword', query, function(results) {
|
|||
|
if (results.success) {
|
|||
|
$('#learnDialog').modal('hide');
|
|||
|
}
|
|||
|
else {
|
|||
|
$('#learnError').slideDown(function() {
|
|||
|
$('#learnKeyword').prop('disabled', false);
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function onForget() {
|
|||
|
$('#forgetKeyword').prop('disabled', true);
|
|||
|
|
|||
|
$('#forgetError').slideUp(function() {
|
|||
|
var query = {
|
|||
|
keyword: $('#keywordToForget').val()
|
|||
|
};
|
|||
|
|
|||
|
$.getJSON('/node/removeKeyword', query, function(results) {
|
|||
|
if (results.success) {
|
|||
|
$('#forgetDialog').modal('hide');
|
|||
|
}
|
|||
|
else {
|
|||
|
$('#forgetError').slideDown(function() {
|
|||
|
$('#forgetKeyword').prop('disabled', false);
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function outputResults(results, count) {
|
|||
|
var searchResultCnt = String(results.length);
|
|||
|
if (results.length < count) {
|
|||
|
searchResultCnt += ' of ' + count;
|
|||
|
}
|
|||
|
$('#count').text(searchResultCnt);
|
|||
|
|
|||
|
var template = Handlebars.compile($('#template').html());
|
|||
|
$('#results').empty();
|
|||
|
$('#results').append(template({'results': results}));
|
|||
|
}
|
|||
|
|
|||
|
$(document).on({
|
|||
|
ajaxStart: function() {
|
|||
|
$('#spinner').show();
|
|||
|
},
|
|||
|
|
|||
|
ajaxStop: function() {
|
|||
|
$('#spinner').hide();
|
|||
|
},
|
|||
|
|
|||
|
ready: function() {
|
|||
|
$('#keywordsToSearch').selectpicker();
|
|||
|
|
|||
|
$.getJSON('/node/getKeywords', function(keywords) {
|
|||
|
$('#searchKeywords').click(onSearch);
|
|||
|
for (var i = 0, count = keywords.length; i < count; ++i) {
|
|||
|
$('#keywordsToSearch').append($('<option></option>', {
|
|||
|
value: keywords[i],
|
|||
|
text: keywords[i]
|
|||
|
}));
|
|||
|
}
|
|||
|
$('#keywordsToSearch').selectpicker('refresh');
|
|||
|
$('#keywordsToSearch').change(function() {
|
|||
|
$('#searchKeywords').prop('disabled', !$(this).val());
|
|||
|
});
|
|||
|
|
|||
|
$('#forgetKeyword').click(onForget);
|
|||
|
$('#forgetDialog').on('show.bs.modal', function() {
|
|||
|
$('#forgetError').hide();
|
|||
|
$.getJSON('/node/getKeywords', function(keywords) {
|
|||
|
$('#forgetKeyword').prop('disabled', keywords.length === 0);
|
|||
|
$('#keywordToForget').empty();
|
|||
|
for (var i = 0, count = keywords.length; i < count; ++i) {
|
|||
|
$('#keywordToForget').append($('<option></option>', {
|
|||
|
value: keywords[i],
|
|||
|
text: keywords[i]
|
|||
|
}));
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
$('#learnKeyword').click(onLearn);
|
|||
|
$('#keywordToLearn').bind('input', function() {
|
|||
|
$('#learnKeyword').prop('disabled', !$(this).val());
|
|||
|
});
|
|||
|
$('#learnDialog').on('show.bs.modal', function() {
|
|||
|
$('#learnKeyword').prop('disabled', true);
|
|||
|
$('#keywordToLearn').val('');
|
|||
|
$('#learnError').hide();
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
}(window.hscd = window.hscd || {}));
|
|||
|
|
|||
|
'use strict';
|
|||
|
|
|||
|
goog.require('goog.math');
|
|||
|
goog.require('goog.math.Coordinate');
|
|||
|
goog.require('goog.math.Range');
|
|||
|
goog.require('goog.math.Rect');
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Column
|
|||
|
//
|
|||
|
|
|||
|
function Column(canvas, name, params, scale, range, bounds) {
|
|||
|
this.clearShapes = function() {
|
|||
|
_.each(this.shapes, function(shape) {
|
|||
|
this.canvas.remove(shape);
|
|||
|
});
|
|||
|
|
|||
|
this.shapes = [];
|
|||
|
}
|
|||
|
|
|||
|
this.updateShapes = function(final) {
|
|||
|
this.columnBounds = this.getColumnBounds(this.bounds);
|
|||
|
this.labelBounds = this.getLabelBounds(this.columnBounds);
|
|||
|
this.hintBounds = this.getHintBounds(this.columnBounds);
|
|||
|
this.fillBounds = this.getFillBounds(this.columnBounds);
|
|||
|
this.handleBounds = this.getHandleBounds(this.columnBounds, this.fillBounds);
|
|||
|
|
|||
|
if (final) {
|
|||
|
this.updateRect('boundsRect', {
|
|||
|
left: this.columnBounds.left,
|
|||
|
top: this.columnBounds.top,
|
|||
|
width: this.columnBounds.width,
|
|||
|
height: this.columnBounds.height,
|
|||
|
stroke: this.strokeColor,
|
|||
|
fill: this.emptyColor
|
|||
|
});
|
|||
|
|
|||
|
this.updateRect('hintRect', {
|
|||
|
left: this.hintBounds.left,
|
|||
|
top: this.hintBounds.top,
|
|||
|
width: this.hintBounds.width,
|
|||
|
height: this.hintBounds.height,
|
|||
|
stroke: this.strokeColor
|
|||
|
});
|
|||
|
|
|||
|
this.hintRect.setGradient('fill', {
|
|||
|
x1: 0.0,
|
|||
|
y1: 0.0,
|
|||
|
x2: 0.0,
|
|||
|
y2: this.hintRect.height,
|
|||
|
colorStops: this.decimateHints(this.steps, this.scale)
|
|||
|
});
|
|||
|
|
|||
|
this.updateRect('labelRect', {
|
|||
|
left: this.labelBounds.left,
|
|||
|
top: this.labelBounds.top,
|
|||
|
width: this.labelBounds.width,
|
|||
|
height: this.labelBounds.height,
|
|||
|
fill: this.strokeColor
|
|||
|
});
|
|||
|
|
|||
|
this.updateText('label', this.name, {
|
|||
|
left: this.fillBounds.left + this.fillBounds.width / 2,
|
|||
|
top: this.labelBounds.top + this.labelBounds.height / 2,
|
|||
|
fontSize: this.labelFontSize,
|
|||
|
originX: 'center',
|
|||
|
originY: 'center',
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.updateRect('fillRect', {
|
|||
|
left: this.fillBounds.left,
|
|||
|
top: this.fillBounds.top,
|
|||
|
width: this.fillBounds.width,
|
|||
|
height: this.fillBounds.height,
|
|||
|
fill: this.getFillColor()
|
|||
|
});
|
|||
|
|
|||
|
this.updateRect('handleRect', {
|
|||
|
left: this.handleBounds.left,
|
|||
|
top: this.handleBounds.top,
|
|||
|
width: this.handleBounds.width,
|
|||
|
height: this.handleBounds.height,
|
|||
|
fill: this.getHandleColor()
|
|||
|
});
|
|||
|
|
|||
|
if (final && goog.math.Range.containsPoint(this.range, 0.0)) {
|
|||
|
var y = this.getPosFromValue(0.0);
|
|||
|
var p = [this.bounds.left, y, this.bounds.left + this.tickLength, y];
|
|||
|
this.updateLine('baseline', p, {
|
|||
|
stroke: this.tickColor
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.canvas.renderAll();
|
|||
|
}
|
|||
|
|
|||
|
this.updateRect = function(name, args) {
|
|||
|
if (name in this) {
|
|||
|
this[name].set(args);
|
|||
|
}
|
|||
|
else {
|
|||
|
var rect = new fabric.Rect(args);
|
|||
|
this.canvas.add(rect);
|
|||
|
this.shapes.push(rect);
|
|||
|
this[name] = rect;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.updateText = function(name, text, args) {
|
|||
|
if (name in this) {
|
|||
|
this[name].set(args);
|
|||
|
}
|
|||
|
else {
|
|||
|
var text = new fabric.Text(text, args);
|
|||
|
this.canvas.add(text);
|
|||
|
this.shapes.push(text);
|
|||
|
this[name] = text;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.updateLine = function(name, points, args) {
|
|||
|
if (name in this) {
|
|||
|
this[name].set(args);
|
|||
|
}
|
|||
|
else {
|
|||
|
var line = new fabric.Line(points, args);
|
|||
|
this.canvas.add(line);
|
|||
|
this.shapes.push(line);
|
|||
|
this[name] = line;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.decimateHints = function(steps, scale) {
|
|||
|
var groups = this.groupHints(steps);
|
|||
|
|
|||
|
var colorStops = {};
|
|||
|
_.each(groups, function(count, index) {
|
|||
|
var colorPercent = 0;
|
|||
|
if (scale.getLength() > 0) {
|
|||
|
colorPercent = Math.max(0, count - scale.start) / scale.getLength();
|
|||
|
}
|
|||
|
|
|||
|
var colorByte = 0xff - Math.min(0xff, Math.round(0xff * colorPercent));
|
|||
|
var colorObj = tinycolor({ r: colorByte, g: colorByte, b: colorByte });
|
|||
|
var colorStr = colorObj.toHexString();
|
|||
|
|
|||
|
colorStops[index / steps] = colorStr;
|
|||
|
});
|
|||
|
|
|||
|
return colorStops;
|
|||
|
}
|
|||
|
|
|||
|
this.groupHints = function(steps) {
|
|||
|
var stepSize = this.range.getLength() / steps;
|
|||
|
|
|||
|
var hintGroups = [];
|
|||
|
for (var i = 0; i < steps; ++i) {
|
|||
|
var stepMax = this.range.end - stepSize * i;
|
|||
|
var stepMin = stepMax - stepSize;
|
|||
|
|
|||
|
var hintCount = 0;
|
|||
|
_.each(this.hints, function(hint) {
|
|||
|
if (hint.sample > stepMin && hint.sample <= stepMax) {
|
|||
|
hintCount += hint.count;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
hintGroups.push(hintCount);
|
|||
|
}
|
|||
|
|
|||
|
return hintGroups;
|
|||
|
}
|
|||
|
|
|||
|
this.setClampedValue = function(value, final) {
|
|||
|
this.value = goog.math.clamp(value, this.range.start, this.range.end);
|
|||
|
this.updateShapes(final);
|
|||
|
|
|||
|
if (this.onValueChanged && final) {
|
|||
|
this.onValueChanged(this.name, this.value);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.setHints = function(hints, scale) {
|
|||
|
this.hints = hints;
|
|||
|
this.scale = scale;
|
|||
|
this.updateShapes(true);
|
|||
|
}
|
|||
|
|
|||
|
this.getLabelBounds = function(bounds) {
|
|||
|
return new goog.math.Rect(
|
|||
|
bounds.left,
|
|||
|
bounds.top + bounds.height,
|
|||
|
bounds.width,
|
|||
|
this.labelSize
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.getColumnBounds = function(bounds) {
|
|||
|
return new goog.math.Rect(
|
|||
|
bounds.left + this.tickLength,
|
|||
|
bounds.top,
|
|||
|
bounds.width - this.tickLength,
|
|||
|
bounds.height - this.labelSize
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.getHintBounds = function(bounds) {
|
|||
|
return new goog.math.Rect(
|
|||
|
bounds.left + bounds.width - this.hintSize,
|
|||
|
bounds.top,
|
|||
|
this.hintSize,
|
|||
|
bounds.height
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.getFillBounds = function(bounds) {
|
|||
|
var y1 = this.getPosFromValue(0.0);
|
|||
|
var y2 = this.getPosFromValue(this.value);
|
|||
|
return new goog.math.Rect(
|
|||
|
bounds.left,
|
|||
|
Math.min(y1, y2),
|
|||
|
bounds.width - this.hintSize,
|
|||
|
Math.abs(y1 - y2)
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.getHandleBounds = function(bounds, fillBounds) {
|
|||
|
var handleBounds = new goog.math.Rect(
|
|||
|
fillBounds.left,
|
|||
|
this.getPosFromValue(this.value) - this.handleSize / 2,
|
|||
|
fillBounds.width,
|
|||
|
this.handleSize
|
|||
|
);
|
|||
|
handleBounds.intersection(bounds);
|
|||
|
return handleBounds;
|
|||
|
}
|
|||
|
|
|||
|
this.valueColorAdjust = function(color, offset) {
|
|||
|
var colorObj = tinycolor(color);
|
|||
|
var rangeEnd = this.value >= 0.0 ? this.range.end : this.range.start;
|
|||
|
var rangeMid = (this.range.start + this.range.end) / 2.0;
|
|||
|
var rangeRat = (this.value - rangeMid) / (rangeEnd - rangeMid);
|
|||
|
var desatVal = Math.max(0.0, 1.0 - rangeRat + offset) * 100.0;
|
|||
|
return colorObj.desaturate(desatVal).toHexString();
|
|||
|
}
|
|||
|
|
|||
|
this.getFillColor = function() {
|
|||
|
var color = this.value >= 0.0 ? this.fillColorPos : this.fillColorNeg;
|
|||
|
return this.valueColorAdjust(color, this.desatOffset);
|
|||
|
}
|
|||
|
|
|||
|
this.getHandleColor = function() {
|
|||
|
var color = this.value >= 0.0 ? this.handleColorPos : this.handleColorNeg;
|
|||
|
return this.valueColorAdjust(color, this.desatOffset);
|
|||
|
}
|
|||
|
|
|||
|
this.mouseDown = function(position) {
|
|||
|
if (this.isGrabbing(position)) {
|
|||
|
this.stateTransition(this.State.DRAG, position);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.mouseUp = function(position) {
|
|||
|
this.stateTransition(
|
|||
|
this.isHovering(position) ? this.State.HOVER : this.State.NORMAL,
|
|||
|
position
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.mouseMove = function(position) {
|
|||
|
switch (this.state) {
|
|||
|
case this.State.NORMAL:
|
|||
|
if (this.isHovering(position)) {
|
|||
|
this.stateTransition(this.State.HOVER, position);
|
|||
|
}
|
|||
|
break;
|
|||
|
case this.State.HOVER:
|
|||
|
if (!this.isHovering(position)) {
|
|||
|
this.stateTransition(this.State.NORMAL, position);
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
this.stateUpdate(position);
|
|||
|
}
|
|||
|
|
|||
|
this.mouseOut = function(position) {
|
|||
|
this.mouseUp(position);
|
|||
|
}
|
|||
|
|
|||
|
this.mouseDoubleClick = function(position) {
|
|||
|
if (this.isContained(position)) {
|
|||
|
this.setClampedValue(this.getValueFromPos(position.y), true);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.getValueFromPos = function(position) {
|
|||
|
var percent = 1.0 - (position - this.columnBounds.top) / this.columnBounds.height;
|
|||
|
return this.range.start + this.range.getLength() * percent;
|
|||
|
}
|
|||
|
|
|||
|
this.getPosFromValue = function(value) {
|
|||
|
var percent = 1.0 - (value - this.range.start) / this.range.getLength();
|
|||
|
return goog.math.clamp(
|
|||
|
this.columnBounds.top + this.columnBounds.height * percent,
|
|||
|
this.columnBounds.top,
|
|||
|
this.columnBounds.top + this.columnBounds.height
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.isHovering = function(position) {
|
|||
|
return this.isGrabbing(position);
|
|||
|
}
|
|||
|
|
|||
|
this.isGrabbing = function(position) {
|
|||
|
return this.handleBounds.contains(position);
|
|||
|
}
|
|||
|
|
|||
|
this.isContained = function(position) {
|
|||
|
return this.columnBounds.contains(position);
|
|||
|
}
|
|||
|
|
|||
|
this.stateUpdate = function(position) {
|
|||
|
switch (this.state) {
|
|||
|
case this.State.DRAG:
|
|||
|
this.setClampedValue(this.getValueFromPos(position.y) + this.dragDelta, false);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.stateTransition = function(state, position) {
|
|||
|
if (state == this.state) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
switch (this.state) {
|
|||
|
case this.State.DRAG:
|
|||
|
this.setClampedValue(this.getValueFromPos(position.y) + this.dragDelta, true);
|
|||
|
case this.State.HOVER:
|
|||
|
if (state == this.State.NORMAL) {
|
|||
|
this.canvas.contextContainer.canvas.style.cursor = 'default';
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
switch (state) {
|
|||
|
case this.State.DRAG:
|
|||
|
this.dragDelta = this.value - this.getValueFromPos(position.y);
|
|||
|
case this.State.HOVER:
|
|||
|
this.canvas.contextContainer.canvas.style.cursor = 'ns-resize';
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
this.state = state;
|
|||
|
}
|
|||
|
|
|||
|
this.State = {
|
|||
|
NORMAL: 0,
|
|||
|
HOVER: 1,
|
|||
|
DRAG: 2
|
|||
|
};
|
|||
|
|
|||
|
this.handleSize = 10;
|
|||
|
this.desatOffset = -0.3;
|
|||
|
this.hintSize = 10;
|
|||
|
this.labelFontSize = 15;
|
|||
|
this.labelSize = 20;
|
|||
|
this.tickLength = 5;
|
|||
|
this.emptyColor = '#eeeeec';
|
|||
|
this.strokeColor = '#d3d7cf';
|
|||
|
this.tickColor = '#888a85';
|
|||
|
this.fillColorNeg = '#3465a4';
|
|||
|
this.fillColorPos = '#cc0000';
|
|||
|
this.handleColorNeg = '#204a87';
|
|||
|
this.handleColorPos = '#a40000';
|
|||
|
|
|||
|
this.canvas = canvas;
|
|||
|
this.shapes = [];
|
|||
|
this.name = name;
|
|||
|
this.value = params.value;
|
|||
|
this.hints = params.hints;
|
|||
|
this.steps = params.steps;
|
|||
|
this.scale = scale;
|
|||
|
this.range = range;
|
|||
|
this.bounds = bounds;
|
|||
|
this.state = this.State.NORMAL;
|
|||
|
|
|||
|
this.updateShapes(true);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Grapher
|
|||
|
//
|
|||
|
|
|||
|
function Grapher(canvas, range, columnWidth, useLocalScale, useRelativeScale) {
|
|||
|
this.setColumns = function(columns) {
|
|||
|
this.clearColumns();
|
|||
|
|
|||
|
var scale = 0;
|
|||
|
if (!useLocalScale) {
|
|||
|
var hintData = {};
|
|||
|
_.each(columns, function(columnValue, columnName) {
|
|||
|
hintData[columnName] = columnValue.hints || [];
|
|||
|
});
|
|||
|
|
|||
|
scale = this.getGlobalScale(hintData);
|
|||
|
}
|
|||
|
|
|||
|
var graphBounds = this.getGraphBounds(this.getCanvasBounds());
|
|||
|
|
|||
|
var index = 0;
|
|||
|
var that = this;
|
|||
|
_.each(columns, function(columnValue, columnName) {
|
|||
|
if (useLocalScale) {
|
|||
|
scale = that.getLocalScale(columnValue.hints);
|
|||
|
}
|
|||
|
|
|||
|
var columnBounds = that.getColumnBounds(graphBounds, index);
|
|||
|
that.columns.push(new Column(that.canvas, columnName, columnValue, scale, that.range, columnBounds));
|
|||
|
that.indexMap[columnName] = index++;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.clearColumns = function() {
|
|||
|
_.each(this.columns, function(column) {
|
|||
|
column.clearShapes();
|
|||
|
});
|
|||
|
|
|||
|
this.columns = [];
|
|||
|
this.indexMap = {};
|
|||
|
}
|
|||
|
|
|||
|
this.setColumnHints = function(hintData) {
|
|||
|
var scale = 0;
|
|||
|
if (!this.useLocalScale) {
|
|||
|
scale = this.getGlobalScale(hintData);
|
|||
|
}
|
|||
|
|
|||
|
var that = this;
|
|||
|
_.each(hintData, function(hints, name) {
|
|||
|
var index = that.getColumnIndex(name);
|
|||
|
console.assert(index >= 0);
|
|||
|
|
|||
|
if (that.useLocalScale) {
|
|||
|
scale = that.getLocalScale(hints);
|
|||
|
}
|
|||
|
|
|||
|
that.columns[index].setHints(hints, scale);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.setUseLocalScale = function(useLocalScale) {
|
|||
|
if (useLocalScale != this.useLocalScale) {
|
|||
|
this.useLocalScale = useLocalScale;
|
|||
|
this.invalidateHints();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.setUseRelativeScale = function(useRelativeScale) {
|
|||
|
if (useRelativeScale != this.useRelativeScale) {
|
|||
|
this.useRelativeScale = useRelativeScale;
|
|||
|
this.invalidateHints();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
this.invalidateHints = function() {
|
|||
|
var hintData = {};
|
|||
|
_.each(this.columns, function(column) {
|
|||
|
hintData[column.name] = column.hints;
|
|||
|
});
|
|||
|
|
|||
|
this.setColumnHints(hintData);
|
|||
|
}
|
|||
|
|
|||
|
this.setValueChangedListener = function(listener) {
|
|||
|
_.each(this.columns, function(column) {
|
|||
|
column.onValueChanged = listener;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.getLocalScale = function(hints) {
|
|||
|
var counts = _.pluck(hints, 'count');
|
|||
|
var min = this.useRelativeScale ? _.min(counts) : 0;
|
|||
|
return new goog.math.Range(min, _.max(counts));
|
|||
|
}
|
|||
|
|
|||
|
this.getGlobalScale = function(hintData) {
|
|||
|
var that = this;
|
|||
|
var globalScale = null;
|
|||
|
|
|||
|
_.each(hintData, function(hints) {
|
|||
|
var localScale = that.getLocalScale(hints);
|
|||
|
if (globalScale) {
|
|||
|
globalScale.includeRange(localScale);
|
|||
|
}
|
|||
|
else {
|
|||
|
globalScale = localScale;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
return globalScale;
|
|||
|
}
|
|||
|
|
|||
|
this.getColumnCount = function() {
|
|||
|
return this.columns.length;
|
|||
|
}
|
|||
|
|
|||
|
this.getColumnIndex = function(name) {
|
|||
|
console.assert(name in this.indexMap);
|
|||
|
return this.indexMap[name];
|
|||
|
}
|
|||
|
|
|||
|
this.getColumnName = function(index) {
|
|||
|
return this.columns[index].name;
|
|||
|
}
|
|||
|
|
|||
|
this.getColumnNames = function() {
|
|||
|
return _.pluck(this.columns, 'name');
|
|||
|
}
|
|||
|
|
|||
|
this.getCanvasBounds = function() {
|
|||
|
return new goog.math.Rect(0, 0, this.canvas.width - 1, this.canvas.height - 1);
|
|||
|
}
|
|||
|
|
|||
|
this.getGraphBounds = function(bounds) {
|
|||
|
return this.getCanvasBounds();
|
|||
|
}
|
|||
|
|
|||
|
this.getColumnBounds = function(bounds, index, count) {
|
|||
|
var width = this.columnWidth + this.padding * 2;
|
|||
|
return new goog.math.Rect(
|
|||
|
bounds.left + width * index + this.padding,
|
|||
|
bounds.top,
|
|||
|
this.columnWidth,
|
|||
|
bounds.height
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.getMousePos = function(e) {
|
|||
|
var rect = e.target.getBoundingClientRect();
|
|||
|
return new goog.math.Coordinate(
|
|||
|
e.clientX - rect.left,
|
|||
|
e.clientY - rect.top
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
this.mouseDown = function(e) {
|
|||
|
var position = this.grapher.getMousePos(e);
|
|||
|
_.each(this.grapher.columns, function(column) {
|
|||
|
column.mouseDown(position);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.mouseUp = function(e) {
|
|||
|
var position = this.grapher.getMousePos(e);
|
|||
|
_.each(this.grapher.columns, function(column) {
|
|||
|
column.mouseUp(position);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.mouseMove = function(e) {
|
|||
|
var position = this.grapher.getMousePos(e);
|
|||
|
_.each(this.grapher.columns, function(column) {
|
|||
|
column.mouseMove(position);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.mouseOut = function(e) {
|
|||
|
var position = this.grapher.getMousePos(e);
|
|||
|
_.each(this.grapher.columns, function(column) {
|
|||
|
column.mouseOut(position);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.mouseDoubleClick = function(e) {
|
|||
|
var position = this.grapher.getMousePos(e);
|
|||
|
_.each(this.grapher.columns, function(column) {
|
|||
|
column.mouseDoubleClick(position);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.useLocalScale = useLocalScale;
|
|||
|
this.useRelativeScale = useRelativeScale;
|
|||
|
this.columnWidth = columnWidth;
|
|||
|
this.canvas = new fabric.StaticCanvas(canvas);
|
|||
|
this.range = new goog.math.Range(range.min, range.max);
|
|||
|
this.padding = 10;
|
|||
|
this.indexMap = {};
|
|||
|
this.columns = [];
|
|||
|
|
|||
|
var c = this.canvas.contextContainer.canvas;
|
|||
|
c.addEventListener('mousedown', this.mouseDown, false);
|
|||
|
c.addEventListener('mouseup', this.mouseUp, false);
|
|||
|
c.addEventListener('mousemove', this.mouseMove, false);
|
|||
|
c.addEventListener('mouseout', this.mouseOut, false);
|
|||
|
c.addEventListener('dblclick', this.mouseDoubleClick, false);
|
|||
|
c.grapher = this;
|
|||
|
}
|