X-Git-Url: https://git.openstreetmap.org/rails.git/blobdiff_plain/ad368d189f4842f18e81fd272c677e5d820a2da4..2ac8b4b76057c293fc93f492359933db87abfc59:/vendor/assets/iD/iD.js diff --git a/vendor/assets/iD/iD.js b/vendor/assets/iD/iD.js index a19dc713e..47393cad2 100644 --- a/vendor/assets/iD/iD.js +++ b/vendor/assets/iD/iD.js @@ -174,11 +174,13 @@ } })(this); -d3 = (function(){ - var d3 = {version: "3.3.8"}; // semver -d3.ascending = function(a, b) { +!function(){ + var d3 = {version: "3.5.5"}; // semver +d3.ascending = d3_ascending; + +function d3_ascending(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; -}; +} d3.descending = function(a, b) { return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; }; @@ -188,10 +190,10 @@ d3.min = function(array, f) { a, b; if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; } while (++i < n) if ((b = array[i]) != null && a > b) a = b; } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; } while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; } return a; @@ -202,10 +204,10 @@ d3.max = function(array, f) { a, b; if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; } while (++i < n) if ((b = array[i]) != null && b > a) a = b; } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = b; break; } while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; } return a; @@ -217,13 +219,13 @@ d3.extent = function(array, f) { b, c; if (arguments.length === 1) { - while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined; + while (++i < n) if ((b = array[i]) != null && b >= b) { a = c = b; break; } while (++i < n) if ((b = array[i]) != null) { if (a > b) a = b; if (c < b) c = b; } } else { - while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { a = c = b; break; } while (++i < n) if ((b = f.call(array, array[i], i)) != null) { if (a > b) a = b; if (c < b) c = b; @@ -231,36 +233,39 @@ d3.extent = function(array, f) { } return [a, c]; }; +function d3_number(x) { + return x === null ? NaN : +x; +} + +function d3_numeric(x) { + return !isNaN(x); +} + d3.sum = function(array, f) { var s = 0, n = array.length, a, i = -1; - if (arguments.length === 1) { - while (++i < n) if (!isNaN(a = +array[i])) s += a; + while (++i < n) if (d3_numeric(a = +array[i])) s += a; // zero and null are equivalent } else { - while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; + while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a; } - return s; }; -function d3_number(x) { - return x != null && !isNaN(x); -} d3.mean = function(array, f) { - var n = array.length, + var s = 0, + n = array.length, a, - m = 0, i = -1, - j = 0; + j = n; if (arguments.length === 1) { - while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j; + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j; } else { - while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j; + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j; } - return j ? m : undefined; + if (j) return s / j; }; // R-7 per d3.quantile = function(values, p) { @@ -272,18 +277,59 @@ d3.quantile = function(values, p) { }; d3.median = function(array, f) { - if (arguments.length > 1) array = array.map(f); - array = array.filter(d3_number); - return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined; + var numbers = [], + n = array.length, + a, + i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a); + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a); + } + if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5); }; -d3.bisector = function(f) { + +d3.variance = function(array, f) { + var n = array.length, + m = 0, + a, + d, + s = 0, + i = -1, + j = 0; + if (arguments.length === 1) { + while (++i < n) { + if (d3_numeric(a = d3_number(array[i]))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } else { + while (++i < n) { + if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } + if (j > 1) return s / (j - 1); +}; + +d3.deviation = function() { + var v = d3.variance.apply(this, arguments); + return v ? Math.sqrt(v) : v; +}; + +function d3_bisector(compare) { return { left: function(a, x, lo, hi) { if (arguments.length < 3) lo = 0; if (arguments.length < 4) hi = a.length; while (lo < hi) { var mid = lo + hi >>> 1; - if (f.call(a, a[mid], mid) < x) lo = mid + 1; + if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; } return lo; @@ -293,22 +339,29 @@ d3.bisector = function(f) { if (arguments.length < 4) hi = a.length; while (lo < hi) { var mid = lo + hi >>> 1; - if (x < f.call(a, a[mid], mid)) hi = mid; + if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; } return lo; } }; -}; +} -var d3_bisector = d3.bisector(function(d) { return d; }); -d3.bisectLeft = d3_bisector.left; -d3.bisect = d3.bisectRight = d3_bisector.right; -d3.shuffle = function(array) { - var m = array.length, t, i; +var d3_bisect = d3_bisector(d3_ascending); +d3.bisectLeft = d3_bisect.left; +d3.bisect = d3.bisectRight = d3_bisect.right; + +d3.bisector = function(f) { + return d3_bisector(f.length === 1 + ? function(d, x) { return d3_ascending(f(d), x); } + : f); +}; +d3.shuffle = function(array, i0, i1) { + if ((m = arguments.length) < 3) { i1 = array.length; if (m < 2) i0 = 0; } + var m = i1 - i0, t, i; while (m) { i = Math.random() * m-- | 0; - t = array[m], array[m] = array[i], array[i] = t; + t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t; } return array; }; @@ -403,67 +456,96 @@ function d3_range_integerScale(x) { return k; } function d3_class(ctor, properties) { - try { - for (var key in properties) { - Object.defineProperty(ctor.prototype, key, { - value: properties[key], - enumerable: false - }); - } - } catch (e) { - ctor.prototype = properties; + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); } } -d3.map = function(object) { +d3.map = function(object, f) { var map = new d3_Map; - if (object instanceof d3_Map) object.forEach(function(key, value) { map.set(key, value); }); - else for (var key in object) map.set(key, object[key]); + if (object instanceof d3_Map) { + object.forEach(function(key, value) { map.set(key, value); }); + } else if (Array.isArray(object)) { + var i = -1, + n = object.length, + o; + if (arguments.length === 1) while (++i < n) map.set(i, object[i]); + else while (++i < n) map.set(f.call(object, o = object[i], i), o); + } else { + for (var key in object) map.set(key, object[key]); + } return map; }; -function d3_Map() {} +function d3_Map() { + this._ = Object.create(null); +} + +var d3_map_proto = "__proto__", + d3_map_zero = "\0"; d3_class(d3_Map, { - has: function(key) { - return d3_map_prefix + key in this; - }, + has: d3_map_has, get: function(key) { - return this[d3_map_prefix + key]; + return this._[d3_map_escape(key)]; }, set: function(key, value) { - return this[d3_map_prefix + key] = value; - }, - remove: function(key) { - key = d3_map_prefix + key; - return key in this && delete this[key]; - }, - keys: function() { - var keys = []; - this.forEach(function(key) { keys.push(key); }); - return keys; + return this._[d3_map_escape(key)] = value; }, + remove: d3_map_remove, + keys: d3_map_keys, values: function() { var values = []; - this.forEach(function(key, value) { values.push(value); }); + for (var key in this._) values.push(this._[key]); return values; }, entries: function() { var entries = []; - this.forEach(function(key, value) { entries.push({key: key, value: value}); }); + for (var key in this._) entries.push({key: d3_map_unescape(key), value: this._[key]}); return entries; }, + size: d3_map_size, + empty: d3_map_empty, forEach: function(f) { - for (var key in this) { - if (key.charCodeAt(0) === d3_map_prefixCode) { - f.call(this, key.substring(1), this[key]); - } - } + for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]); } }); -var d3_map_prefix = "\0", // prevent collision with built-ins - d3_map_prefixCode = d3_map_prefix.charCodeAt(0); +function d3_map_escape(key) { + return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key; +} + +function d3_map_unescape(key) { + return (key += "")[0] === d3_map_zero ? key.slice(1) : key; +} + +function d3_map_has(key) { + return d3_map_escape(key) in this._; +} + +function d3_map_remove(key) { + return (key = d3_map_escape(key)) in this._ && delete this._[key]; +} + +function d3_map_keys() { + var keys = []; + for (var key in this._) keys.push(d3_map_unescape(key)); + return keys; +} + +function d3_map_size() { + var size = 0; + for (var key in this._) ++size; + return size; +} + +function d3_map_empty() { + for (var key in this._) return false; + return true; +} d3.nest = function() { var nest = {}, @@ -567,52 +649,39 @@ d3.set = function(array) { return set; }; -function d3_Set() {} +function d3_Set() { + this._ = Object.create(null); +} d3_class(d3_Set, { - has: function(value) { - return d3_map_prefix + value in this; - }, - add: function(value) { - this[d3_map_prefix + value] = true; - return value; - }, - remove: function(value) { - value = d3_map_prefix + value; - return value in this && delete this[value]; - }, - values: function() { - var values = []; - this.forEach(function(value) { - values.push(value); - }); - return values; + has: d3_map_has, + add: function(key) { + this._[d3_map_escape(key += "")] = true; + return key; }, + remove: d3_map_remove, + values: d3_map_keys, + size: d3_map_size, + empty: d3_map_empty, forEach: function(f) { - for (var value in this) { - if (value.charCodeAt(0) === d3_map_prefixCode) { - f.call(this, value.substring(1)); - } - } + for (var key in this._) f.call(this, d3_map_unescape(key)); } }); d3.behavior = {}; -var d3_arraySlice = [].slice, - d3_array = function(list) { return d3_arraySlice.call(list); }; // conversion for NodeLists +var d3_document = this.document; -var d3_document = document, - d3_documentElement = d3_document.documentElement, - d3_window = window; - -// Redefine d3_array if the browser doesn’t support slice-based conversion. -try { - d3_array(d3_documentElement.childNodes)[0].nodeType; -} catch(e) { - d3_array = function(list) { - var i = list.length, array = new Array(i); - while (i--) array[i] = list[i]; - return array; - }; +function d3_documentElement(node) { + return node + && (node.ownerDocument // node is a Node + || node.document // node is a Window + || node).documentElement; // node is a Document +} + +function d3_window(node) { + return node + && ((node.ownerDocument && node.ownerDocument.defaultView) // node is a Node + || (node.document && node) // node is a Window + || node.defaultView); // node is a Document } // Copies a variable number of methods from source to target. d3.rebind = function(target, source) { @@ -630,10 +699,9 @@ function d3_rebind(target, source, method) { return value === source ? target : value; }; } - function d3_vendorSymbol(object, name) { if (name in object) return name; - name = name.charAt(0).toUpperCase() + name.substring(1); + name = name.charAt(0).toUpperCase() + name.slice(1); for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { var prefixName = d3_vendorPrefixes[i] + name; if (prefixName in object) return prefixName; @@ -641,6 +709,8 @@ function d3_vendorSymbol(object, name) { } var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"]; +var d3_arraySlice = [].slice, + d3_array = function(list) { return d3_arraySlice.call(list); }; // conversion for NodeLists function d3_noop() {} d3.dispatch = function() { @@ -659,8 +729,8 @@ d3_dispatch.prototype.on = function(type, listener) { // Extract optional namespace, e.g., "click.foo" if (i >= 0) { - name = type.substring(i + 1); - type = type.substring(0, i); + name = type.slice(i + 1); + type = type.slice(0, i); } if (type) return arguments.length < 2 @@ -788,18 +858,23 @@ function d3_selection(groups) { var d3_select = function(s, n) { return n.querySelector(s); }, d3_selectAll = function(s, n) { return n.querySelectorAll(s); }, - d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], - d3_selectMatches = function(n, s) { return d3_selectMatcher.call(n, s); }; + d3_selectMatches = function(n, s) { + var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; + d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + return d3_selectMatches(n, s); + }; // Prefer Sizzle, if available. if (typeof Sizzle === "function") { d3_select = function(s, n) { return Sizzle(s, n)[0] || null; }; - d3_selectAll = function(s, n) { return Sizzle.uniqueSort(Sizzle(s, n)); }; + d3_selectAll = Sizzle; d3_selectMatches = Sizzle.matchesSelector; } d3.selection = function() { - return d3_selectionRoot; + return d3.select(d3_document.documentElement); }; var d3_selectionPrototype = d3.selection.prototype = []; @@ -874,8 +949,8 @@ d3.ns = { var i = name.indexOf(":"), prefix = name; if (i >= 0) { - prefix = name.substring(0, i); - name = name.substring(i + 1); + prefix = name.slice(0, i); + name = name.slice(i + 1); } return d3_nsPrefix.hasOwnProperty(prefix) ? {space: d3_nsPrefix[prefix], local: name} @@ -954,7 +1029,7 @@ d3_selectionPrototype.classed = function(name, value) { // probably doesn't support it on SVG elements (which can be animated). if (typeof name === "string") { var node = this.node(), - n = (name = name.trim().split(/^|\s+/g)).length, + n = (name = d3_selection_classes(name)).length, i = -1; if (value = node.classList) { while (++i < n) if (!value.contains(name[i])) return false; @@ -979,9 +1054,13 @@ function d3_selection_classedRe(name) { return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); } +function d3_selection_classes(name) { + return (name + "").trim().split(/^|\s+/); +} + // Multiple class names are allowed (e.g., "foo bar"). function d3_selection_classed(name, value) { - name = name.trim().split(/\s+/).map(d3_selection_classedName); + name = d3_selection_classes(name).map(d3_selection_classedName); var n = name.length; function classedConstant() { @@ -1030,7 +1109,10 @@ d3_selectionPrototype.style = function(name, value, priority) { } // For style(string), return the computed style value for the first node. - if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); + if (n < 2) { + var node = this.node(); + return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); + } // For style(string, string) or style(string, function), use the default // priority. The priority is ignored for style(string, null). @@ -1137,9 +1219,22 @@ d3_selectionPrototype.append = function(name) { }; function d3_selection_creator(name) { + + function create() { + var document = this.ownerDocument, + namespace = this.namespaceURI; + return namespace + ? document.createElementNS(namespace, name) + : document.createElement(name); + } + + function createNS() { + return this.ownerDocument.createElementNS(name.space, name.local); + } + return typeof name === "function" ? name - : (name = d3.ns.qualify(name)).local ? function() { return this.ownerDocument.createElementNS(name.space, name.local); } - : function() { return this.ownerDocument.createElementNS(this.namespaceURI, name); }; + : (name = d3.ns.qualify(name)).local ? createNS + : create; } d3_selectionPrototype.insert = function(name, before) { @@ -1154,12 +1249,14 @@ d3_selectionPrototype.insert = function(name, before) { // TODO remove(node)? // TODO remove(function)? d3_selectionPrototype.remove = function() { - return this.each(function() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); - }); + return this.each(d3_selectionRemove); }; +function d3_selectionRemove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); +} + d3_selectionPrototype.data = function(value, key) { var i = -1, n = this.length, @@ -1190,34 +1287,30 @@ d3_selectionPrototype.data = function(value, key) { if (key) { var nodeByKeyValue = new d3_Map, - dataByKeyValue = new d3_Map, - keyValues = [], + keyValues = new Array(n), keyValue; for (i = -1; ++i < n;) { - keyValue = key.call(node = group[i], node.__data__, i); - if (nodeByKeyValue.has(keyValue)) { + if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) { exitNodes[i] = node; // duplicate selection key } else { nodeByKeyValue.set(keyValue, node); } - keyValues.push(keyValue); + keyValues[i] = keyValue; } for (i = -1; ++i < m;) { - keyValue = key.call(groupData, nodeData = groupData[i], i); - if (node = nodeByKeyValue.get(keyValue)) { + if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } else if (node !== true) { // no duplicate data key updateNodes[i] = node; node.__data__ = nodeData; - } else if (!dataByKeyValue.has(keyValue)) { // no duplicate data key - enterNodes[i] = d3_selection_dataNode(nodeData); } - dataByKeyValue.set(keyValue, nodeData); - nodeByKeyValue.remove(keyValue); + nodeByKeyValue.set(keyValue, true); } for (i = -1; ++i < n;) { - if (nodeByKeyValue.has(keyValues[i])) { + if (nodeByKeyValue.get(keyValues[i]) !== true) { exitNodes[i] = group[i]; } } @@ -1294,7 +1387,7 @@ d3_selectionPrototype.filter = function(filter) { subgroups.push(subgroup = []); subgroup.parentNode = (group = this[j]).parentNode; for (var i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i)) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { subgroup.push(node); } } @@ -1328,7 +1421,7 @@ d3_selectionPrototype.sort = function(comparator) { }; function d3_selection_sortComparator(comparator) { - if (!arguments.length) comparator = d3.ascending; + if (!arguments.length) comparator = d3_ascending; return function(a, b) { return a && b ? comparator(a.__data__, b.__data__) : !a - !b; }; @@ -1371,7 +1464,7 @@ d3_selectionPrototype.node = function() { d3_selectionPrototype.size = function() { var n = 0; - this.each(function() { ++n; }); + d3_selection_each(this, function() { ++n; }); return n; }; @@ -1435,51 +1528,31 @@ function d3_selection_enterInsertBefore(enter) { }; } -// import "../transition/transition"; - -d3_selectionPrototype.transition = function() { - var id = d3_transitionInheritId || ++d3_transitionId, - subgroups = [], - subgroup, - node, - transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250}; - - for (var j = -1, m = this.length; ++j < m;) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n;) { - if (node = group[i]) d3_transitionNode(node, i, id, transition); - subgroup.push(node); - } - } - - return d3_transition(subgroups, id); -}; -// import "../transition/transition"; - -d3_selectionPrototype.interrupt = function() { - return this.each(d3_selection_interrupt); -}; - -function d3_selection_interrupt() { - var lock = this.__transition__; - if (lock) ++lock.active; -} - // TODO fast singleton implementation? d3.select = function(node) { - var group = [typeof node === "string" ? d3_select(node, d3_document) : node]; - group.parentNode = d3_documentElement; + var group; + if (typeof node === "string") { + group = [d3_select(node, d3_document)]; + group.parentNode = d3_document.documentElement; + } else { + group = [node]; + group.parentNode = d3_documentElement(node); + } return d3_selection([group]); }; d3.selectAll = function(nodes) { - var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes); - group.parentNode = d3_documentElement; + var group; + if (typeof nodes === "string") { + group = d3_array(d3_selectAll(nodes, d3_document)); + group.parentNode = d3_document.documentElement; + } else { + group = nodes; + group.parentNode = null; + } return d3_selection([group]); }; -var d3_selectionRoot = d3.select(d3_documentElement); - d3_selectionPrototype.on = function(type, listener, capture) { var n = arguments.length; if (n < 3) { @@ -1509,7 +1582,7 @@ function d3_selection_on(type, listener, capture) { i = type.indexOf("."), wrap = d3_selection_onListener; - if (i > 0) type = type.substring(0, i); + if (i > 0) type = type.slice(0, i); var filter = d3_selection_onFilters.get(type); if (filter) type = filter, wrap = d3_selection_onFilter; @@ -1551,9 +1624,11 @@ var d3_selection_onFilters = d3.map({ mouseleave: "mouseout" }); -d3_selection_onFilters.forEach(function(k) { - if ("on" + k in d3_document) d3_selection_onFilters.remove(k); -}); +if (d3_document) { + d3_selection_onFilters.forEach(function(k) { + if ("on" + k in d3_document) d3_selection_onFilters.remove(k); + }); +} function d3_selection_onListener(listener, argumentz) { return function(e) { @@ -1578,24 +1653,33 @@ function d3_selection_onFilter(listener, argumentz) { }; } -var d3_event_dragSelect = d3_vendorSymbol(d3_documentElement.style, "userSelect"), +var d3_event_dragSelect, d3_event_dragId = 0; -function d3_event_dragSuppress() { +function d3_event_dragSuppress(node) { var name = ".dragsuppress-" + ++d3_event_dragId, - touchmove = "touchmove" + name, - selectstart = "selectstart" + name, - dragstart = "dragstart" + name, click = "click" + name, - w = d3.select(d3_window).on(touchmove, d3_eventPreventDefault).on(selectstart, d3_eventPreventDefault).on(dragstart, d3_eventPreventDefault), - style = d3_documentElement.style, - select = style[d3_event_dragSelect]; - style[d3_event_dragSelect] = "none"; + w = d3.select(d3_window(node)) + .on("touchmove" + name, d3_eventPreventDefault) + .on("dragstart" + name, d3_eventPreventDefault) + .on("selectstart" + name, d3_eventPreventDefault); + + if (d3_event_dragSelect == null) { + d3_event_dragSelect = "onselectstart" in node ? false + : d3_vendorSymbol(node.style, "userSelect"); + } + + if (d3_event_dragSelect) { + var style = d3_documentElement(node).style, + select = style[d3_event_dragSelect]; + style[d3_event_dragSelect] = "none"; + } + return function(suppressClick) { w.on(name, null); - style[d3_event_dragSelect] = select; + if (d3_event_dragSelect) style[d3_event_dragSelect] = select; if (suppressClick) { // suppress the next click, but only if it’s immediate - function off() { w.on(click, null); } + var off = function() { w.on(click, null); }; w.on(click, function() { d3_eventCancel(); off(); }, true); setTimeout(off, 0); } @@ -1607,25 +1691,28 @@ d3.mouse = function(container) { }; // https://bugs.webkit.org/show_bug.cgi?id=44083 -var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0; +var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; function d3_mousePoint(container, e) { if (e.changedTouches) e = e.changedTouches[0]; var svg = container.ownerSVGElement || container; if (svg.createSVGPoint) { var point = svg.createSVGPoint(); - if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) { - svg = d3.select("body").append("svg").style({ - position: "absolute", - top: 0, - left: 0, - margin: 0, - padding: 0, - border: "none" - }, "important"); - var ctm = svg[0][0].getScreenCTM(); - d3_mouse_bug44083 = !(ctm.f || ctm.e); - svg.remove(); + if (d3_mouse_bug44083 < 0) { + var window = d3_window(container); + if (window.scrollX || window.scrollY) { + svg = d3.select("body").append("svg").style({ + position: "absolute", + top: 0, + left: 0, + margin: 0, + padding: 0, + border: "none" + }, "important"); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } } if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, point.y = e.clientY; @@ -1644,11 +1731,12 @@ d3.touches = function(container, touches) { return point; }) : []; }; -var π = Math.PI, +var ε = 1e-6, + ε2 = ε * ε, + π = Math.PI, τ = 2 * π, + τε = τ - ε, halfπ = π / 2, - ε = 1e-6, - ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π; @@ -1656,6 +1744,14 @@ function d3_sgn(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; } +// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of +// the 3D cross product in a quadrant I Cartesian coordinate system (+x is +// right, +y is up). Returns a positive value if ABC is counter-clockwise, +// negative if clockwise, and zero if the points are collinear. +function d3_cross2d(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); +} + function d3_acos(x) { return x > 1 ? 0 : x < -1 ? π : Math.acos(x); } @@ -1729,9 +1825,12 @@ d3.interpolateZoom = function(p0, p1) { d3.behavior.zoom = function() { var view = {x: 0, y: 0, k: 1}, translate0, // translate when we started zooming (to avoid drift) - center, // desired position of translate0 after zooming + center0, // implicit desired position of translate0 after zooming + center, // explicit desired position of translate0 after zooming size = [960, 500], // viewport size; required for zoom interpolation scaleExtent = d3_behavior_zoomInfinity, + duration = 250, + zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", @@ -1744,47 +1843,57 @@ d3.behavior.zoom = function() { y0, y1; + // Lazily determine the DOM’s support for Wheel events. + // https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel + if (!d3_behavior_zoomWheel) { + d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel") + : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel") + : (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll"); + } + function zoom(g) { g .on(mousedown, mousedowned) .on(d3_behavior_zoomWheel + ".zoom", mousewheeled) - .on(mousemove, mousewheelreset) .on("dblclick.zoom", dblclicked) .on(touchstart, touchstarted); } zoom.event = function(g) { g.each(function() { - var event_ = event.of(this, arguments), + var dispatch = event.of(this, arguments), view1 = view; if (d3_transitionInheritId) { - d3.select(this).transition() - .each("start.zoom", function() { - view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state - zoomstarted(event_); - }) - .tween("zoom:zoom", function() { - var dx = size[0], - dy = size[1], - cx = dx / 2, - cy = dy / 2, - i = d3.interpolateZoom( - [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k], - [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k] - ); - return function(t) { - var l = i(t), k = dx / l[2]; - this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k}; - zoomed(event_); - }; - }) - .each("end.zoom", function() { - zoomended(event_); - }); + d3.select(this).transition() + .each("start.zoom", function() { + view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state + zoomstarted(dispatch); + }) + .tween("zoom:zoom", function() { + var dx = size[0], + dy = size[1], + cx = center0 ? center0[0] : dx / 2, + cy = center0 ? center0[1] : dy / 2, + i = d3.interpolateZoom( + [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k], + [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k] + ); + return function(t) { + var l = i(t), k = dx / l[2]; + this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k}; + zoomed(dispatch); + }; + }) + .each("interrupt.zoom", function() { + zoomended(dispatch); + }) + .each("end.zoom", function() { + zoomended(dispatch); + }); } else { this.__chart__ = view; - zoomstarted(event_); - zoomed(event_); - zoomended(event_); + zoomstarted(dispatch); + zoomed(dispatch); + zoomended(dispatch); } }); } @@ -1821,6 +1930,12 @@ d3.behavior.zoom = function() { return zoom; }; + zoom.duration = function(_) { + if (!arguments.length) return duration; + duration = +_; // TODO function based on interpolateZoom distance? + return zoom; + }; + zoom.x = function(z) { if (!arguments.length) return x1; x1 = z; @@ -1855,70 +1970,85 @@ d3.behavior.zoom = function() { view.y += p[1] - l[1]; } + function zoomTo(that, p, l, k) { + that.__chart__ = {x: view.x, y: view.y, k: view.k}; + + scaleTo(Math.pow(2, k)); + translateTo(center0 = p, l); + + that = d3.select(that); + if (duration > 0) that = that.transition().duration(duration); + that.call(zoom.event); + } + function rescale() { if (x1) x1.domain(x0.range().map(function(x) { return (x - view.x) / view.k; }).map(x0.invert)); if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert)); } - function zoomstarted(event) { - event({type: "zoomstart"}); + function zoomstarted(dispatch) { + if (!zooming++) dispatch({type: "zoomstart"}); } - function zoomed(event) { + function zoomed(dispatch) { rescale(); - event({type: "zoom", scale: view.k, translate: [view.x, view.y]}); + dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]}); } - function zoomended(event) { - event({type: "zoomend"}); + function zoomended(dispatch) { + if (!--zooming) dispatch({type: "zoomend"}); + center0 = null; } function mousedowned() { - var target = this, - event_ = event.of(target, arguments), - eventTarget = d3.event.target, + var that = this, + target = d3.event.target, + dispatch = event.of(that, arguments), dragged = 0, - w = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), - l = location(d3.mouse(target)), - dragRestore = d3_event_dragSuppress(); + subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), + location0 = location(d3.mouse(that)), + dragRestore = d3_event_dragSuppress(that); - d3_selection_interrupt.call(target); - zoomstarted(event_); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); function moved() { dragged = 1; - translateTo(d3.mouse(target), l); - zoomed(event_); + translateTo(d3.mouse(that), location0); + zoomed(dispatch); } function ended() { - w.on(mousemove, d3_window === target ? mousewheelreset : null).on(mouseup, null); - dragRestore(dragged && d3.event.target === eventTarget); - zoomended(event_); + subject.on(mousemove, null).on(mouseup, null); + dragRestore(dragged && d3.event.target === target); + zoomended(dispatch); } } // These closures persist for as long as at least one touch is active. function touchstarted() { - var target = this, - event_ = event.of(target, arguments), + var that = this, + dispatch = event.of(that, arguments), locations0 = {}, // touchstart locations distance0 = 0, // distance² between initial touches scale0, // scale when we started touching - eventId = d3.event.changedTouches[0].identifier, - touchmove = "touchmove.zoom-" + eventId, - touchend = "touchend.zoom-" + eventId, - w = d3.select(d3_window).on(touchmove, moved).on(touchend, ended), - t = d3.select(target).on(mousedown, null).on(touchstart, started), // prevent duplicate events - dragRestore = d3_event_dragSuppress(); - - d3_selection_interrupt.call(target); + zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, + touchmove = "touchmove" + zoomName, + touchend = "touchend" + zoomName, + targets = [], + subject = d3.select(that), + dragRestore = d3_event_dragSuppress(that); + started(); - zoomstarted(event_); + zoomstarted(dispatch); + + // Workaround for Chrome issue 412723: the touchstart listener must be set + // after the touchmove listener. + subject.on(mousedown, null).on(touchstart, started); // prevent duplicate events // Updates locations of any touches in locations0. function relocate() { - var touches = d3.touches(target); + var touches = d3.touches(that); scale0 = view.k; touches.forEach(function(t) { if (t.identifier in locations0) locations0[t.identifier] = location(t); @@ -1928,7 +2058,13 @@ d3.behavior.zoom = function() { // Temporarily override touchstart while gesture is active. function started() { - // Only track touches started on the target element. + + // Listen for touchmove and touchend on the target of touchstart. + var target = d3.event.target; + d3.select(target).on(touchmove, moved).on(touchend, ended); + targets.push(target); + + // Only track touches started on the same subject element. var changed = d3.event.changedTouches; for (var i = 0, n = changed.length; i < n; ++i) { locations0[changed[i].identifier] = null; @@ -1939,11 +2075,9 @@ d3.behavior.zoom = function() { if (touches.length === 1) { if (now - touchtime < 500) { // dbltap - var p = touches[0], l = locations0[p.identifier]; - scaleTo(view.k * 2); - translateTo(p, l); + var p = touches[0]; + zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); d3_eventPreventDefault(); - zoomed(event_); } touchtime = now; } else if (touches.length > 1) { @@ -1954,9 +2088,12 @@ d3.behavior.zoom = function() { } function moved() { - var touches = d3.touches(target), + var touches = d3.touches(that), p0, l0, p1, l1; + + d3_selection_interrupt.call(that); + for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { p1 = touches[i]; if (l1 = locations0[p1.identifier]) { @@ -1975,7 +2112,7 @@ d3.behavior.zoom = function() { touchtime = null; translateTo(p0, l0); - zoomed(event_); + zoomed(dispatch); } function ended() { @@ -1993,64 +2130,58 @@ d3.behavior.zoom = function() { } } // Otherwise, remove touchmove and touchend listeners. - w.on(touchmove, null).on(touchend, null); - t.on(mousedown, mousedowned).on(touchstart, touchstarted); + d3.selectAll(targets).on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); dragRestore(); - zoomended(event_); + zoomended(dispatch); } } function mousewheeled() { - var event_ = event.of(this, arguments); + var dispatch = event.of(this, arguments); if (mousewheelTimer) clearTimeout(mousewheelTimer); - else d3_selection_interrupt.call(this), zoomstarted(event_); - mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(event_); }, 50); + else translate0 = location(center0 = center || d3.mouse(this)), d3_selection_interrupt.call(this), zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50); d3_eventPreventDefault(); - var point = center || d3.mouse(this); - if (!translate0) translate0 = location(point); scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); - translateTo(point, translate0); - zoomed(event_); - } - - function mousewheelreset() { - translate0 = null; + translateTo(center0, translate0); + zoomed(dispatch); } function dblclicked() { - var event_ = event.of(this, arguments), - p = d3.mouse(this), - l = location(p), + var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2; - zoomstarted(event_); - scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); - translateTo(p, l); - zoomed(event_); - zoomended(event_); + + zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1); } return d3.rebind(zoom, event, "on"); }; -var d3_behavior_zoomInfinity = [0, Infinity]; // default scale extent - -// https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel -var d3_behavior_zoomDelta, d3_behavior_zoomWheel - = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel") - : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel") - : (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll"); +var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent + d3_behavior_zoomDelta, // initialized lazily + d3_behavior_zoomWheel; function d3_functor(v) { return typeof v === "function" ? v : function() { return v; }; } d3.functor = d3_functor; +d3.touch = function(container, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; + if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return d3_mousePoint(container, touch); + } + } +}; + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, // is an interval (or frame) active? d3_timer_timeout, // is a timeout active? d3_timer_active, // active timer object - d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); }; + d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); }; // The timer will continue to fire until callback returns true. d3.timer = function(callback, delay, then) { @@ -2255,7 +2386,6 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { clip.lineEnd = ringEnd; segments = []; polygon = []; - listener.polygonStart(); }, polygonEnd: function() { clip.point = point; @@ -2265,13 +2395,15 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { segments = d3.merge(segments); var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; listener.lineStart(); interpolate(null, null, 1, listener); listener.lineEnd(); } - listener.polygonEnd(); + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; segments = polygon = null; }, sphere: function() { @@ -2298,6 +2430,7 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), + polygonStarted = false, polygon, ring; @@ -2333,9 +2466,12 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { var n = segment.length - 1, i = -1, point; - listener.lineStart(); - while (++i < n) listener.point((point = segment[i])[0], point[1]); - listener.lineEnd(); + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } return; } @@ -2379,171 +2515,96 @@ function d3_geo_clipSort(a, b) { return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); } -// Adds floating point numbers with twice the normal precision. -// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and -// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) -// 305–363 (1997). -// Code adapted from GeographicLib by Charles F. F. Karney, -// http://geographiclib.sourceforge.net/ -// See lib/geographiclib/LICENSE for details. - -function d3_adder() {} -d3_adder.prototype = { - s: 0, // rounded value - t: 0, // exact error - add: function(y) { - d3_adderSum(y, this.t, d3_adderTemp); - d3_adderSum(d3_adderTemp.s, this.s, this); - if (this.s) this.t += d3_adderTemp.t; - else this.s = d3_adderTemp.t; - }, - reset: function() { - this.s = this.t = 0; - }, - valueOf: function() { - return this.s; - } -}; - -var d3_adderTemp = new d3_adder; - -function d3_adderSum(a, b, o) { - var x = o.s = a + b, // a + b - bv = x - a, av = x - bv; // b_virtual & a_virtual - o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff -} - -d3.geo.stream = function(object, listener) { - if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { - d3_geo_streamObjectType[object.type](object, listener); - } else { - d3_geo_streamGeometry(object, listener); - } -}; - -function d3_geo_streamGeometry(geometry, listener) { - if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { - d3_geo_streamGeometryType[geometry.type](geometry, listener); - } -} - -var d3_geo_streamObjectType = { - Feature: function(feature, listener) { - d3_geo_streamGeometry(feature.geometry, listener); - }, - FeatureCollection: function(object, listener) { - var features = object.features, i = -1, n = features.length; - while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); - } -}; +var d3_geo_clipAntimeridian = d3_geo_clip( + d3_true, + d3_geo_clipAntimeridianLine, + d3_geo_clipAntimeridianInterpolate, + [-π, -π / 2]); -var d3_geo_streamGeometryType = { - Sphere: function(object, listener) { - listener.sphere(); - }, - Point: function(object, listener) { - object = object.coordinates; - listener.point(object[0], object[1], object[2]); - }, - MultiPoint: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); - }, - LineString: function(object, listener) { - d3_geo_streamLine(object.coordinates, listener, 0); - }, - MultiLineString: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); - }, - Polygon: function(object, listener) { - d3_geo_streamPolygon(object.coordinates, listener); - }, - MultiPolygon: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); - }, - GeometryCollection: function(object, listener) { - var geometries = object.geometries, i = -1, n = geometries.length; - while (++i < n) d3_geo_streamGeometry(geometries[i], listener); - } -}; +// Takes a line and cuts into visible segments. Return values: +// 0: there were intersections or the line was empty. +// 1: no intersections. +// 2: there were intersections, and the first and last segments should be +// rejoined. +function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, + φ0 = NaN, + sλ0 = NaN, + clean; // no intersections -function d3_geo_streamLine(coordinates, listener, closed) { - var i = -1, n = coordinates.length - closed, coordinate; - listener.lineStart(); - while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); - listener.lineEnd(); + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, + dλ = abs(λ1 - λ0); + if (abs(dλ - π) < ε) { // line crosses a pole + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian + // handle degeneracies + if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + // if there are intersections, we always rejoin the first and last segments. + clean: function() { return 2 - clean; } + }; } -function d3_geo_streamPolygon(coordinates, listener) { - var i = -1, n = coordinates.length; - listener.polygonStart(); - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); - listener.polygonEnd(); +function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, + cosφ1, + sinλ0_λ1 = Math.sin(λ0 - λ1); + return abs(sinλ0_λ1) > ε + ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) + - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) + / (cosφ0 * cosφ1 * sinλ0_λ1)) + : (φ0 + φ1) / 2; } -d3.geo.area = function(object) { - d3_geo_areaSum = 0; - d3.geo.stream(object, d3_geo_area); - return d3_geo_areaSum; -}; - -var d3_geo_areaSum, - d3_geo_areaRingSum = new d3_adder; - -var d3_geo_area = { - sphere: function() { d3_geo_areaSum += 4 * π; }, - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - - // Only count area for polygon rings. - polygonStart: function() { - d3_geo_areaRingSum.reset(); - d3_geo_area.lineStart = d3_geo_areaRingStart; - }, - polygonEnd: function() { - var area = 2 * d3_geo_areaRingSum; - d3_geo_areaSum += area < 0 ? 4 * π + area : area; - d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; - } -}; - -function d3_geo_areaRingStart() { - var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point - - // For the first point, … - d3_geo_area.point = function(λ, φ) { - d3_geo_area.point = nextPoint; - λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ); - }; - - // For subsequent points, … - function nextPoint(λ, φ) { - λ *= d3_radians; - φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole - - // Spherical excess E for a spherical triangle with vertices: south pole, - // previous point, current point. Uses a formula derived from Cagnoli’s - // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). - var dλ = λ - λ0, - cosφ = Math.cos(φ), - sinφ = Math.sin(φ), - k = sinφ0 * sinφ, - u = cosφ0 * cosφ + k * Math.cos(dλ), - v = k * Math.sin(dλ); - d3_geo_areaRingSum.add(Math.atan2(v, u)); - - // Advance the previous points. - λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; +function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * halfπ; + listener.point(-π, φ); + listener.point( 0, φ); + listener.point( π, φ); + listener.point( π, 0); + listener.point( π, -φ); + listener.point( 0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? π : -π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point( 0, φ); + listener.point( s, φ); + } else { + listener.point(to[0], to[1]); } - - // For the last point, return to the start. - d3_geo_area.lineEnd = function() { - nextPoint(λ00, φ00); - }; } // TODO // cross and scale return new vectors, @@ -2592,160 +2653,17 @@ function d3_geo_cartesianNormalize(d) { d[1] /= l; d[2] /= l; } +function d3_geo_compose(a, b) { -function d3_geo_pointInPolygon(point, polygon) { - var meridian = point[0], - parallel = point[1], - meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0], - polarAngle = 0, - winding = 0; - d3_geo_areaRingSum.reset(); - - for (var i = 0, n = polygon.length; i < n; ++i) { - var ring = polygon[i], - m = ring.length; - if (!m) continue; - var point0 = ring[0], - λ0 = point0[0], - φ0 = point0[1] / 2 + π / 4, - sinφ0 = Math.sin(φ0), - cosφ0 = Math.cos(φ0), - j = 1; - - while (true) { - if (j === m) j = 0; - point = ring[j]; - var λ = point[0], - φ = point[1] / 2 + π / 4, - sinφ = Math.sin(φ), - cosφ = Math.cos(φ), - dλ = λ - λ0, - antimeridian = abs(dλ) > π, - k = sinφ0 * sinφ; - d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosφ0 * cosφ + k * Math.cos(dλ))); - - polarAngle += antimeridian ? dλ + (dλ >= 0 ? τ : -τ): dλ; - - // Are the longitudes either side of the point's meridian, and are the - // latitudes smaller than the parallel? - if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { - var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); - d3_geo_cartesianNormalize(arc); - var intersection = d3_geo_cartesianCross(meridianNormal, arc); - d3_geo_cartesianNormalize(intersection); - var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); - if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { - winding += antimeridian ^ dλ >= 0 ? 1 : -1; - } - } - if (!j++) break; - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; - } + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); } - // First, determine whether the South pole is inside or outside: - // - // It is inside if: - // * the polygon winds around it in a clockwise direction. - // * the polygon does not (cumulatively) wind around it, but has a negative - // (counter-clockwise) area. - // - // Second, count the (signed) number of times a segment crosses a meridian - // from the point to the South pole. If it is zero, then the point is the - // same side as the South pole. - - return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1); -} - -var d3_geo_clipAntimeridian = d3_geo_clip( - d3_true, - d3_geo_clipAntimeridianLine, - d3_geo_clipAntimeridianInterpolate, - [-π, -π / 2]); - -// Takes a line and cuts into visible segments. Return values: -// 0: there were intersections or the line was empty. -// 1: no intersections. -// 2: there were intersections, and the first and last segments should be -// rejoined. -function d3_geo_clipAntimeridianLine(listener) { - var λ0 = NaN, - φ0 = NaN, - sλ0 = NaN, - clean; // no intersections - - return { - lineStart: function() { - listener.lineStart(); - clean = 1; - }, - point: function(λ1, φ1) { - var sλ1 = λ1 > 0 ? π : -π, - dλ = abs(λ1 - λ0); - if (abs(dλ - π) < ε) { // line crosses a pole - listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - listener.point( λ1, φ0); - clean = 0; - } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian - // handle degeneracies - if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; - if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; - φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - clean = 0; - } - listener.point(λ0 = λ1, φ0 = φ1); - sλ0 = sλ1; - }, - lineEnd: function() { - listener.lineEnd(); - λ0 = φ0 = NaN; - }, - // if there are intersections, we always rejoin the first and last segments. - clean: function() { return 2 - clean; } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); }; -} -function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { - var cosφ0, - cosφ1, - sinλ0_λ1 = Math.sin(λ0 - λ1); - return abs(sinλ0_λ1) > ε - ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) - / (cosφ0 * cosφ1 * sinλ0_λ1)) - : (φ0 + φ1) / 2; -} - -function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { - var φ; - if (from == null) { - φ = direction * halfπ; - listener.point(-π, φ); - listener.point( 0, φ); - listener.point( π, φ); - listener.point( π, 0); - listener.point( π, -φ); - listener.point( 0, -φ); - listener.point(-π, -φ); - listener.point(-π, 0); - listener.point(-π, φ); - } else if (abs(from[0] - to[0]) > ε) { - var s = from[0] < to[0] ? π : -π; - φ = direction * s / 2; - listener.point(-s, φ); - listener.point( 0, φ); - listener.point( s, φ); - } else { - listener.point(to[0], to[1]); - } + return compose; } function d3_geo_equirectangular(λ, φ) { @@ -2906,6 +2824,240 @@ function d3_geo_circleAngle(cr, point) { var angle = d3_acos(-a[1]); return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); } +// Adds floating point numbers with twice the normal precision. +// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and +// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) +// 305–363 (1997). +// Code adapted from GeographicLib by Charles F. F. Karney, +// http://geographiclib.sourceforge.net/ +// See lib/geographiclib/LICENSE for details. + +function d3_adder() {} + +d3_adder.prototype = { + s: 0, // rounded value + t: 0, // exact error + add: function(y) { + d3_adderSum(y, this.t, d3_adderTemp); + d3_adderSum(d3_adderTemp.s, this.s, this); + if (this.s) this.t += d3_adderTemp.t; + else this.s = d3_adderTemp.t; + }, + reset: function() { + this.s = this.t = 0; + }, + valueOf: function() { + return this.s; + } +}; + +var d3_adderTemp = new d3_adder; + +function d3_adderSum(a, b, o) { + var x = o.s = a + b, // a + b + bv = x - a, av = x - bv; // b_virtual & a_virtual + o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff +} + +d3.geo.stream = function(object, listener) { + if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } +}; + +function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } +} + +var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } +}; + +var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + object = object.coordinates; + listener.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } +}; + +function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); + listener.lineEnd(); +} + +function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); +} + +d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; +}; + +var d3_geo_areaSum, + d3_geo_areaRingSum = new d3_adder; + +var d3_geo_area = { + sphere: function() { d3_geo_areaSum += 4 * π; }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + + // Only count area for polygon rings. + polygonStart: function() { + d3_geo_areaRingSum.reset(); + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * d3_geo_areaRingSum; + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } +}; + +function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point + + // For the first point, … + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), sinφ0 = Math.sin(φ); + }; + + // For subsequent points, … + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; // half the angular distance from south pole + + // Spherical excess E for a spherical triangle with vertices: south pole, + // previous point, current point. Uses a formula derived from Cagnoli’s + // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). + var dλ = λ - λ0, + sdλ = dλ >= 0 ? 1 : -1, + adλ = sdλ * dλ, + cosφ = Math.cos(φ), + sinφ = Math.sin(φ), + k = sinφ0 * sinφ, + u = cosφ0 * cosφ + k * Math.cos(adλ), + v = k * sdλ * Math.sin(adλ); + d3_geo_areaRingSum.add(Math.atan2(v, u)); + + // Advance the previous points. + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + + // For the last point, return to the start. + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; +} + +function d3_geo_pointInPolygon(point, polygon) { + var meridian = point[0], + parallel = point[1], + meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0], + polarAngle = 0, + winding = 0; + d3_geo_areaRingSum.reset(); + + for (var i = 0, n = polygon.length; i < n; ++i) { + var ring = polygon[i], + m = ring.length; + if (!m) continue; + var point0 = ring[0], + λ0 = point0[0], + φ0 = point0[1] / 2 + π / 4, + sinφ0 = Math.sin(φ0), + cosφ0 = Math.cos(φ0), + j = 1; + + while (true) { + if (j === m) j = 0; + point = ring[j]; + var λ = point[0], + φ = point[1] / 2 + π / 4, + sinφ = Math.sin(φ), + cosφ = Math.cos(φ), + dλ = λ - λ0, + sdλ = dλ >= 0 ? 1 : -1, + adλ = sdλ * dλ, + antimeridian = adλ > π, + k = sinφ0 * sinφ; + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); + + polarAngle += antimeridian ? dλ + sdλ * τ : dλ; + + // Are the longitudes either side of the point's meridian, and are the + // latitudes smaller than the parallel? + if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { + var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); + d3_geo_cartesianNormalize(arc); + var intersection = d3_geo_cartesianCross(meridianNormal, arc); + d3_geo_cartesianNormalize(intersection); + var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); + if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { + winding += antimeridian ^ dλ >= 0 ? 1 : -1; + } + } + if (!j++) break; + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; + } + } + + // First, determine whether the South pole is inside or outside: + // + // It is inside if: + // * the polygon winds around it in a clockwise direction. + // * the polygon does not (cumulatively) wind around it, but has a negative + // (counter-clockwise) area. + // + // Second, count the (signed) number of times a segment crosses a meridian + // from the point to the South pole. If it is zero, then the point is the + // same side as the South pole. + + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1); +} // Clip features against a small circle centered at [0°, 0°]. function d3_geo_clipCircle(radius) { @@ -3216,9 +3368,9 @@ function d3_geo_clipExtent(x0, y0, x1, y1) { for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { b = v[j]; if (a[1] <= y) { - if (b[1] > y && isLeft(a, b, p) > 0) ++wn; + if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; } else { - if (b[1] <= y && isLeft(a, b, p) < 0) --wn; + if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; } a = b; } @@ -3226,10 +3378,6 @@ function d3_geo_clipExtent(x0, y0, x1, y1) { return wn !== 0; } - function isLeft(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]); - } - function interpolate(from, to, direction, listener) { var a = 0, a1 = 0; if (from == null || @@ -3335,18 +3483,6 @@ function d3_geo_clipExtent(x0, y0, x1, y1) { : b[0] - a[0]; } } -function d3_geo_compose(a, b) { - - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); - } - - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); - }; - - return compose; -} function d3_geo_conic(projectAt) { var φ0 = 0, @@ -4051,7 +4187,7 @@ function d3_geo_pathContext(context) { }; function point(x, y) { - context.moveTo(x, y); + context.moveTo(x + pointRadius, y); context.arc(x, y, pointRadius, 0, τ); } @@ -4081,6 +4217,17 @@ function d3_geo_resample(project) { maxDepth = 16; function resample(stream) { + return (maxDepth ? resampleRecursive : resampleNone)(stream); + } + + function resampleNone(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + }); + } + + function resampleRecursive(stream) { var λ00, φ00, x00, y00, a00, b00, c00, // first point λ0, x0, y0, a0, b0, c0; // previous point @@ -4144,7 +4291,7 @@ function d3_geo_resample(project) { c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), - λ2 = abs(abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), + λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], @@ -4170,29 +4317,6 @@ function d3_geo_resample(project) { return resample; } -d3.geo.transform = function(methods) { - return { - stream: function(stream) { - var transform = new d3_geo_transform(stream); - for (var k in methods) transform[k] = methods[k]; - return transform; - } - }; -}; - -function d3_geo_transform(stream) { - this.stream = stream; -} - -d3_geo_transform.prototype = { - point: function(x, y) { this.stream.point(x, y); }, - sphere: function() { this.stream.sphere(); }, - lineStart: function() { this.stream.lineStart(); }, - lineEnd: function() { this.stream.lineEnd(); }, - polygonStart: function() { this.stream.polygonStart(); }, - polygonEnd: function() { this.stream.polygonEnd(); } -}; - d3.geo.path = function() { var pointRadius = 4.5, projection, @@ -4262,10 +4386,40 @@ d3.geo.path = function() { function d3_geo_pathProjectStream(project) { var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); }); - return function(stream) { - var transform = new d3_geo_transform(stream = resample(stream)); - transform.point = function(x, y) { stream.point(x * d3_radians, y * d3_radians); }; - return transform; + return function(stream) { return d3_geo_projectionRadians(resample(stream)); }; +} + +d3.geo.transform = function(methods) { + return { + stream: function(stream) { + var transform = new d3_geo_transform(stream); + for (var k in methods) transform[k] = methods[k]; + return transform; + } + }; +}; + +function d3_geo_transform(stream) { + this.stream = stream; +} + +d3_geo_transform.prototype = { + point: function(x, y) { this.stream.point(x, y); }, + sphere: function() { this.stream.sphere(); }, + lineStart: function() { this.stream.lineStart(); }, + lineEnd: function() { this.stream.lineEnd(); }, + polygonStart: function() { this.stream.polygonStart(); }, + polygonEnd: function() { this.stream.polygonEnd(); } +}; + +function d3_geo_transformPoint(stream, point) { + return { + point: point, + sphere: function() { stream.sphere(); }, + lineStart: function() { stream.lineStart(); }, + lineEnd: function() { stream.lineEnd(); }, + polygonStart: function() { stream.polygonStart(); }, + polygonEnd: function() { stream.polygonEnd(); }, }; } @@ -4373,11 +4527,9 @@ function d3_geo_projectionMutator(projectAt) { } function d3_geo_projectionRadians(stream) { - var transform = new d3_geo_transform(stream); - transform.point = function(λ, φ) { - stream.point(λ * d3_radians, φ * d3_radians); - }; - return transform; + return d3_geo_transformPoint(stream, function(x, y) { + stream.point(x * d3_radians, y * d3_radians); + }); } function d3_geo_mercator(λ, φ) { @@ -4528,118 +4680,149 @@ function d3_geom_polygonClosed(coordinates) { b = coordinates[coordinates.length - 1]; return !(a[0] - b[0] || a[1] - b[1]); } +function d3_geom_pointX(d) { + return d[0]; +} -var d3_ease_default = function() { return d3_identity; }; +function d3_geom_pointY(d) { + return d[1]; +} -var d3_ease = d3.map({ - linear: d3_ease_default, - poly: d3_ease_poly, - quad: function() { return d3_ease_quad; }, - cubic: function() { return d3_ease_cubic; }, - sin: function() { return d3_ease_sin; }, - exp: function() { return d3_ease_exp; }, - circle: function() { return d3_ease_circle; }, - elastic: d3_ease_elastic, - back: d3_ease_back, - bounce: function() { return d3_ease_bounce; } -}); +/** + * Computes the 2D convex hull of a set of points using the monotone chain + * algorithm: + * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain) + * + * The runtime of this algorithm is O(n log n), where n is the number of input + * points. However in practice it outperforms other O(n log n) hulls. + * + * @param vertices [[x1, y1], [x2, y2], ...] + * @returns polygon [[x1, y1], [x2, y2], ...] + */ +d3.geom.hull = function(vertices) { + var x = d3_geom_pointX, + y = d3_geom_pointY; -var d3_ease_mode = d3.map({ - "in": d3_identity, - "out": d3_ease_reverse, - "in-out": d3_ease_reflect, - "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); } -}); + if (arguments.length) return hull(vertices); -d3.ease = function(name) { - var i = name.indexOf("-"), - t = i >= 0 ? name.substring(0, i) : name, - m = i >= 0 ? name.substring(i + 1) : "in"; - t = d3_ease.get(t) || d3_ease_default; - m = d3_ease_mode.get(m) || d3_identity; - return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); -}; + function hull(data) { + // Hull of < 3 points is not well-defined + if (data.length < 3) return []; -function d3_ease_clamp(f) { - return function(t) { - return t <= 0 ? 0 : t >= 1 ? 1 : f(t); - }; -} + var fx = d3_functor(x), + fy = d3_functor(y), + i, + n = data.length, + points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]] + flippedPoints = []; -function d3_ease_reverse(f) { - return function(t) { - return 1 - f(1 - t); + for (i = 0 ; i < n; i++) { + points.push([+fx.call(this, data[i], i), +fy.call(this, data[i], i), i]); + } + + // sort ascending by x-coord first, y-coord second + points.sort(d3_geom_hullOrder); + + // we flip bottommost points across y axis so we can use the upper hull routine on both + for (i = 0; i < n; i++) flippedPoints.push([points[i][0], -points[i][1]]); + + var upper = d3_geom_hullUpper(points), + lower = d3_geom_hullUpper(flippedPoints); + + // construct the polygon, removing possible duplicate endpoints + var skipLeft = lower[0] === upper[0], + skipRight = lower[lower.length - 1] === upper[upper.length - 1], + polygon = []; + + // add upper hull in r->l order + // then add lower hull in l->r order + for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); + for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); + + return polygon; + } + + hull.x = function(_) { + return arguments.length ? (x = _, hull) : x; }; -} -function d3_ease_reflect(f) { - return function(t) { - return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t))); + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; }; -} -function d3_ease_quad(t) { - return t * t; -} + return hull; +}; -function d3_ease_cubic(t) { - return t * t * t; -} +// finds the 'upper convex hull' (see wiki link above) +// assumes points arg has >=3 elements, is sorted by x, unique in y +// returns array of indices into points in left to right order +function d3_geom_hullUpper(points) { + var n = points.length, + hull = [0, 1], + hs = 2; // hull size -// Optimized clamp(reflect(poly(3))). -function d3_ease_cubicInOut(t) { - if (t <= 0) return 0; - if (t >= 1) return 1; - var t2 = t * t, t3 = t2 * t; - return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); -} + for (var i = 2; i < n; i++) { + while (hs > 1 && d3_cross2d(points[hull[hs-2]], points[hull[hs-1]], points[i]) <= 0) --hs; + hull[hs++] = i; + } -function d3_ease_poly(e) { - return function(t) { - return Math.pow(t, e); - }; + // we slice to make sure that the points we 'popped' from hull don't stay behind + return hull.slice(0, hs); } -function d3_ease_sin(t) { - return 1 - Math.cos(t * halfπ); +// comparator for ascending sort by x-coord first, y-coord second +function d3_geom_hullOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; } +// import "../transition/transition"; -function d3_ease_exp(t) { - return Math.pow(2, 10 * (t - 1)); -} +d3_selectionPrototype.transition = function(name) { + var id = d3_transitionInheritId || ++d3_transitionId, + ns = d3_transitionNamespace(name), + subgroups = [], + subgroup, + node, + transition = d3_transitionInherit || {time: Date.now(), ease: d3_ease_cubicInOut, delay: 0, duration: 250}; -function d3_ease_circle(t) { - return 1 - Math.sqrt(1 - t * t); -} + for (var j = -1, m = this.length; ++j < m;) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n;) { + if (node = group[i]) d3_transitionNode(node, i, ns, id, transition); + subgroup.push(node); + } + } -function d3_ease_elastic(a, p) { - var s; - if (arguments.length < 2) p = 0.45; - if (arguments.length) s = p / τ * Math.asin(1 / a); - else a = 1, s = p / 4; - return function(t) { - return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); - }; -} + return d3_transition(subgroups, ns, id); +}; +// import "../transition/transition"; -function d3_ease_back(s) { - if (!s) s = 1.70158; - return function(t) { - return t * t * ((s + 1) * t - s); - }; -} +// TODO Interrupt transitions for all namespaces? +d3_selectionPrototype.interrupt = function(name) { + return this.each(name == null + ? d3_selection_interrupt + : d3_selection_interruptNS(d3_transitionNamespace(name))); +}; -function d3_ease_bounce(t) { - return t < 1 / 2.75 ? 7.5625 * t * t - : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 - : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 - : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; +var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); + +function d3_selection_interruptNS(ns) { + return function() { + var lock, active; + if ((lock = this[ns]) && (active = lock[lock.active])) { + if (--lock.count) delete lock[lock.active]; + else delete this[ns]; + lock.active += .5; + active.event && active.event.interrupt.call(this, this.__data__, active.index); + } + }; } -function d3_transition(groups, id) { +function d3_transition(groups, ns, id) { d3_subclass(groups, d3_transitionPrototype); - groups.id = id; // Note: read-only! + // Note: read-only! + groups.namespace = ns; + groups.id = id; return groups; } @@ -4654,10 +4837,10 @@ d3_transitionPrototype.empty = d3_selectionPrototype.empty; d3_transitionPrototype.node = d3_selectionPrototype.node; d3_transitionPrototype.size = d3_selectionPrototype.size; -d3.transition = function(selection) { - return arguments.length - ? (d3_transitionInheritId ? selection.transition() : selection) - : d3_selectionRoot.transition(); +d3.transition = function(selection, name) { + return selection && selection.transition + ? (d3_transitionInheritId ? selection.transition(name) : selection) + : d3.selection().transition(selection); }; d3.transition.prototype = d3_transitionPrototype; @@ -4665,6 +4848,7 @@ d3.transition.prototype = d3_transitionPrototype; d3_transitionPrototype.select = function(selector) { var id = this.id, + ns = this.namespace, subgroups = [], subgroup, subnode, @@ -4677,7 +4861,7 @@ d3_transitionPrototype.select = function(selector) { for (var group = this[j], i = -1, n = group.length; ++i < n;) { if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { if ("__data__" in node) subnode.__data__ = node.__data__; - d3_transitionNode(subnode, i, id, node.__transition__[id]); + d3_transitionNode(subnode, i, ns, id, node[ns][id]); subgroup.push(subnode); } else { subgroup.push(null); @@ -4685,11 +4869,12 @@ d3_transitionPrototype.select = function(selector) { } } - return d3_transition(subgroups, id); + return d3_transition(subgroups, ns, id); }; d3_transitionPrototype.selectAll = function(selector) { var id = this.id, + ns = this.namespace, subgroups = [], subgroup, subnodes, @@ -4702,18 +4887,18 @@ d3_transitionPrototype.selectAll = function(selector) { for (var j = -1, m = this.length; ++j < m;) { for (var group = this[j], i = -1, n = group.length; ++i < n;) { if (node = group[i]) { - transition = node.__transition__[id]; + transition = node[ns][id]; subnodes = selector.call(node, node.__data__, i, j); subgroups.push(subgroup = []); for (var k = -1, o = subnodes.length; ++k < o;) { - if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition); + if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition); subgroup.push(subnode); } } } } - return d3_transition(subgroups, id); + return d3_transition(subgroups, ns, id); }; d3_transitionPrototype.filter = function(filter) { @@ -4727,47 +4912,41 @@ d3_transitionPrototype.filter = function(filter) { for (var j = 0, m = this.length; j < m; j++) { subgroups.push(subgroup = []); for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i)) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { subgroup.push(node); } } } - return d3_transition(subgroups, this.id); + return d3_transition(subgroups, this.namespace, this.id); }; -function d3_Color() {} +d3.color = d3_color; + +function d3_color() {} -d3_Color.prototype.toString = function() { +d3_color.prototype.toString = function() { return this.rgb() + ""; }; -d3.hsl = function(h, s, l) { - return arguments.length === 1 - ? (h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) - : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl)) - : d3_hsl(+h, +s, +l); -}; +d3.hsl = d3_hsl; function d3_hsl(h, s, l) { - return new d3_Hsl(h, s, l); -} - -function d3_Hsl(h, s, l) { - this.h = h; - this.s = s; - this.l = l; + return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) + : arguments.length < 2 ? (h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) + : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl)) + : new d3_hsl(h, s, l); } -var d3_hslPrototype = d3_Hsl.prototype = new d3_Color; +var d3_hslPrototype = d3_hsl.prototype = new d3_color; d3_hslPrototype.brighter = function(k) { k = Math.pow(0.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, this.l / k); + return new d3_hsl(this.h, this.s, this.l / k); }; d3_hslPrototype.darker = function(k) { k = Math.pow(0.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, k * this.l); + return new d3_hsl(this.h, this.s, k * this.l); }; d3_hslPrototype.rgb = function() { @@ -4800,35 +4979,27 @@ function d3_hsl_rgb(h, s, l) { return Math.round(v(h) * 255); } - return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + return new d3_rgb(vv(h + 120), vv(h), vv(h - 120)); } -d3.hcl = function(h, c, l) { - return arguments.length === 1 - ? (h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) - : (h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) - : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b))) - : d3_hcl(+h, +c, +l); -}; +d3.hcl = d3_hcl; function d3_hcl(h, c, l) { - return new d3_Hcl(h, c, l); -} - -function d3_Hcl(h, c, l) { - this.h = h; - this.c = c; - this.l = l; + return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) + : arguments.length < 2 ? (h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) + : (h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) + : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b))) + : new d3_hcl(h, c, l); } -var d3_hclPrototype = d3_Hcl.prototype = new d3_Color; +var d3_hclPrototype = d3_hcl.prototype = new d3_color; d3_hclPrototype.brighter = function(k) { - return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); }; d3_hclPrototype.darker = function(k) { - return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); }; d3_hclPrototype.rgb = function() { @@ -4838,25 +5009,17 @@ d3_hclPrototype.rgb = function() { function d3_hcl_lab(h, c, l) { if (isNaN(h)) h = 0; if (isNaN(c)) c = 0; - return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); } -d3.lab = function(l, a, b) { - return arguments.length === 1 - ? (l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) - : (l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) - : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b))) - : d3_lab(+l, +a, +b); -}; +d3.lab = d3_lab; function d3_lab(l, a, b) { - return new d3_Lab(l, a, b); -} - -function d3_Lab(l, a, b) { - this.l = l; - this.a = a; - this.b = b; + return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) + : arguments.length < 2 ? (l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) + : (l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) + : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b))) + : new d3_lab(l, a, b); } // Corresponds roughly to RGB brighter/darker @@ -4867,14 +5030,14 @@ var d3_lab_X = 0.950470, d3_lab_Y = 1, d3_lab_Z = 1.088830; -var d3_labPrototype = d3_Lab.prototype = new d3_Color; +var d3_labPrototype = d3_lab.prototype = new d3_color; d3_labPrototype.brighter = function(k) { - return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); }; d3_labPrototype.darker = function(k) { - return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); }; d3_labPrototype.rgb = function() { @@ -4888,7 +5051,7 @@ function d3_lab_rgb(l, a, b) { x = d3_lab_xyz(x) * d3_lab_X; y = d3_lab_xyz(y) * d3_lab_Y; z = d3_lab_xyz(z) * d3_lab_Z; - return d3_rgb( + return new d3_rgb( d3_xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), d3_xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), d3_xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z) @@ -4897,8 +5060,8 @@ function d3_lab_rgb(l, a, b) { function d3_lab_hcl(l, a, b) { return l > 0 - ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) - : d3_hcl(NaN, NaN, l); + ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) + : new d3_hcl(NaN, NaN, l); } function d3_lab_xyz(x) { @@ -4912,32 +5075,24 @@ function d3_xyz_rgb(r) { return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055)); } -d3.rgb = function(r, g, b) { - return arguments.length === 1 - ? (r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) +d3.rgb = d3_rgb; + +function d3_rgb(r, g, b) { + return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) + : arguments.length < 2 ? (r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb)) - : d3_rgb(~~r, ~~g, ~~b); -}; + : new d3_rgb(r, g, b); +} function d3_rgbNumber(value) { - return d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff); + return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff); } function d3_rgbString(value) { return d3_rgbNumber(value) + ""; } -function d3_rgb(r, g, b) { - return new d3_Rgb(r, g, b); -} - -function d3_Rgb(r, g, b) { - this.r = r; - this.g = g; - this.b = b; -} - -var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color; +var d3_rgbPrototype = d3_rgb.prototype = new d3_color; d3_rgbPrototype.brighter = function(k) { k = Math.pow(0.7, arguments.length ? k : 1); @@ -4945,16 +5100,16 @@ d3_rgbPrototype.brighter = function(k) { g = this.g, b = this.b, i = 30; - if (!r && !g && !b) return d3_rgb(i, i, i); + if (!r && !g && !b) return new d3_rgb(i, i, i); if (r && r < i) r = i; if (g && g < i) g = i; if (b && b < i) b = i; - return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k))); + return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k)); }; d3_rgbPrototype.darker = function(k) { k = Math.pow(0.7, arguments.length ? k : 1); - return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b)); + return new d3_rgb(k * this.r, k * this.g, k * this.b); }; d3_rgbPrototype.hsl = function() { @@ -4977,7 +5132,7 @@ function d3_rgb_parse(format, rgb, hsl) { b = 0, // blue channel; int in [0, 255] m1, // CSS color specification match m2, // CSS color specification type (e.g., rgb) - name; + color; /* Handle hsl, rgb. */ m1 = /([a-z]+)\((.*)\)/i.exec(format); @@ -5002,22 +5157,21 @@ function d3_rgb_parse(format, rgb, hsl) { } /* Named colors. */ - if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b); + if (color = d3_rgb_names.get(format.toLowerCase())) { + return rgb(color.r, color.g, color.b); + } /* Hexadecimal colors: #rgb and #rrggbb. */ - if (format != null && format.charAt(0) === "#") { + if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) { if (format.length === 4) { - r = format.charAt(1); r += r; - g = format.charAt(2); g += g; - b = format.charAt(3); b += b; + r = (color & 0xf00) >> 4; r = (r >> 4) | r; + g = (color & 0xf0); g = (g >> 4) | g; + b = (color & 0xf); b = (b << 4) | b; } else if (format.length === 7) { - r = format.substring(1, 3); - g = format.substring(3, 5); - b = format.substring(5, 7); + r = (color & 0xff0000) >> 16; + g = (color & 0xff00) >> 8; + b = (color & 0xff); } - r = parseInt(r, 16); - g = parseInt(g, 16); - b = parseInt(b, 16); } return rgb(r, g, b); @@ -5040,7 +5194,7 @@ function d3_rgb_hsl(r, g, b) { h = NaN; s = l > 0 && l < 1 ? 0 : h; } - return d3_hsl(h, s, l); + return new d3_hsl(h, s, l); } function d3_rgb_lab(r, g, b) { @@ -5182,6 +5336,7 @@ var d3_rgb_names = d3.map({ plum: 0xdda0dd, powderblue: 0xb0e0e6, purple: 0x800080, + rebeccapurple: 0x663399, red: 0xff0000, rosybrown: 0xbc8f8f, royalblue: 0x4169e1, @@ -5279,96 +5434,62 @@ function d3_interpolateArray(a, b) { d3.interpolateNumber = d3_interpolateNumber; function d3_interpolateNumber(a, b) { - b -= a = +a; - return function(t) { return a + b * t; }; + a = +a, b = +b; + return function(t) { return a * (1 - t) + b * t; }; } d3.interpolateString = d3_interpolateString; function d3_interpolateString(a, b) { - var m, // current match - i, // current index - j, // current index (for coalescing) - s0 = 0, // start index of current string prefix - s1 = 0, // end index of current string prefix + var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, // scan index for next number in b + am, // current match in a + bm, // current match in b + bs, // string preceding current number in b, if any + i = -1, // index in s s = [], // string constants and placeholders - q = [], // number interpolators - n, // q.length - o; + q = []; // number interpolators // Coerce inputs to strings. a = a + "", b = b + ""; - // Reset our regular expression! - d3_interpolate_number.lastIndex = 0; - - // Find all numbers in b. - for (i = 0; m = d3_interpolate_number.exec(b); ++i) { - if (m.index) s.push(b.substring(s0, s1 = m.index)); - q.push({i: s.length, x: m[0]}); - s.push(null); - s0 = d3_interpolate_number.lastIndex; - } - if (s0 < b.length) s.push(b.substring(s0)); - - // Find all numbers in a. - for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) { - o = q[i]; - if (o.x == m[0]) { // The numbers match, so coalesce. - if (o.i) { - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i - 1] += o.x; - s.splice(o.i, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } else { // This match is followed by a string, so coalesce twice. - s[o.i - 1] += o.x + s[o.i + 1]; - s.splice(o.i, 2); - for (j = i + 1; j < n; ++j) q[j].i -= 2; - } - } else { - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i] = o.x; - } else { // This match is followed by a string, so coalesce twice. - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } - } - q.splice(i, 1); - n--; - i--; - } else { - o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x)); + // Interpolate pairs of numbers in a & b. + while ((am = d3_interpolate_numberA.exec(a)) + && (bm = d3_interpolate_numberB.exec(b))) { + if ((bs = bm.index) > bi) { // a string precedes the next number in b + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; } - } - - // Remove any numbers in b not found in a. - while (i < n) { - o = q.pop(); - if (s[o.i + 1] == null) { // This match is followed by another number. - s[o.i] = o.x; - } else { // This match is followed by a string, so coalesce twice. - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); + if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match + if (s[i]) s[i] += bm; // coalesce with previous string + else s[++i] = bm; + } else { // interpolate non-matching numbers + s[++i] = null; + q.push({i: i, x: d3_interpolateNumber(am, bm)}); } - n--; + bi = d3_interpolate_numberB.lastIndex; } - // Special optimization for only a single match. - if (s.length === 1) { - return s[0] == null - ? (o = q[0].x, function(t) { return o(t) + ""; }) - : function() { return b; }; + // Add remains of b. + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; } + // Special optimization for only a single match. // Otherwise, interpolate each of the numbers and rejoin the string. - return function(t) { - for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; + return s.length < 2 + ? (q[0] ? (b = q[0].x, function(t) { return b(t) + ""; }) + : function() { return b; }) + : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); } -var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; +var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, + d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); d3.interpolate = d3_interpolate; @@ -5382,8 +5503,9 @@ d3.interpolators = [ function(a, b) { var t = typeof b; return (t === "string" ? (d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString) - : b instanceof d3_Color ? d3_interpolateRgb - : t === "object" ? (Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject) + : b instanceof d3_color ? d3_interpolateRgb + : Array.isArray(b) ? d3_interpolateArray + : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); } ]; @@ -5505,18 +5627,18 @@ function d3_interpolateTransform(a, b) { } d3_transitionPrototype.tween = function(name, tween) { - var id = this.id; - if (arguments.length < 2) return this.node().__transition__[id].tween.get(name); + var id = this.id, ns = this.namespace; + if (arguments.length < 2) return this.node()[ns][id].tween.get(name); return d3_selection_each(this, tween == null - ? function(node) { node.__transition__[id].tween.remove(name); } - : function(node) { node.__transition__[id].tween.set(name, tween); }); + ? function(node) { node[ns][id].tween.remove(name); } + : function(node) { node[ns][id].tween.set(name, tween); }); }; function d3_transition_tween(groups, name, value, tween) { - var id = groups.id; + var id = groups.id, ns = groups.namespace; return d3_selection_each(groups, typeof value === "function" - ? function(node, i, j) { node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j))); } - : (value = tween(value), function(node) { node.__transition__[id].tween.set(name, value); })); + ? function(node, i, j) { node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); } + : (value = tween(value), function(node) { node[ns][id].tween.set(name, value); })); } d3_transitionPrototype.attr = function(nameNS, value) { @@ -5602,7 +5724,7 @@ d3_transitionPrototype.style = function(name, value, priority) { // Otherwise, a name, value and priority are specified, and handled as below. function styleString(b) { return b == null ? styleNull : (b += "", function() { - var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; + var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i; return a !== b && (i = d3_interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); }); }); } @@ -5614,7 +5736,7 @@ d3_transitionPrototype.styleTween = function(name, tween, priority) { if (arguments.length < 3) priority = ""; function styleTween(d, i) { - var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); + var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name)); return f && function(t) { this.style.setProperty(name, f(t), priority); }; } @@ -5631,49 +5753,162 @@ function d3_transition_text(b) { } d3_transitionPrototype.remove = function() { + var ns = this.namespace; return this.each("end.transition", function() { var p; - if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this); + if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); }); }; +var d3_ease_default = function() { return d3_identity; }; + +var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { return d3_ease_quad; }, + cubic: function() { return d3_ease_cubic; }, + sin: function() { return d3_ease_sin; }, + exp: function() { return d3_ease_exp; }, + circle: function() { return d3_ease_circle; }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { return d3_ease_bounce; } +}); + +var d3_ease_mode = d3.map({ + "in": d3_identity, + "out": d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { return d3_ease_reflect(d3_ease_reverse(f)); } +}); + +d3.ease = function(name) { + var i = name.indexOf("-"), + t = i >= 0 ? name.slice(0, i) : name, + m = i >= 0 ? name.slice(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); +}; + +function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; +} + +function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; +} + +function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t))); + }; +} + +function d3_ease_quad(t) { + return t * t; +} + +function d3_ease_cubic(t) { + return t * t * t; +} + +// Optimized clamp(reflect(poly(3))). +function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); +} + +function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; +} + +function d3_ease_sin(t) { + return 1 - Math.cos(t * halfπ); +} + +function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); +} + +function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); +} + +function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = 0.45; + if (arguments.length) s = p / τ * Math.asin(1 / a); + else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); + }; +} + +function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; +} + +function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t + : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 + : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 + : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; +} + d3_transitionPrototype.ease = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].ease; + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].ease; if (typeof value !== "function") value = d3.ease.apply(d3, arguments); - return d3_selection_each(this, function(node) { node.__transition__[id].ease = value; }); + return d3_selection_each(this, function(node) { node[ns][id].ease = value; }); }; d3_transitionPrototype.delay = function(value) { - var id = this.id; + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].delay; return d3_selection_each(this, typeof value === "function" - ? function(node, i, j) { node.__transition__[id].delay = +value.call(node, node.__data__, i, j); } - : (value = +value, function(node) { node.__transition__[id].delay = value; })); + ? function(node, i, j) { node[ns][id].delay = +value.call(node, node.__data__, i, j); } + : (value = +value, function(node) { node[ns][id].delay = value; })); }; d3_transitionPrototype.duration = function(value) { - var id = this.id; + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].duration; return d3_selection_each(this, typeof value === "function" - ? function(node, i, j) { node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j)); } - : (value = Math.max(1, value), function(node) { node.__transition__[id].duration = value; })); + ? function(node, i, j) { node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); } + : (value = Math.max(1, value), function(node) { node[ns][id].duration = value; })); }; d3_transitionPrototype.each = function(type, listener) { - var id = this.id; + var id = this.id, ns = this.namespace; if (arguments.length < 2) { var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; - d3_transitionInheritId = id; - d3_selection_each(this, function(node, i, j) { - d3_transitionInherit = node.__transition__[id]; - type.call(node, node.__data__, i, j); - }); - d3_transitionInherit = inherit; - d3_transitionInheritId = inheritId; + try { + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node[ns][id]; + type.call(node, node.__data__, i, j); + }); + } finally { + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } } else { d3_selection_each(this, function(node) { - var transition = node.__transition__[id]; - (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener); + var transition = node[ns][id]; + (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); }); } return this; @@ -5682,6 +5917,7 @@ d3_transitionPrototype.each = function(type, listener) { d3_transitionPrototype.transition = function() { var id0 = this.id, id1 = ++d3_transitionId, + ns = this.namespace, subgroups = [], subgroup, group, @@ -5692,19 +5928,22 @@ d3_transitionPrototype.transition = function() { subgroups.push(subgroup = []); for (var group = this[j], i = 0, n = group.length; i < n; i++) { if (node = group[i]) { - transition = Object.create(node.__transition__[id0]); - transition.delay += transition.duration; - d3_transitionNode(node, i, id1, transition); + transition = node[ns][id0]; + d3_transitionNode(node, i, ns, id1, {time: transition.time, ease: transition.ease, delay: transition.delay + transition.duration, duration: transition.duration}); } subgroup.push(node); } } - return d3_transition(subgroups, id1); + return d3_transition(subgroups, ns, id1); }; -function d3_transitionNode(node, i, id, inherit) { - var lock = node.__transition__ || (node.__transition__ = {active: 0, count: 0}), +function d3_transitionNamespace(name) { + return name == null ? "__transition__" : "__transition_" + name + "__"; +} + +function d3_transitionNode(node, i, ns, id, inherit) { + var lock = node[ns] || (node[ns] = {active: 0, count: 0}), transition = lock[id]; if (!transition) { @@ -5713,18 +5952,20 @@ function d3_transitionNode(node, i, id, inherit) { transition = lock[id] = { tween: new d3_Map, time: time, - ease: inherit.ease, delay: inherit.delay, - duration: inherit.duration + duration: inherit.duration, + ease: inherit.ease, + index: i }; + inherit = null; // allow gc + ++lock.count; d3.timer(function(elapsed) { - var d = node.__data__, - ease = transition.ease, - delay = transition.delay, - duration = transition.duration, + var delay = transition.delay, + duration, + ease, timer = d3_timer_active, tweened = []; @@ -5734,15 +5975,28 @@ function d3_transitionNode(node, i, id, inherit) { function start(elapsed) { if (lock.active > id) return stop(); + + var active = lock[lock.active]; + if (active) { + --lock.count; + delete lock[lock.active]; + active.event && active.event.interrupt.call(node, node.__data__, active.index); + } + lock.active = id; - transition.event && transition.event.start.call(node, d, i); + + transition.event && transition.event.start.call(node, node.__data__, i); transition.tween.forEach(function(key, value) { - if (value = value.call(node, d, i)) { + if (value = value.call(node, node.__data__, i)) { tweened.push(value); } }); + // Deferred capture to allow tweens to initialize ease & duration. + ease = transition.ease; + duration = transition.duration; + d3.timer(function() { // defer to end of current frame timer.c = tick(elapsed || 1) ? d3_true : tick; return 1; @@ -5750,7 +6004,7 @@ function d3_transitionNode(node, i, id, inherit) { } function tick(elapsed) { - if (lock.active !== id) return stop(); + if (lock.active !== id) return 1; var t = elapsed / duration, e = ease(t), @@ -5761,14 +6015,14 @@ function d3_transitionNode(node, i, id, inherit) { } if (t >= 1) { - transition.event && transition.event.end.call(node, d, i); + transition.event && transition.event.end.call(node, node.__data__, i); return stop(); } } function stop() { if (--lock.count) delete lock[id]; - else delete node.__transition__; + else delete node[ns]; return 1; } }, 0, time); @@ -5792,7 +6046,7 @@ function d3_xhr(url, mimeType, response, callback) { responseType = null; // If IE does not support CORS, use XDomainRequest. - if (d3_window.XDomainRequest + if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest; @@ -5802,7 +6056,7 @@ function d3_xhr(url, mimeType, response, callback) { function respond() { var status = request.status, result; - if (!status && request.responseText || status >= 200 && status < 300 || status === 304) { + if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { try { result = response.call(xhr, request); } catch (e) { @@ -5889,6 +6143,13 @@ function d3_xhr_fixCallback(callback) { : callback; } +function d3_xhrHasResponse(request) { + var type = request.responseType; + return type && type !== "text" + ? request.response // null on error + : request.responseText; // "" on error +} + d3.text = d3_xhrType(function(request) { return request.responseText; }); @@ -5914,12 +6175,15 @@ function d3_html(request) { d3.xml = d3_xhrType(function(request) { return request.responseXML; }); - return d3; -})(); + if (typeof define === "function" && define.amd) define(d3); + else if (typeof module === "object" && module.exports) module.exports = d3; + this.d3 = d3; +}(); d3.combobox = function() { var event = d3.dispatch('accept'), data = [], - suggestions = []; + suggestions = [], + minItems = 2; var fetcher = function(val, cb) { cb(data.filter(function(d) { @@ -5961,8 +6225,12 @@ d3.combobox = function() { // on mousedown d3.event.stopPropagation(); d3.event.preventDefault(); - input.node().focus(); - fetch('', render); + if (!shown) { + input.node().focus(); + fetch('', render); + } else { + hide(); + } }); }); @@ -6017,6 +6285,8 @@ d3.combobox = function() { input.on('input.typeahead', function() { idx = -1; render(); + var start = input.property('selectionStart'); + input.node().setSelectionRange(start, start); input.on('input.typeahead', change); }); break; @@ -6108,7 +6378,7 @@ d3.combobox = function() { } function render() { - if (suggestions.length > 1 && document.activeElement === input.node()) { + if (suggestions.length >= minItems && document.activeElement === input.node()) { show(); } else { hide(); @@ -6174,6 +6444,12 @@ d3.combobox = function() { return combobox; }; + combobox.minItems = function(_) { + if (!arguments.length) return minItems; + minItems = _; + return combobox; + }; + return d3.rebind(combobox, event, 'on'); }; d3.geo.tile = function() { @@ -6406,14 +6682,20 @@ d3.keybinding = function(namespace) { // Up Arrow Key, or ↓ '↓': 40, down: 40, 'arrow-down': 40, // odities, printing characters that come out wrong: + // Firefox Equals + 'ffequals': 61, // Num-Multiply, or * '*': 106, star: 106, asterisk: 106, multiply: 106, // Num-Plus or + '+': 107, 'plus': 107, // Num-Subtract, or - '-': 109, subtract: 109, + // Firefox Minus + 'ffplus': 171, + // Firefox Minus + 'ffminus': 173, // Semicolon - ';': 186, semicolon:186, + ';': 186, semicolon: 186, // = or equals '=': 187, 'equals': 187, // Comma, or , @@ -6759,6 +7041,437 @@ d3.selection.prototype.value = function(value) { if (!arguments.length) return this.property('value'); return this.each(d3_selection_value(value)); }; +// Copyright (c) 2006, 2008 Tony Garnock-Jones +// Copyright (c) 2006, 2008 LShift Ltd. +// +// 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. + +// source: https://bitbucket.org/lshift/synchrotron/src + +Diff3 = (function() { + 'use strict'; + + var diff3 = { + longest_common_subsequence: function(file1, file2) { + /* Text diff algorithm following Hunt and McIlroy 1976. + * J. W. Hunt and M. D. McIlroy, An algorithm for differential file + * comparison, Bell Telephone Laboratories CSTR #41 (1976) + * http://www.cs.dartmouth.edu/~doug/ + * + * Expects two arrays of strings. + */ + var equivalenceClasses; + var file2indices; + var newCandidate; + var candidates; + var line; + var c, i, j, jX, r, s; + + equivalenceClasses = {}; + for (j = 0; j < file2.length; j++) { + line = file2[j]; + if (equivalenceClasses[line]) { + equivalenceClasses[line].push(j); + } else { + equivalenceClasses[line] = [j]; + } + } + + candidates = [{file1index: -1, + file2index: -1, + chain: null}]; + + for (i = 0; i < file1.length; i++) { + line = file1[i]; + file2indices = equivalenceClasses[line] || []; + + r = 0; + c = candidates[0]; + + for (jX = 0; jX < file2indices.length; jX++) { + j = file2indices[jX]; + + for (s = 0; s < candidates.length; s++) { + if ((candidates[s].file2index < j) && + ((s == candidates.length - 1) || + (candidates[s + 1].file2index > j))) + break; + } + + if (s < candidates.length) { + newCandidate = {file1index: i, + file2index: j, + chain: candidates[s]}; + if (r == candidates.length) { + candidates.push(c); + } else { + candidates[r] = c; + } + r = s + 1; + c = newCandidate; + if (r == candidates.length) { + break; // no point in examining further (j)s + } + } + } + + candidates[r] = c; + } + + // At this point, we know the LCS: it's in the reverse of the + // linked-list through .chain of + // candidates[candidates.length - 1]. + + return candidates[candidates.length - 1]; + }, + + diff_comm: function(file1, file2) { + // We apply the LCS to build a "comm"-style picture of the + // differences between file1 and file2. + + var result = []; + var tail1 = file1.length; + var tail2 = file2.length; + var common = {common: []}; + + function processCommon() { + if (common.common.length) { + common.common.reverse(); + result.push(common); + common = {common: []}; + } + } + + for (var candidate = Diff3.longest_common_subsequence(file1, file2); + candidate !== null; + candidate = candidate.chain) + { + var different = {file1: [], file2: []}; + + while (--tail1 > candidate.file1index) { + different.file1.push(file1[tail1]); + } + + while (--tail2 > candidate.file2index) { + different.file2.push(file2[tail2]); + } + + if (different.file1.length || different.file2.length) { + processCommon(); + different.file1.reverse(); + different.file2.reverse(); + result.push(different); + } + + if (tail1 >= 0) { + common.common.push(file1[tail1]); + } + } + + processCommon(); + + result.reverse(); + return result; + }, + + diff_patch: function(file1, file2) { + // We apply the LCD to build a JSON representation of a + // diff(1)-style patch. + + var result = []; + var tail1 = file1.length; + var tail2 = file2.length; + + function chunkDescription(file, offset, length) { + var chunk = []; + for (var i = 0; i < length; i++) { + chunk.push(file[offset + i]); + } + return {offset: offset, + length: length, + chunk: chunk}; + } + + for (var candidate = Diff3.longest_common_subsequence(file1, file2); + candidate !== null; + candidate = candidate.chain) + { + var mismatchLength1 = tail1 - candidate.file1index - 1; + var mismatchLength2 = tail2 - candidate.file2index - 1; + tail1 = candidate.file1index; + tail2 = candidate.file2index; + + if (mismatchLength1 || mismatchLength2) { + result.push({file1: chunkDescription(file1, + candidate.file1index + 1, + mismatchLength1), + file2: chunkDescription(file2, + candidate.file2index + 1, + mismatchLength2)}); + } + } + + result.reverse(); + return result; + }, + + strip_patch: function(patch) { + // Takes the output of Diff3.diff_patch(), and removes + // information from it. It can still be used by patch(), + // below, but can no longer be inverted. + var newpatch = []; + for (var i = 0; i < patch.length; i++) { + var chunk = patch[i]; + newpatch.push({file1: {offset: chunk.file1.offset, + length: chunk.file1.length}, + file2: {chunk: chunk.file2.chunk}}); + } + return newpatch; + }, + + invert_patch: function(patch) { + // Takes the output of Diff3.diff_patch(), and inverts the + // sense of it, so that it can be applied to file2 to give + // file1 rather than the other way around. + + for (var i = 0; i < patch.length; i++) { + var chunk = patch[i]; + var tmp = chunk.file1; + chunk.file1 = chunk.file2; + chunk.file2 = tmp; + } + }, + + patch: function (file, patch) { + // Applies a patch to a file. + // + // Given file1 and file2, Diff3.patch(file1, + // Diff3.diff_patch(file1, file2)) should give file2. + + var result = []; + var commonOffset = 0; + + function copyCommon(targetOffset) { + while (commonOffset < targetOffset) { + result.push(file[commonOffset]); + commonOffset++; + } + } + + for (var chunkIndex = 0; chunkIndex < patch.length; chunkIndex++) { + var chunk = patch[chunkIndex]; + copyCommon(chunk.file1.offset); + for (var lineIndex = 0; lineIndex < chunk.file2.chunk.length; lineIndex++) { + result.push(chunk.file2.chunk[lineIndex]); + } + commonOffset += chunk.file1.length; + } + + copyCommon(file.length); + return result; + }, + + diff_indices: function(file1, file2) { + // We apply the LCS to give a simple representation of the + // offsets and lengths of mismatched chunks in the input + // files. This is used by diff3_merge_indices below. + + var result = []; + var tail1 = file1.length; + var tail2 = file2.length; + + for (var candidate = Diff3.longest_common_subsequence(file1, file2); + candidate !== null; + candidate = candidate.chain) + { + var mismatchLength1 = tail1 - candidate.file1index - 1; + var mismatchLength2 = tail2 - candidate.file2index - 1; + tail1 = candidate.file1index; + tail2 = candidate.file2index; + + if (mismatchLength1 || mismatchLength2) { + result.push({file1: [tail1 + 1, mismatchLength1], + file2: [tail2 + 1, mismatchLength2]}); + } + } + + result.reverse(); + return result; + }, + + diff3_merge_indices: function (a, o, b) { + // Given three files, A, O, and B, where both A and B are + // independently derived from O, returns a fairly complicated + // internal representation of merge decisions it's taken. The + // interested reader may wish to consult + // + // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce. "A + // Formal Investigation of Diff3." In Arvind and Prasad, + // editors, Foundations of Software Technology and Theoretical + // Computer Science (FSTTCS), December 2007. + // + // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf) + var i; + + var m1 = Diff3.diff_indices(o, a); + var m2 = Diff3.diff_indices(o, b); + + var hunks = []; + function addHunk(h, side) { + hunks.push([h.file1[0], side, h.file1[1], h.file2[0], h.file2[1]]); + } + for (i = 0; i < m1.length; i++) { addHunk(m1[i], 0); } + for (i = 0; i < m2.length; i++) { addHunk(m2[i], 2); } + hunks.sort(); + + var result = []; + var commonOffset = 0; + function copyCommon(targetOffset) { + if (targetOffset > commonOffset) { + result.push([1, commonOffset, targetOffset - commonOffset]); + commonOffset = targetOffset; + } + } + + for (var hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) { + var firstHunkIndex = hunkIndex; + var hunk = hunks[hunkIndex]; + var regionLhs = hunk[0]; + var regionRhs = regionLhs + hunk[2]; + while (hunkIndex < hunks.length - 1) { + var maybeOverlapping = hunks[hunkIndex + 1]; + var maybeLhs = maybeOverlapping[0]; + if (maybeLhs > regionRhs) break; + regionRhs = maybeLhs + maybeOverlapping[2]; + hunkIndex++; + } + + copyCommon(regionLhs); + if (firstHunkIndex == hunkIndex) { + // The "overlap" was only one hunk long, meaning that + // there's no conflict here. Either a and o were the + // same, or b and o were the same. + if (hunk[4] > 0) { + result.push([hunk[1], hunk[3], hunk[4]]); + } + } else { + // A proper conflict. Determine the extents of the + // regions involved from a, o and b. Effectively merge + // all the hunks on the left into one giant hunk, and + // do the same for the right; then, correct for skew + // in the regions of o that each side changed, and + // report appropriate spans for the three sides. + var regions = { + 0: [a.length, -1, o.length, -1], + 2: [b.length, -1, o.length, -1] + }; + for (i = firstHunkIndex; i <= hunkIndex; i++) { + hunk = hunks[i]; + var side = hunk[1]; + var r = regions[side]; + var oLhs = hunk[0]; + var oRhs = oLhs + hunk[2]; + var abLhs = hunk[3]; + var abRhs = abLhs + hunk[4]; + r[0] = Math.min(abLhs, r[0]); + r[1] = Math.max(abRhs, r[1]); + r[2] = Math.min(oLhs, r[2]); + r[3] = Math.max(oRhs, r[3]); + } + var aLhs = regions[0][0] + (regionLhs - regions[0][2]); + var aRhs = regions[0][1] + (regionRhs - regions[0][3]); + var bLhs = regions[2][0] + (regionLhs - regions[2][2]); + var bRhs = regions[2][1] + (regionRhs - regions[2][3]); + result.push([-1, + aLhs, aRhs - aLhs, + regionLhs, regionRhs - regionLhs, + bLhs, bRhs - bLhs]); + } + commonOffset = regionRhs; + } + + copyCommon(o.length); + return result; + }, + + diff3_merge: function (a, o, b, excludeFalseConflicts) { + // Applies the output of Diff3.diff3_merge_indices to actually + // construct the merged file; the returned result alternates + // between "ok" and "conflict" blocks. + + var result = []; + var files = [a, o, b]; + var indices = Diff3.diff3_merge_indices(a, o, b); + + var okLines = []; + function flushOk() { + if (okLines.length) { + result.push({ok: okLines}); + } + okLines = []; + } + function pushOk(xs) { + for (var j = 0; j < xs.length; j++) { + okLines.push(xs[j]); + } + } + + function isTrueConflict(rec) { + if (rec[2] != rec[6]) return true; + var aoff = rec[1]; + var boff = rec[5]; + for (var j = 0; j < rec[2]; j++) { + if (a[j + aoff] != b[j + boff]) return true; + } + return false; + } + + for (var i = 0; i < indices.length; i++) { + var x = indices[i]; + var side = x[0]; + if (side == -1) { + if (excludeFalseConflicts && !isTrueConflict(x)) { + pushOk(files[0].slice(x[1], x[1] + x[2])); + } else { + flushOk(); + result.push({conflict: {a: a.slice(x[1], x[1] + x[2]), + aIndex: x[1], + o: o.slice(x[3], x[3] + x[4]), + oIndex: x[3], + b: b.slice(x[5], x[5] + x[6]), + bIndex: x[5]}}); + } + } else { + pushOk(files[side].slice(x[1], x[1] + x[2])); + } + } + + flushOk(); + return result; + } + }; + return diff3; +})(); + +if (typeof module !== 'undefined') module.exports = Diff3; var JXON = new (function () { var sValueProp = "keyValue", sAttributesProp = "keyAttributes", sAttrPref = "@", /* you can customize these values */ @@ -6900,211 +7613,63 @@ var JXON = new (function () { // var newDoc = JXON.unbuild(myObject); // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc)); -/*! - * Lo-Dash 1.0.0-rc.3 - * (c) 2012 John-David Dalton - * Based on Underscore.js 1.4.3 - * (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. +/** + * @license + * Lo-Dash 2.3.0 (Custom Build) + * Build: `lodash --debug --output js/lib/lodash.js include="any,assign,bind,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,pairs,pluck,reject,some,throttle,union,uniq,unique,values,without,flatten,value,chain,cloneDeep,merge,pick,reduce" exports="global,node"` + * Copyright 2012-2013 The Dojo Foundation + * Based on Underscore.js 1.5.2 + * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license */ -;(function(window, undefined) { - - /** Detect free variable `exports` */ - var freeExports = typeof exports == 'object' && exports; - - /** Detect free variable `global` and use it as `window` */ - var freeGlobal = typeof global == 'object' && global; - if (freeGlobal.global === freeGlobal) { - window = freeGlobal; - } +;(function() { - /** Used for array and object method references */ - var arrayRef = [], - // avoid a Closure Compiler bug by creatively creating an object - objectRef = new function(){}; + /** Used as a safe reference for `undefined` in pre ES5 environments */ + var undefined; - /** Used to generate unique IDs */ - var idCounter = 0; + /** Used to pool arrays and objects used internally */ + var arrayPool = [], + objectPool = []; /** Used internally to indicate various things */ - var indicatorObject = objectRef; - - /** Used by `cachedContains` as the default size when optimizations are enabled for large arrays */ - var largeArraySize = 30; + var indicatorObject = {}; - /** Used to restore the original `_` reference in `noConflict` */ - var oldDash = window._; + /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */ + var keyPrefix = +new Date + ''; - /** Used to detect template delimiter values that require a with-statement */ - var reComplexDelimiter = /[-?+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + /** Used as the size when optimizations are enabled for large arrays */ + var largeArraySize = 75; - /** Used to match HTML entities */ - var reEscapedHtml = /&(?:amp|lt|gt|quot|#x27);/g; - - /** Used to match empty string literals in compiled template source */ - var reEmptyStringLeading = /\b__p \+= '';/g, - reEmptyStringMiddle = /\b(__p \+=) '' \+/g, - reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + /** Used as the max size of the `arrayPool` and `objectPool` */ + var maxPoolSize = 40; /** Used to match regexp flags from their coerced string values */ var reFlags = /\w*$/; - /** Used to insert the data object variable into compiled template source */ - var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; - - /** Used to detect if a method is native */ - var reNative = RegExp('^' + - (objectRef.valueOf + '') - .replace(/[.*+?^=!:${}()|[\]\/\\]/g, '\\$&') - .replace(/valueOf|for [^\]]+/g, '.+?') + '$' - ); - - /** - * Used to match ES6 template delimiters - * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6 - */ - var reEsTemplate = /\$\{((?:(?=\\?)\\?[\s\S])*?)}/g; - - /** Used to match "interpolate" template delimiters */ - var reInterpolate = /<%=([\s\S]+?)%>/g; + /** Used to detected named functions */ + var reFuncName = /^\s*function[ \n\r\t]+\w/; - /** Used to ensure capturing order of template delimiters */ - var reNoMatch = /($^)/; - - /** Used to match HTML characters */ - var reUnescapedHtml = /[&<>"']/g; - - /** Used to match unescaped characters in compiled string literals */ - var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + /** Used to detect functions containing a `this` reference */ + var reThis = /\bthis\b/; /** Used to fix the JScript [[DontEnum]] bug */ - var shadowed = [ + var shadowedProps = [ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf' ]; - /** Used to make template sourceURLs easier to identify */ - var templateCounter = 0; - - /** Native method shortcuts */ - var ceil = Math.ceil, - concat = arrayRef.concat, - floor = Math.floor, - getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, - hasOwnProperty = objectRef.hasOwnProperty, - push = arrayRef.push, - propertyIsEnumerable = objectRef.propertyIsEnumerable, - toString = objectRef.toString; - - /* Native method shortcuts for methods with the same name as other `lodash` methods */ - var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind, - nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, - nativeIsFinite = window.isFinite, - nativeIsNaN = window.isNaN, - nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, - nativeMax = Math.max, - nativeMin = Math.min, - nativeRandom = Math.random; - /** `Object#toString` result shortcuts */ var argsClass = '[object Arguments]', arrayClass = '[object Array]', boolClass = '[object Boolean]', dateClass = '[object Date]', + errorClass = '[object Error]', funcClass = '[object Function]', numberClass = '[object Number]', objectClass = '[object Object]', regexpClass = '[object RegExp]', stringClass = '[object String]'; - /** Detect various environments */ - var isIeOpera = !!window.attachEvent, - isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera); - - /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ - var isBindFast = nativeBind && !isV8; - - /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ - var isKeysFast = nativeKeys && (isIeOpera || isV8); - - /** - * Detect the JScript [[DontEnum]] bug: - * - * In IE < 9 an objects own properties, shadowing non-enumerable ones, are - * made non-enumerable as well. - */ - var hasDontEnumBug; - - /** Detect if own properties are iterated after inherited properties (IE < 9) */ - var iteratesOwnLast; - - /** - * Detect if `Array#shift` and `Array#splice` augment array-like objects - * incorrectly: - * - * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` - * and `splice()` functions that fail to remove the last element, `value[0]`, - * of array-like objects even though the `length` property is set to `0`. - * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` - * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. - */ - var hasObjectSpliceBug = (hasObjectSpliceBug = { '0': 1, 'length': 1 }, - arrayRef.splice.call(hasObjectSpliceBug, 0, 1), hasObjectSpliceBug[0]); - - /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */ - var nonEnumArgs = true; - - (function() { - var props = []; - function ctor() { this.x = 1; } - ctor.prototype = { 'valueOf': 1, 'y': 1 }; - for (var prop in new ctor) { props.push(prop); } - for (prop in arguments) { nonEnumArgs = !prop; } - - hasDontEnumBug = !/valueOf/.test(props); - iteratesOwnLast = props[0] != 'x'; - }(1)); - - /** Detect if `arguments` objects are `Object` objects (all but Opera < 10.5) */ - var argsAreObjects = arguments.constructor == Object; - - /** Detect if `arguments` objects [[Class]] is unresolvable (Firefox < 4, IE < 9) */ - var noArgsClass = !isArguments(arguments); - - /** - * Detect lack of support for accessing string characters by index: - * - * IE < 8 can't access characters by index and IE 8 can only access - * characters by index on string literals. - */ - var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; - - /** - * Detect if a node's [[Class]] is unresolvable (IE < 9) - * and that the JS engine won't error when attempting to coerce an object to - * a string without a `toString` property value of `typeof` "function". - */ - try { - var noNodeClass = ({ 'toString': 0 } + '', toString.call(document) == objectClass); - } catch(e) { } - - /** - * Detect if sourceURL syntax is usable without erroring: - * - * The JS engine embedded in Adobe products will throw a syntax error when - * it encounters a single line comment beginning with the `@` symbol. - * - * The JS engine in Narwhal will generate the function `function anonymous(){//}` - * and throw a syntax error. - * - * Avoid comments beginning `@` symbols in IE because they are part of its - * non-standard conditional compilation support. - * http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx - */ - try { - var useSourceURL = (Function('//@')(), !isIeOpera); - } catch(e) { } - /** Used to identify object classifications that `_.clone` supports */ var cloneableClasses = {}; cloneableClasses[funcClass] = false; @@ -7113,15 +7678,35 @@ var JXON = new (function () { cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; - /** Used to lookup a built-in constructor by [[Class]] */ - var ctorByClass = {}; - ctorByClass[arrayClass] = Array; - ctorByClass[boolClass] = Boolean; - ctorByClass[dateClass] = Date; - ctorByClass[objectClass] = Object; - ctorByClass[numberClass] = Number; - ctorByClass[regexpClass] = RegExp; - ctorByClass[stringClass] = String; + /** Used as an internal `_.debounce` options object */ + var debounceOptions = { + 'leading': false, + 'maxWait': 0, + 'trailing': false + }; + + /** Used as the property descriptor for `__bindData__` */ + var descriptor = { + 'configurable': false, + 'enumerable': false, + 'value': null, + 'writable': false + }; + + /** Used as the data object for `iteratorTemplate` */ + var iteratorData = { + 'args': '', + 'array': null, + 'bottom': '', + 'firstArg': '', + 'init': '', + 'keys': null, + 'loop': '', + 'shadowedProps': null, + 'support': null, + 'top': '', + 'useHas': false + }; /** Used to determine if values are of the language type Object */ var objectTypes = { @@ -7133,511 +7718,171 @@ var JXON = new (function () { 'undefined': false }; - /** Used to escape characters for inclusion in compiled string literals */ - var stringEscapes = { - '\\': '\\', - "'": "'", - '\n': 'n', - '\r': 'r', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - /*--------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object, that wraps the given `value`, to enable - * method chaining. - * - * The chainable wrapper functions are: - * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`, - * `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`, - * `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`, - * `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`, - * `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `pick`, `pluck`, - * `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, - * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`, - * `unshift`, `values`, `where`, `without`, `wrap`, and `zip` - * - * The non-chainable wrapper functions are: - * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `has`, `identity`, - * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, - * `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, - * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`, - * `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`, - * `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId` - * - * The wrapper functions `first` and `last` return wrapped values when `n` is - * passed, otherwise they return unwrapped values. - * - * @name _ - * @constructor - * @category Chaining - * @param {Mixed} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns a `lodash` instance. - */ - function lodash(value) { - // exit early if already wrapped, even if wrapped by a different `lodash` constructor - if (value && typeof value == 'object' && value.__wrapped__) { - return value; - } - // allow invoking `lodash` without the `new` operator - if (!(this instanceof lodash)) { - return new lodash(value); - } - this.__wrapped__ = value; - } - - /** - * By default, the template delimiters used by Lo-Dash are similar to those in - * embedded Ruby (ERB). Change the following template settings to use alternative - * delimiters. - * - * @static - * @memberOf _ - * @type Object - */ - lodash.templateSettings = { - - /** - * Used to detect `data` property values to be HTML-escaped. - * - * @static - * @memberOf _.templateSettings - * @type RegExp - */ - 'escape': /<%-([\s\S]+?)%>/g, - - /** - * Used to detect code to be evaluated. - * - * @static - * @memberOf _.templateSettings - * @type RegExp - */ - 'evaluate': /<%([\s\S]+?)%>/g, - - /** - * Used to detect `data` property values to inject. - * - * @static - * @memberOf _.templateSettings - * @type RegExp - */ - 'interpolate': reInterpolate, - - /** - * Used to reference the data object in the template text. - * - * @static - * @memberOf _.templateSettings - * @type String - */ - 'variable': '' - }; - - /*--------------------------------------------------------------------------*/ + /** Used as a reference to the global object */ + var root = (objectTypes[typeof window] && window) || this; - /** - * The template used to create iterator functions. - * - * @private - * @param {Obect} data The data object used to populate the text. - * @returns {String} Returns the interpolated text. - */ - var iteratorTemplate = template( - // conditional strict mode - "<% if (obj.useStrict) { %>'use strict';\n<% } %>" + - - // the `iteratee` may be reassigned by the `top` snippet - 'var index, iteratee = <%= firstArg %>, ' + - // assign the `result` variable an initial value - 'result = <%= firstArg %>;\n' + - // exit early if the first argument is falsey - 'if (!<%= firstArg %>) return result;\n' + - // add code before the iteration branches - '<%= top %>;\n' + - - // array-like iteration: - '<% if (arrayLoop) { %>' + - 'var length = iteratee.length; index = -1;\n' + - "if (typeof length == 'number') {" + - - // add support for accessing string characters by index if needed - ' <% if (noCharByIndex) { %>\n' + - ' if (isString(iteratee)) {\n' + - " iteratee = iteratee.split('')\n" + - ' }' + - ' <% } %>\n' + - - // iterate over the array-like value - ' while (++index < length) {\n' + - ' <%= arrayLoop %>\n' + - ' }\n' + - '}\n' + - 'else {' + - - // object iteration: - // add support for iterating over `arguments` objects if needed - ' <% } else if (nonEnumArgs) { %>\n' + - ' var length = iteratee.length; index = -1;\n' + - ' if (length && isArguments(iteratee)) {\n' + - ' while (++index < length) {\n' + - " index += '';\n" + - ' <%= objectLoop %>\n' + - ' }\n' + - ' } else {' + - ' <% } %>' + - - // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 - // (if the prototype or a property on the prototype has been set) - // incorrectly sets a function's `prototype` property [[Enumerable]] - // value to `true`. Because of this Lo-Dash standardizes on skipping - // the the `prototype` property of functions regardless of its - // [[Enumerable]] value. - ' <% if (!hasDontEnumBug) { %>\n' + - " var skipProto = typeof iteratee == 'function' && \n" + - " propertyIsEnumerable.call(iteratee, 'prototype');\n" + - ' <% } %>' + - - // iterate own properties using `Object.keys` if it's fast - ' <% if (isKeysFast && useHas) { %>\n' + - ' var ownIndex = -1,\n' + - ' ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' + - ' length = ownProps.length;\n\n' + - ' while (++ownIndex < length) {\n' + - ' index = ownProps[ownIndex];\n' + - " <% if (!hasDontEnumBug) { %>if (!(skipProto && index == 'prototype')) {\n <% } %>" + - ' <%= objectLoop %>\n' + - ' <% if (!hasDontEnumBug) { %>}\n<% } %>' + - ' }' + - - // else using a for-in loop - ' <% } else { %>\n' + - ' for (index in iteratee) {<%' + - ' if (!hasDontEnumBug || useHas) { %>\n if (<%' + - " if (!hasDontEnumBug) { %>!(skipProto && index == 'prototype')<% }" + - ' if (!hasDontEnumBug && useHas) { %> && <% }' + - ' if (useHas) { %>hasOwnProperty.call(iteratee, index)<% }' + - ' %>) {' + - ' <% } %>\n' + - ' <%= objectLoop %>;' + - ' <% if (!hasDontEnumBug || useHas) { %>\n }<% } %>\n' + - ' }' + - ' <% } %>' + - - // Because IE < 9 can't set the `[[Enumerable]]` attribute of an - // existing property and the `constructor` property of a prototype - // defaults to non-enumerable, Lo-Dash skips the `constructor` - // property when it infers it's iterating over a `prototype` object. - ' <% if (hasDontEnumBug) { %>\n\n' + - ' var ctor = iteratee.constructor;\n' + - ' <% for (var k = 0; k < 7; k++) { %>\n' + - " index = '<%= shadowed[k] %>';\n" + - ' if (<%' + - " if (shadowed[k] == 'constructor') {" + - ' %>!(ctor && ctor.prototype === iteratee) && <%' + - ' } %>hasOwnProperty.call(iteratee, index)) {\n' + - ' <%= objectLoop %>\n' + - ' }' + - ' <% } %>' + - ' <% } %>' + - ' <% if (arrayLoop || nonEnumArgs) { %>\n}<% } %>\n' + - - // add code to the bottom of the iteration function - '<%= bottom %>;\n' + - // finally, return the `result` - 'return result' - ); + /** Detect free variable `exports` */ + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; - /** Reusable iterator options for `assign` and `defaults` */ - var assignIteratorOptions = { - 'args': 'object, source, guard', - 'top': - "for (var argsIndex = 1, argsLength = typeof guard == 'number' ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n" + - ' if ((iteratee = arguments[argsIndex])) {', - 'objectLoop': 'result[index] = iteratee[index]', - 'bottom': ' }\n}' - }; + /** Detect free variable `module` */ + var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; - /** - * Reusable iterator options shared by `each`, `forIn`, and `forOwn`. - */ - var eachIteratorOptions = { - 'args': 'collection, callback, thisArg', - 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg)", - 'arrayLoop': 'if (callback(iteratee[index], index, collection) === false) return result', - 'objectLoop': 'if (callback(iteratee[index], index, collection) === false) return result' - }; + /** Detect the popular CommonJS extension `module.exports` */ + var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; - /** Reusable iterator options for `forIn` and `forOwn` */ - var forOwnIteratorOptions = { - 'arrayLoop': null - }; + /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */ + var freeGlobal = objectTypes[typeof global] && global; + if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { + root = freeGlobal; + } /*--------------------------------------------------------------------------*/ /** - * Creates a function optimized to search large arrays for a given `value`, - * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`. + * The base implementation of `_.indexOf` without support for binary searches + * or `fromIndex` constraints. * * @private * @param {Array} array The array to search. - * @param {Mixed} value The value to search for. - * @param {Number} [fromIndex=0] The index to search from. - * @param {Number} [largeSize=30] The length at which an array is considered large. - * @returns {Boolean} Returns `true` if `value` is found, else `false`. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the matched value or `-1`. */ - function cachedContains(array, fromIndex, largeSize) { - fromIndex || (fromIndex = 0); - - var length = array.length, - isLarge = (length - fromIndex) >= (largeSize || largeArraySize); - - if (isLarge) { - var cache = {}, - index = fromIndex - 1; + function baseIndexOf(array, value, fromIndex) { + var index = (fromIndex || 0) - 1, + length = array ? array.length : 0; - while (++index < length) { - // manually coerce `value` to a string because `hasOwnProperty`, in some - // older versions of Firefox, coerces objects incorrectly - var key = array[index] + ''; - (hasOwnProperty.call(cache, key) ? cache[key] : (cache[key] = [])).push(array[index]); - } - } - return function(value) { - if (isLarge) { - var key = value + ''; - return hasOwnProperty.call(cache, key) && indexOf(cache[key], value) > -1; + while (++index < length) { + if (array[index] === value) { + return index; } - return indexOf(array, value, fromIndex) > -1; } + return -1; } /** - * Used by `_.max` and `_.min` as the default `callback` when a given - * `collection` is a string value. - * - * @private - * @param {String} value The character to inspect. - * @returns {Number} Returns the code unit of given character. - */ - function charAtCallback(value) { - return value.charCodeAt(0); - } - - /** - * Used by `sortBy` to compare transformed `collection` values, stable sorting - * them in ascending order. + * An implementation of `_.contains` for cache objects that mimics the return + * signature of `_.indexOf` by returning `0` if the value is found, else `-1`. * * @private - * @param {Object} a The object to compare to `b`. - * @param {Object} b The object to compare to `a`. - * @returns {Number} Returns the sort order indicator of `1` or `-1`. + * @param {Object} cache The cache object to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns `0` if `value` is found, else `-1`. */ - function compareAscending(a, b) { - var ai = a.index, - bi = b.index; + function cacheIndexOf(cache, value) { + var type = typeof value; + cache = cache.cache; - a = a.criteria; - b = b.criteria; - - // ensure a stable sort in V8 and other engines - // http://code.google.com/p/v8/issues/detail?id=90 - if (a !== b) { - if (a > b || typeof a == 'undefined') { - return 1; - } - if (a < b || typeof b == 'undefined') { - return -1; - } + if (type == 'boolean' || value == null) { + return cache[value] ? 0 : -1; + } + if (type != 'number' && type != 'string') { + type = 'object'; } - return ai < bi ? -1 : 1; + var key = type == 'number' ? value : keyPrefix + value; + cache = (cache = cache[type]) && cache[key]; + + return type == 'object' + ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1) + : (cache ? 0 : -1); } /** - * Creates a function that, when called, invokes `func` with the `this` - * binding of `thisArg` and prepends any `partailArgs` to the arguments passed - * to the bound function. + * Adds a given value to the corresponding cache object. * * @private - * @param {Function|String} func The function to bind or the method name. - * @param {Mixed} [thisArg] The `this` binding of `func`. - * @param {Array} partialArgs An array of arguments to be partially applied. - * @returns {Function} Returns the new bound function. + * @param {*} value The value to add to the cache. */ - function createBound(func, thisArg, partialArgs) { - var isFunc = isFunction(func), - isPartial = !partialArgs, - key = thisArg; + function cachePush(value) { + var cache = this.cache, + type = typeof value; - // juggle arguments - if (isPartial) { - partialArgs = thisArg; - } - if (!isFunc) { - thisArg = func; - } - - function bound() { - // `Function#bind` spec - // http://es5.github.com/#x15.3.4.5 - var args = arguments, - thisBinding = isPartial ? this : thisArg; - - if (!isFunc) { - func = thisArg[key]; - } - if (partialArgs.length) { - args = args.length - ? partialArgs.concat(slice(args)) - : partialArgs; + if (type == 'boolean' || value == null) { + cache[value] = true; + } else { + if (type != 'number' && type != 'string') { + type = 'object'; } - if (this instanceof bound) { - // ensure `new bound` is an instance of `bound` and `func` - noop.prototype = func.prototype; - thisBinding = new noop; - noop.prototype = null; + var key = type == 'number' ? value : keyPrefix + value, + typeCache = cache[type] || (cache[type] = {}); - // mimic the constructor's `return` behavior - // http://es5.github.com/#x13.2.2 - var result = func.apply(thisBinding, args); - return isObject(result) ? result : thisBinding; + if (type == 'object') { + (typeCache[key] || (typeCache[key] = [])).push(value); + } else { + typeCache[key] = true; } - return func.apply(thisBinding, args); } - return bound; } /** - * Produces an iteration callback bound to an optional `thisArg`. If `func` is - * a property name, the callback will return the property value for a given element. + * Creates a cache object to optimize linear searches of large arrays. * * @private - * @param {Function|String} [func=identity|property] The function called per - * iteration or property name to query. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @param {Object} [accumulating] Used to indicate that the callback should - * accept an `accumulator` argument. - * @returns {Function} Returns a callback function. + * @param {Array} [array=[]] The array to search. + * @returns {null|Object} Returns the cache object or `null` if caching should not be used. */ - function createCallback(func, thisArg, accumulating) { - if (!func) { - return identity; - } - if (typeof func != 'function') { - return function(object) { - return object[func]; - }; - } - if (typeof thisArg != 'undefined') { - if (accumulating) { - return function(accumulator, value, index, object) { - return func.call(thisArg, accumulator, value, index, object); - }; - } - return function(value, index, object) { - return func.call(thisArg, value, index, object); - }; + function createCache(array) { + var index = -1, + length = array.length, + first = array[0], + mid = array[(length / 2) | 0], + last = array[length - 1]; + + if (first && typeof first == 'object' && + mid && typeof mid == 'object' && last && typeof last == 'object') { + return false; } - return func; - } + var cache = getObject(); + cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false; - /** - * Creates compiled iteration functions. - * - * @private - * @param {Object} [options1, options2, ...] The compile options object(s). - * useHas - A boolean to specify using `hasOwnProperty` checks in the object loop. - * args - A string of comma separated arguments the iteration function will accept. - * top - A string of code to execute before the iteration branches. - * arrayLoop - A string of code to execute in the array loop. - * objectLoop - A string of code to execute in the object loop. - * bottom - A string of code to execute after the iteration branches. - * - * @returns {Function} Returns the compiled function. - */ - function createIterator() { - var data = { - 'arrayLoop': '', - 'bottom': '', - 'hasDontEnumBug': hasDontEnumBug, - 'isKeysFast': isKeysFast, - 'objectLoop': '', - 'nonEnumArgs': nonEnumArgs, - 'noCharByIndex': noCharByIndex, - 'shadowed': shadowed, - 'top': '', - 'useHas': true - }; + var result = getObject(); + result.array = array; + result.cache = cache; + result.push = cachePush; - // merge options into a template data object - for (var object, index = 0; object = arguments[index]; index++) { - for (var key in object) { - data[key] = object[key]; - } + while (++index < length) { + result.push(array[index]); } - var args = data.args; - data.firstArg = /^[^,]+/.exec(args)[0]; - - // create the function factory - var factory = Function( - 'createCallback, hasOwnProperty, isArguments, isString, objectTypes, ' + - 'nativeKeys, propertyIsEnumerable', - 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' - ); - // return the compiled function - return factory( - createCallback, hasOwnProperty, isArguments, isString, objectTypes, - nativeKeys, propertyIsEnumerable - ); + return result; } /** - * A function compiled to iterate `arguments` objects, arrays, objects, and - * strings consistenly across environments, executing the `callback` for each - * element in the `collection`. The `callback` is bound to `thisArg` and invoked - * with three arguments; (value, index|key, collection). Callbacks may exit - * iteration early by explicitly returning `false`. - * - * @private - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Array|Object|String} Returns `collection`. - */ - var each = createIterator(eachIteratorOptions); - - /** - * Used by `template` to escape characters for inclusion in compiled - * string literals. + * Gets an array from the array pool or creates a new one if the pool is empty. * * @private - * @param {String} match The matched character to escape. - * @returns {String} Returns the escaped character. + * @returns {Array} The array from the pool. */ - function escapeStringChar(match) { - return '\\' + stringEscapes[match]; + function getArray() { + return arrayPool.pop() || []; } /** - * Used by `escape` to convert characters to HTML entities. + * Gets an object from the object pool or creates a new one if the pool is empty. * * @private - * @param {String} match The matched character to escape. - * @returns {String} Returns the escaped character. + * @returns {Object} The object from the pool. */ - function escapeHtmlChar(match) { - return htmlEscapes[match]; + function getObject() { + return objectPool.pop() || { + 'array': null, + 'cache': null, + 'false': false, + 'null': false, + 'number': null, + 'object': null, + 'push': null, + 'string': null, + 'true': false, + 'undefined': false + }; } /** * Checks if `value` is a DOM node in IE < 9. * * @private - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a DOM node, else `false`. + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`. */ function isNode(value) { // IE < 9 presents DOM nodes as `Object` objects except they have `toString` @@ -7646,25 +7891,46 @@ var JXON = new (function () { } /** - * A no-operation function. + * Releases the given array back to the array pool. * * @private + * @param {Array} [array] The array to release. */ - function noop() { - // no operation performed + function releaseArray(array) { + array.length = 0; + if (arrayPool.length < maxPoolSize) { + arrayPool.push(array); + } + } + + /** + * Releases the given object back to the object pool. + * + * @private + * @param {Object} [object] The object to release. + */ + function releaseObject(object) { + var cache = object.cache; + if (cache) { + releaseObject(cache); + } + object.array = object.cache =object.object = object.number = object.string =null; + if (objectPool.length < maxPoolSize) { + objectPool.push(object); + } } /** * Slices the `collection` from the `start` index up to, but not including, * the `end` index. * - * Note: This function is used, instead of `Array#slice`, to support node lists + * Note: This function is used instead of `Array#slice` to support node lists * in IE < 9 and to ensure dense arrays are returned. * * @private - * @param {Array|Object|String} collection The collection to slice. - * @param {Number} start The start index. - * @param {Number} end The end index. + * @param {Array|Object|string} collection The collection to slice. + * @param {number} start The start index. + * @param {number} end The end index. * @returns {Array} Returns the new array. */ function slice(array, start, end) { @@ -7682,618 +7948,862 @@ var JXON = new (function () { return result; } + /*--------------------------------------------------------------------------*/ + /** - * Used by `unescape` to convert HTML entities to characters. + * Used for `Array` method references. * - * @private - * @param {String} match The matched character to unescape. - * @returns {String} Returns the unescaped character. + * Normally `Array.prototype` would suffice, however, using an array literal + * avoids issues in Narwhal. */ - function unescapeHtmlChar(match) { - return htmlUnescapes[match]; - } + var arrayRef = []; + + /** Used for native method references */ + var errorProto = Error.prototype, + objectProto = Object.prototype, + stringProto = String.prototype; + + /** Used to resolve the internal [[Class]] of values */ + var toString = objectProto.toString; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + String(toString) + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + .replace(/toString| for [^\]]+/g, '.*?') + '$' + ); + + /** Native method shortcuts */ + var fnToString = Function.prototype.toString, + getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, + hasOwnProperty = objectProto.hasOwnProperty, + now = reNative.test(now = Date.now) && now || function() { return +new Date; }, + push = arrayRef.push, + propertyIsEnumerable = objectProto.propertyIsEnumerable; + + /** Used to set meta data on functions */ + var defineProperty = (function() { + // IE 8 only accepts DOM elements + try { + var o = {}, + func = reNative.test(func = Object.defineProperty) && func, + result = func(o, o, o) && func; + } catch(e) { } + return result; + }()); + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate, + nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray, + nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys, + nativeMax = Math.max, + nativeMin = Math.min; + + /** Used to lookup a built-in constructor by [[Class]] */ + var ctorByClass = {}; + ctorByClass[arrayClass] = Array; + ctorByClass[boolClass] = Boolean; + ctorByClass[dateClass] = Date; + ctorByClass[funcClass] = Function; + ctorByClass[objectClass] = Object; + ctorByClass[numberClass] = Number; + ctorByClass[regexpClass] = RegExp; + ctorByClass[stringClass] = String; + + /** Used to avoid iterating non-enumerable properties in IE < 9 */ + var nonEnumProps = {}; + nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true }; + nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true }; + nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true }; + nonEnumProps[objectClass] = { 'constructor': true }; + + (function() { + var length = shadowedProps.length; + while (length--) { + var key = shadowedProps[length]; + for (var className in nonEnumProps) { + if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) { + nonEnumProps[className][key] = false; + } + } + } + }()); /*--------------------------------------------------------------------------*/ /** - * Assigns own enumerable properties of source object(s) to the `destination` - * object. Subsequent sources will overwrite propery assignments of previous - * sources. + * Creates a `lodash` object which wraps the given value to enable intuitive + * method chaining. * - * @static - * @memberOf _ - * @alias extend - * @category Objects - * @param {Object} object The destination object. - * @param {Object} [source1, source2, ...] The source objects. - * @returns {Object} Returns the destination object. - * @example + * In addition to Lo-Dash methods, wrappers also have the following `Array` methods: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, + * and `unshift` * - * _.assign({ 'name': 'moe' }, { 'age': 40 }); - * // => { 'name': 'moe', 'age': 40 } - */ - var assign = createIterator(assignIteratorOptions); - - /** - * Checks if `value` is an `arguments` object. + * Chaining is supported in custom builds as long as the `value` method is + * implicitly or explicitly included in the build. * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. - * @example + * The chainable wrapper functions are: + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, + * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`, + * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, + * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, + * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, + * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, + * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, + * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, + * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, + * and `zip` * - * (function() { return _.isArguments(arguments); })(1, 2, 3); - * // => true + * The non-chainable wrapper functions are: + * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`, + * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`, + * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, + * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, + * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, + * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`, + * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`, + * `template`, `unescape`, `uniqueId`, and `value` * - * _.isArguments([1, 2, 3]); - * // => false - */ - function isArguments(value) { - return toString.call(value) == argsClass; - } - // fallback for browsers that can't detect `arguments` objects by [[Class]] - if (noArgsClass) { - isArguments = function(value) { - return value ? hasOwnProperty.call(value, 'callee') : false; - }; - } - - /** - * Iterates over `object`'s own and inherited enumerable properties, executing - * the `callback` for each property. The `callback` is bound to `thisArg` and - * invoked with three arguments; (value, key, object). Callbacks may exit iteration - * early by explicitly returning `false`. + * The wrapper functions `first` and `last` return wrapped values when `n` is + * provided, otherwise they return unwrapped values. * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. + * Explicit chaining can be enabled by using the `_.chain` method. + * + * @name _ + * @constructor + * @category Chaining + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns a `lodash` instance. * @example * - * function Dog(name) { - * this.name = name; - * } + * var wrapped = _([1, 2, 3]); * - * Dog.prototype.bark = function() { - * alert('Woof, woof!'); - * }; + * // returns an unwrapped value + * wrapped.reduce(function(sum, num) { + * return sum + num; + * }); + * // => 6 * - * _.forIn(new Dog('Dagny'), function(value, key) { - * alert(key); + * // returns a wrapped value + * var squares = wrapped.map(function(num) { + * return num * num; * }); - * // => alerts 'name' and 'bark' (order is not guaranteed) - */ - var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { - 'useHas': false - }); - - /** - * Iterates over an object's own enumerable properties, executing the `callback` - * for each property. The `callback` is bound to `thisArg` and invoked with three - * arguments; (value, key, object). Callbacks may exit iteration early by explicitly - * returning `false`. * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. - * @example + * _.isArray(squares); + * // => false * - * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * alert(key); - * }); - * // => alerts '0', '1', and 'length' (order is not guaranteed) + * _.isArray(squares.value()); + * // => true */ - var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); + function lodash(value) { + // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor + return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__')) + ? value + : new lodashWrapper(value); + } /** - * A fallback implementation of `isPlainObject` that checks if a given `value` - * is an object created by the `Object` constructor, assuming objects created - * by the `Object` constructor have no inherited enumerable properties and that - * there are no `Object.prototype` extensions. + * A fast path for creating `lodash` wrapper objects. * * @private - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. + * @param {*} value The value to wrap in a `lodash` instance. + * @param {boolean} chainAll A flag to enable chaining for all methods + * @returns {Object} Returns a `lodash` instance. */ - function shimIsPlainObject(value) { - // avoid non-objects and false positives for `arguments` objects - var result = false; - if (!(value && typeof value == 'object') || isArguments(value)) { - return result; - } - // check that the constructor is `Object` (i.e. `Object instanceof Object`) - var ctor = value.constructor; - if ((!isFunction(ctor) && (!noNodeClass || !isNode(value))) || ctor instanceof ctor) { - // IE < 9 iterates inherited properties before own properties. If the first - // iterated property is an object's own property then there are no inherited - // enumerable properties. - if (iteratesOwnLast) { - forIn(value, function(value, key, object) { - result = !hasOwnProperty.call(object, key); - return false; - }); - return result === false; - } - // In most environments an object's own properties are iterated before - // its inherited properties. If the last iterated property is an object's - // own property then there are no inherited enumerable properties. - forIn(value, function(value, key) { - result = key; - }); - return result === false || hasOwnProperty.call(value, result); - } - return result; + function lodashWrapper(value, chainAll) { + this.__chain__ = !!chainAll; + this.__wrapped__ = value; } + // ensure `new lodashWrapper` is an instance of `lodash` + lodashWrapper.prototype = lodash.prototype; /** - * A fallback implementation of `Object.keys` that produces an array of the - * given object's own enumerable property names. + * An object used to flag environments features. * - * @private - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. + * @static + * @memberOf _ + * @type Object */ - function shimKeys(object) { - var result = []; - forOwn(object, function(value, key) { - result.push(key); - }); - return result; - } + var support = lodash.support = {}; + + (function() { + var ctor = function() { this.x = 1; }, + object = { '0': 1, 'length': 1 }, + props = []; + + ctor.prototype = { 'valueOf': 1, 'y': 1 }; + for (var key in new ctor) { props.push(key); } + for (key in arguments) { } + + /** + * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9). + * + * @memberOf _.support + * @type boolean + */ + support.argsClass = toString.call(arguments) == argsClass; + + /** + * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5). + * + * @memberOf _.support + * @type boolean + */ + support.argsObject = arguments.constructor == Object && !(arguments instanceof Array); + + /** + * Detect if `name` or `message` properties of `Error.prototype` are + * enumerable by default. (IE < 9, Safari < 5.1) + * + * @memberOf _.support + * @type boolean + */ + support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); + + /** + * Detect if `prototype` properties are enumerable by default. + * + * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + * (if the prototype or a property on the prototype has been set) + * incorrectly sets a function's `prototype` property [[Enumerable]] + * value to `true`. + * + * @memberOf _.support + * @type boolean + */ + support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype'); + + /** + * Detect if functions can be decompiled by `Function#toString` + * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps). + * + * @memberOf _.support + * @type boolean + */ + support.funcDecomp = !reNative.test(root.WinRTError) && reThis.test(function() { return this; }); + + /** + * Detect if `Function#name` is supported (all but IE). + * + * @memberOf _.support + * @type boolean + */ + support.funcNames = typeof Function.name == 'string'; + + /** + * Detect if `arguments` object indexes are non-enumerable + * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1). + * + * @memberOf _.support + * @type boolean + */ + support.nonEnumArgs = key != 0; + + /** + * Detect if properties shadowing those on `Object.prototype` are non-enumerable. + * + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug). + * + * @memberOf _.support + * @type boolean + */ + support.nonEnumShadows = !/valueOf/.test(props); + + /** + * Detect if own properties are iterated after inherited properties (all but IE < 9). + * + * @memberOf _.support + * @type boolean + */ + support.ownLast = props[0] != 'x'; + + /** + * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly. + * + * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` + * and `splice()` functions that fail to remove the last element, `value[0]`, + * of array-like objects even though the `length` property is set to `0`. + * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + * + * @memberOf _.support + * @type boolean + */ + support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]); + + /** + * Detect lack of support for accessing string characters by index. + * + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + * + * @memberOf _.support + * @type boolean + */ + support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx'; + + /** + * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9) + * and that the JS engine errors when attempting to coerce an object to + * a string without a `toString` function. + * + * @memberOf _.support + * @type boolean + */ + try { + support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + '')); + } catch(e) { + support.nodeClass = true; + } + }(1)); + + /*--------------------------------------------------------------------------*/ /** - * Used to convert characters to HTML entities: + * The template used to create iterator functions. * - * Though the `>` character is escaped for symmetry, characters like `>` and `/` - * don't require escaping in HTML and have no special meaning unless they're part - * of a tag or an unquoted attribute value. - * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact") + * @private + * @param {Object} data The data object used to populate the text. + * @returns {string} Returns the interpolated text. */ - var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; + var iteratorTemplate = function(obj) { + + var __p = 'var index, iterable = ' + + (obj.firstArg) + + ', result = ' + + (obj.init) + + ';\nif (!iterable) return result;\n' + + (obj.top) + + ';'; + if (obj.array) { + __p += '\nvar length = iterable.length; index = -1;\nif (' + + (obj.array) + + ') { '; + if (support.unindexedChars) { + __p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } '; + } + __p += '\n while (++index < length) {\n ' + + (obj.loop) + + ';\n }\n}\nelse { '; + } else if (support.nonEnumArgs) { + __p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' + + (obj.loop) + + ';\n }\n } else { '; + } + + if (support.enumPrototypes) { + __p += '\n var skipProto = typeof iterable == \'function\';\n '; + } + + if (support.enumErrorProps) { + __p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n '; + } + + var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); } + + if (obj.useHas && obj.keys) { + __p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n'; + if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; + } + __p += + (obj.loop) + + '; '; + if (conditions.length) { + __p += '\n }'; + } + __p += '\n } '; + } else { + __p += '\n for (index in iterable) {\n'; + if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; + } + __p += + (obj.loop) + + '; '; + if (conditions.length) { + __p += '\n }'; + } + __p += '\n } '; + if (support.nonEnumShadows) { + __p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n '; + for (k = 0; k < 7; k++) { + __p += '\n index = \'' + + (obj.shadowedProps[k]) + + '\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))'; + if (!obj.useHas) { + __p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])'; + } + __p += ') {\n ' + + (obj.loop) + + ';\n } '; + } + __p += '\n } '; + } + + } - /** Used to convert HTML entities to characters */ - var htmlUnescapes = invert(htmlEscapes); + if (obj.array || support.nonEnumArgs) { + __p += '\n}'; + } + __p += + (obj.bottom) + + ';\nreturn result'; + + return __p + }; /*--------------------------------------------------------------------------*/ /** - * Creates a clone of `value`. If `deep` is `true`, nested objects will also - * be cloned, otherwise they will be assigned by reference. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to clone. - * @param {Boolean} deep A flag to indicate a deep clone. - * @param- {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `deep`. - * @param- {Array} [stackA=[]] Internally used to track traversed source objects. - * @param- {Array} [stackB=[]] Internally used to associate clones with their - * source counterparts. - * @returns {Mixed} Returns the cloned `value`. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * var shallow = _.clone(stooges); - * shallow[0] === stooges[0]; - * // => true + * The base implementation of `_.bind` that creates the bound function and + * sets its meta data. * - * var deep = _.clone(stooges, true); - * deep[0] === stooges[0]; - * // => false + * @private + * @param {Array} bindData The bind data array. + * @returns {Function} Returns the new bound function. */ - function clone(value, deep, guard, stackA, stackB) { - if (value == null) { - return value; + function baseBind(bindData) { + var func = bindData[0], + partialArgs = bindData[2], + thisArg = bindData[4]; + + function bound() { + // `Function#bind` spec + // http://es5.github.io/#x15.3.4.5 + if (partialArgs) { + var args = partialArgs.slice(); + push.apply(args, arguments); + } + // mimic the constructor's `return` behavior + // http://es5.github.io/#x13.2.2 + if (this instanceof bound) { + // ensure `new bound` is an instance of `func` + var thisBinding = baseCreate(func.prototype), + result = func.apply(thisBinding, args || arguments); + return isObject(result) ? result : thisBinding; + } + return func.apply(thisArg, args || arguments); } - if (guard) { - deep = false; + setBindData(bound, bindData); + return bound; + } + + /** + * The base implementation of `_.clone` without argument juggling or support + * for `thisArg` binding. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} [isDeep=false] Specify a deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates clones with source counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, isDeep, callback, stackA, stackB) { + if (callback) { + var result = callback(value); + if (typeof result != 'undefined') { + return result; + } } // inspect [[Class]] var isObj = isObject(value); if (isObj) { var className = toString.call(value); - if (!cloneableClasses[className] || (noNodeClass && isNode(value))) { + if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) { return value; } - var isArr = isArray(value); - } - // shallow clone - if (!isObj || !deep) { - return isObj - ? (isArr ? slice(value) : assign({}, value)) - : value; + var ctor = ctorByClass[className]; + switch (className) { + case boolClass: + case dateClass: + return new ctor(+value); + + case numberClass: + case stringClass: + return new ctor(value); + + case regexpClass: + result = ctor(value.source, reFlags.exec(value)); + result.lastIndex = value.lastIndex; + return result; + } + } else { + return value; } - var ctor = ctorByClass[className]; - switch (className) { - case boolClass: - case dateClass: - return new ctor(+value); - - case numberClass: - case stringClass: - return new ctor(value); + var isArr = isArray(value); + if (isDeep) { + // check for circular references and return corresponding clone + var initedStack = !stackA; + stackA || (stackA = getArray()); + stackB || (stackB = getArray()); - case regexpClass: - return ctor(value.source, reFlags.exec(value)); + var length = stackA.length; + while (length--) { + if (stackA[length] == value) { + return stackB[length]; + } + } + result = isArr ? ctor(value.length) : {}; } - // check for circular references and return corresponding clone - stackA || (stackA = []); - stackB || (stackB = []); - - var length = stackA.length; - while (length--) { - if (stackA[length] == value) { - return stackB[length]; + else { + result = isArr ? slice(value) : assign({}, value); + } + // add array properties assigned by `RegExp#exec` + if (isArr) { + if (hasOwnProperty.call(value, 'index')) { + result.index = value.index; + } + if (hasOwnProperty.call(value, 'input')) { + result.input = value.input; } } - // init cloned object - var result = isArr ? ctor(value.length) : {}; - + // exit for shallow clone + if (!isDeep) { + return result; + } // add the source value to the stack of traversed objects // and associate it with its clone stackA.push(value); stackB.push(result); // recursively populate clone (susceptible to call stack limits) - (isArr ? forEach : forOwn)(value, function(objValue, key) { - result[key] = clone(objValue, deep, null, stackA, stackB); + (isArr ? baseEach : forOwn)(value, function(objValue, key) { + result[key] = baseClone(objValue, isDeep, callback, stackA, stackB); }); - // add array properties assigned by `RegExp#exec` - if (isArr) { - if (hasOwnProperty.call(value, 'index')) { - result.index = value.index; - } - if (hasOwnProperty.call(value, 'input')) { - result.input = value.input; - } + if (initedStack) { + releaseArray(stackA); + releaseArray(stackB); } return result; } /** - * Creates a deep clone of `value`. Functions and DOM nodes are **not** cloned. - * The enumerable properties of `arguments` objects and objects created by - * constructors other than `Object` are cloned to plain `Object` objects. - * - * Note: This function is loosely based on the structured clone algorithm. - * See http://www.w3.org/TR/html5/common-dom-interfaces.html#internal-structured-cloning-algorithm. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to deep clone. - * @returns {Mixed} Returns the deep cloned `value`. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; + * The base implementation of `_.create` without support for assigning + * properties to the created object. * - * var deep = _.cloneDeep(stooges); - * deep[0] === stooges[0]; - * // => false + * @private + * @param {Object} prototype The object to inherit from. + * @returns {Object} Returns the new object. */ - function cloneDeep(value) { - return clone(value, true); + function baseCreate(prototype, properties) { + return isObject(prototype) ? nativeCreate(prototype) : {}; + } + // fallback for browsers without `Object.create` + if (!nativeCreate) { + baseCreate = (function() { + function Object() {} + return function(prototype) { + if (isObject(prototype)) { + Object.prototype = prototype; + var result = new Object; + Object.prototype = null; + } + return result || root.Object(); + }; + }()); } /** - * Assigns own enumerable properties of source object(s) to the `destination` - * object for all `destination` properties that resolve to `null`/`undefined`. - * Once a property is set, additional defaults of the same property will be - * ignored. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The destination object. - * @param {Object} [default1, default2, ...] The default objects. - * @returns {Object} Returns the destination object. - * @example - * - * var iceCream = { 'flavor': 'chocolate' }; - * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); - * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } - */ - var defaults = createIterator(assignIteratorOptions, { - 'objectLoop': 'if (result[index] == null) ' + assignIteratorOptions.objectLoop - }); - - /** - * Creates a sorted array of all enumerable properties, own and inherited, - * of `object` that have function values. - * - * @static - * @memberOf _ - * @alias methods - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names that have function values. - * @example + * The base implementation of `_.createCallback` without support for creating + * "_.pluck" or "_.where" style callbacks. * - * _.functions(_); - * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + * @private + * @param {*} [func=identity] The value to convert to a callback. + * @param {*} [thisArg] The `this` binding of the created callback. + * @param {number} [argCount] The number of arguments the callback accepts. + * @returns {Function} Returns a callback function. */ - function functions(object) { - var result = []; - forIn(object, function(value, key) { - if (isFunction(value)) { - result.push(key); + function baseCreateCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + // exit early for no `thisArg` or already bound by `Function#bind` + if (typeof thisArg == 'undefined' || !('prototype' in func)) { + return func; + } + var bindData = func.__bindData__; + if (typeof bindData == 'undefined') { + if (support.funcNames) { + bindData = !func.name; } - }); - return result.sort(); + bindData = bindData || !support.funcDecomp; + if (!bindData) { + var source = fnToString.call(func); + if (!support.funcNames) { + bindData = !reFuncName.test(source); + } + if (!bindData) { + // checks if `func` references the `this` keyword and stores the result + bindData = reThis.test(source); + setBindData(func, bindData); + } + } + } + // exit early if there are no `this` references or `func` is bound + if (bindData === false || (bindData !== true && bindData[1] & 1)) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 2: return function(a, b) { + return func.call(thisArg, a, b); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + } + return bind(func, thisArg); } /** - * Checks if the specified object `property` exists and is a direct property, - * instead of an inherited property. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to check. - * @param {String} property The property to check for. - * @returns {Boolean} Returns `true` if key is a direct property, else `false`. - * @example + * The base implementation of `createWrapper` that creates the wrapper and + * sets its meta data. * - * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); - * // => true + * @private + * @param {Array} bindData The bind data array. + * @returns {Function} Returns the new function. */ - function has(object, property) { - return object ? hasOwnProperty.call(object, property) : false; - } + function baseCreateWrapper(bindData) { + var func = bindData[0], + bitmask = bindData[1], + partialArgs = bindData[2], + partialRightArgs = bindData[3], + thisArg = bindData[4], + arity = bindData[5]; + + var isBind = bitmask & 1, + isBindKey = bitmask & 2, + isCurry = bitmask & 4, + isCurryBound = bitmask & 8, + key = func; - /** - * Creates an object composed of the inverted keys and values of the given `object`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to invert. - * @returns {Object} Returns the created inverted object. - * @example - * - * _.invert({ 'first': 'Moe', 'second': 'Larry', 'third': 'Curly' }); - * // => { 'Moe': 'first', 'Larry': 'second', 'Curly': 'third' } (order is not guaranteed) - */ - function invert(object) { - var result = {}; - forOwn(object, function(value, key) { - result[value] = key; - }); - return result; + function bound() { + var thisBinding = isBind ? thisArg : this; + if (partialArgs) { + var args = partialArgs.slice(); + push.apply(args, arguments); + } + if (partialRightArgs || isCurry) { + args || (args = slice(arguments)); + if (partialRightArgs) { + push.apply(args, partialRightArgs); + } + if (isCurry && args.length < arity) { + bitmask |= 16 & ~32; + return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); + } + } + args || (args = arguments); + if (isBindKey) { + func = thisBinding[key]; + } + if (this instanceof bound) { + thisBinding = baseCreate(func.prototype); + var result = func.apply(thisBinding, args); + return isObject(result) ? result : thisBinding; + } + return func.apply(thisBinding, args); + } + setBindData(bound, bindData); + return bound; } /** - * Checks if `value` is an array. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. - * @example - * - * (function() { return _.isArray(arguments); })(); - * // => false + * The base implementation of `_.difference` that accepts a single array + * of values to exclude. * - * _.isArray([1, 2, 3]); - * // => true + * @private + * @param {Array} array The array to process. + * @param {Array} [values] The array of values to exclude. + * @returns {Array} Returns a new array of filtered values. */ - var isArray = nativeIsArray || function(value) { - // `instanceof` may cause a memory leak in IE 7 if `value` is a host object - // http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak - return (argsAreObjects && value instanceof Array) || toString.call(value) == arrayClass; - }; + function baseDifference(array, values) { + var index = -1, + indexOf = getIndexOf(), + length = array ? array.length : 0, + isLarge = length >= largeArraySize && indexOf === baseIndexOf, + result = []; - /** - * Checks if `value` is a boolean (`true` or `false`) value. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. - * @example - * - * _.isBoolean(null); - * // => false - */ - function isBoolean(value) { - return value === true || value === false || toString.call(value) == boolClass; + if (isLarge) { + var cache = createCache(values); + if (cache) { + indexOf = cacheIndexOf; + values = cache; + } else { + isLarge = false; + } + } + while (++index < length) { + var value = array[index]; + if (indexOf(values, value) < 0) { + result.push(value); + } + } + if (isLarge) { + releaseObject(values); + } + return result; } /** - * Checks if `value` is a date. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. - * @example + * The base implementation of `_.flatten` without support for callback + * shorthands or `thisArg` binding. * - * _.isDate(new Date); - * // => true + * @private + * @param {Array} array The array to flatten. + * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. + * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects. + * @param {number} [fromIndex=0] The index to start from. + * @returns {Array} Returns a new flattened array. */ - function isDate(value) { - return value instanceof Date || toString.call(value) == dateClass; - } + function baseFlatten(array, isShallow, isStrict, fromIndex) { + var index = (fromIndex || 0) - 1, + length = array ? array.length : 0, + result = []; - /** - * Checks if `value` is a DOM element. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - */ - function isElement(value) { - return value ? value.nodeType === 1 : false; - } + while (++index < length) { + var value = array[index]; - /** - * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a - * length of `0` and objects with no own enumerable properties are considered - * "empty". - * - * @static - * @memberOf _ - * @category Objects - * @param {Array|Object|String} value The value to inspect. - * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. - * @example - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({}); - * // => true - * - * _.isEmpty(''); - * // => true - */ - function isEmpty(value) { - var result = true; - if (!value) { - return result; - } - var className = toString.call(value), - length = value.length; + if (value && typeof value == 'object' && typeof value.length == 'number' + && (isArray(value) || isArguments(value))) { + // recursively flatten arrays (susceptible to call stack limits) + if (!isShallow) { + value = baseFlatten(value, isShallow, isStrict); + } + var valIndex = -1, + valLength = value.length, + resIndex = result.length; - if ((className == arrayClass || className == stringClass || - className == argsClass || (noArgsClass && isArguments(value))) || - (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { - return !length; + result.length += valLength; + while (++valIndex < valLength) { + result[resIndex++] = value[valIndex]; + } + } else if (!isStrict) { + result.push(value); + } } - forOwn(value, function() { - return (result = false); - }); return result; } /** - * Performs a deep comparison between two values to determine if they are - * equivalent to each other. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} a The value to compare. - * @param {Mixed} b The other value to compare. - * @param- {Object} [stackA=[]] Internally used track traversed `a` objects. - * @param- {Object} [stackB=[]] Internally used track traversed `b` objects. - * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. - * @example + * The base implementation of `_.isEqual`, without support for `thisArg` binding, + * that allows partial "_.where" style comparisons. * - * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * - * moe == clone; - * // => false - * - * _.isEqual(moe, clone); - * // => true + * @private + * @param {*} a The value to compare. + * @param {*} b The other value to compare. + * @param {Function} [callback] The function to customize comparing values. + * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons. + * @param {Array} [stackA=[]] Tracks traversed `a` objects. + * @param {Array} [stackB=[]] Tracks traversed `b` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ - function isEqual(a, b, stackA, stackB) { + function baseIsEqual(a, b, callback, isWhere, stackA, stackB) { + // used to indicate that when comparing objects, `a` has at least the properties of `b` + if (callback) { + var result = callback(a, b); + if (typeof result != 'undefined') { + return !!result; + } + } // exit early for identical values if (a === b) { // treat `+0` vs. `-0` as not equal return a !== 0 || (1 / a == 1 / b); } - // a strict comparison is necessary because `null == undefined` + var type = typeof a, + otherType = typeof b; + + // exit early for unlike primitive values + if (a === a && + !(a && objectTypes[type]) && + !(b && objectTypes[otherType])) { + return false; + } + // exit early for `null` and `undefined` avoiding ES3's Function#call behavior + // http://es5.github.io/#x15.3.4.4 if (a == null || b == null) { return a === b; } // compare [[Class]] names var className = toString.call(a), - otherName = toString.call(b); + otherClass = toString.call(b); if (className == argsClass) { className = objectClass; } - if (otherName == argsClass) { - otherName = objectClass; + if (otherClass == argsClass) { + otherClass = objectClass; } - if (className != otherName) { + if (className != otherClass) { return false; } switch (className) { case boolClass: case dateClass: // coerce dates and booleans to numbers, dates to milliseconds and booleans - // to `1` or `0`, treating invalid dates coerced to `NaN` as not equal + // to `1` or `0` treating invalid dates coerced to `NaN` as not equal return +a == +b; case numberClass: // treat `NaN` vs. `NaN` as equal - return a != +a + return (a != +a) ? b != +b // but treat `+0` vs. `-0` as not equal : (a == 0 ? (1 / a == 1 / b) : a == +b); case regexpClass: case stringClass: - // coerce regexes to strings (http://es5.github.com/#x15.10.6.4) + // coerce regexes to strings (http://es5.github.io/#x15.10.6.4) // treat string primitives and their corresponding object instances as equal - return a == b + ''; + return a == String(b); } var isArr = className == arrayClass; if (!isArr) { // unwrap any `lodash` wrapped values - if (a.__wrapped__ || b.__wrapped__) { - return isEqual(a.__wrapped__ || a, b.__wrapped__ || b); + var aWrapped = hasOwnProperty.call(a, '__wrapped__'), + bWrapped = hasOwnProperty.call(b, '__wrapped__'); + + if (aWrapped || bWrapped) { + return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB); } // exit for functions and DOM nodes - if (className != objectClass || (noNodeClass && (isNode(a) || isNode(b)))) { + if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) { return false; } // in older versions of Opera, `arguments` objects have `Array` constructors - var ctorA = !argsAreObjects && isArguments(a) ? Object : a.constructor, - ctorB = !argsAreObjects && isArguments(b) ? Object : b.constructor; + var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor, + ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor; // non `Object` object instances with different constructors are not equal - if (ctorA != ctorB && !( - isFunction(ctorA) && ctorA instanceof ctorA && - isFunction(ctorB) && ctorB instanceof ctorB - )) { + if (ctorA != ctorB && + !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) && + ('constructor' in a && 'constructor' in b) + ) { return false; } } // assume cyclic structures are equal // the algorithm for detecting cyclic structures is adapted from ES 5.1 - // section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3) - stackA || (stackA = []); - stackB || (stackB = []); + // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3) + var initedStack = !stackA; + stackA || (stackA = getArray()); + stackB || (stackB = getArray()); var length = stackA.length; while (length--) { @@ -8301,9 +8811,8 @@ var JXON = new (function () { return stackB[length] == b; } } - var index = -1, - result = true, - size = 0; + var size = 0; + result = true; // add `a` and `b` to the stack of traversed objects stackA.push(a); @@ -8311,428 +8820,1080 @@ var JXON = new (function () { // recursively compare objects and arrays (susceptible to call stack limits) if (isArr) { - // compare lengths to determine if a deep comparison is necessary - size = a.length; - result = size == b.length; + length = a.length; + size = b.length; - if (result) { - // deep compare the contents, ignoring non-numeric properties - while (size--) { - if (!(result = isEqual(a[size], b[size], stackA, stackB))) { - break; + // compare lengths to determine if a deep comparison is necessary + result = size == a.length; + if (!result && !isWhere) { + return result; + } + // deep compare the contents, ignoring non-numeric properties + while (size--) { + var index = length, + value = b[size]; + + if (isWhere) { + while (index--) { + if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) { + break; + } } + } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) { + break; } } return result; } // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys` // which, in this case, is more costly - forIn(a, function(value, key, a) { - if (hasOwnProperty.call(a, key)) { + forIn(b, function(value, key, b) { + if (hasOwnProperty.call(b, key)) { // count the number of properties. size++; // deep compare each property value. - return (result = hasOwnProperty.call(b, key) && isEqual(value, b[key], stackA, stackB)); + return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB)); } }); - if (result) { + if (result && !isWhere) { // ensure both objects have the same number of properties - forIn(b, function(value, key, b) { - if (hasOwnProperty.call(b, key)) { - // `size` will be `-1` if `b` has more properties than `a` + forIn(a, function(value, key, a) { + if (hasOwnProperty.call(a, key)) { + // `size` will be `-1` if `a` has more properties than `b` return (result = --size > -1); } }); } + if (initedStack) { + releaseArray(stackA); + releaseArray(stackB); + } return result; } /** - * Checks if `value` is, or can be coerced to, a finite number. - * - * Note: This is not the same as native `isFinite`, which will return true for - * booleans and empty strings. See http://es5.github.com/#x15.1.2.5. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. - * @example - * - * _.isFinite(-101); - * // => true - * - * _.isFinite('10'); - * // => true - * - * _.isFinite(true); - * // => false + * The base implementation of `_.merge` without argument juggling or support + * for `thisArg` binding. * - * _.isFinite(''); - * // => false + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} [callback] The function to customize merging properties. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + */ + function baseMerge(object, source, callback, stackA, stackB) { + (isArray(source) ? forEach : forOwn)(source, function(source, key) { + var found, + isArr, + result = source, + value = object[key]; + + if (source && ((isArr = isArray(source)) || isPlainObject(source))) { + // avoid merging previously merged cyclic sources + var stackLength = stackA.length; + while (stackLength--) { + if ((found = stackA[stackLength] == source)) { + value = stackB[stackLength]; + break; + } + } + if (!found) { + var isShallow; + if (callback) { + result = callback(value, source); + if ((isShallow = typeof result != 'undefined')) { + value = result; + } + } + if (!isShallow) { + value = isArr + ? (isArray(value) ? value : []) + : (isPlainObject(value) ? value : {}); + } + // add `source` and associated `value` to the stack of traversed objects + stackA.push(source); + stackB.push(value); + + // recursively merge objects and arrays (susceptible to call stack limits) + if (!isShallow) { + baseMerge(value, source, callback, stackA, stackB); + } + } + } + else { + if (callback) { + result = callback(value, source); + if (typeof result == 'undefined') { + result = source; + } + } + if (typeof result != 'undefined') { + value = result; + } + } + object[key] = value; + }); + } + + /** + * The base implementation of `_.uniq` without support for callback shorthands + * or `thisArg` binding. * - * _.isFinite(Infinity); - * // => false + * @private + * @param {Array} array The array to process. + * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. + * @param {Function} [callback] The function called per iteration. + * @returns {Array} Returns a duplicate-value-free array. */ - function isFinite(value) { - return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value)); + function baseUniq(array, isSorted, callback) { + var index = -1, + indexOf = getIndexOf(), + length = array ? array.length : 0, + result = []; + + var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf, + seen = (callback || isLarge) ? getArray() : result; + + if (isLarge) { + var cache = createCache(seen); + if (cache) { + indexOf = cacheIndexOf; + seen = cache; + } else { + isLarge = false; + seen = callback ? seen : (releaseArray(seen), result); + } + } + while (++index < length) { + var value = array[index], + computed = callback ? callback(value, index, array) : value; + + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + if (callback || isLarge) { + seen.push(computed); + } + result.push(value); + } + } + if (isLarge) { + releaseArray(seen.array); + releaseObject(seen); + } else if (callback) { + releaseArray(seen); + } + return result; } /** - * Checks if `value` is a function. + * Creates a function that aggregates a collection, creating an object composed + * of keys generated from the results of running each element of the collection + * through a callback. The given `setter` function sets the keys and values + * of the composed object. + * + * @private + * @param {Function} setter The setter function. + * @returns {Function} Returns the new aggregator function. + */ + function createAggregator(setter) { + return function(collection, callback, thisArg) { + var result = {}; + callback = lodash.createCallback(callback, thisArg, 3); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + var value = collection[index]; + setter(result, value, callback(value, index, collection), collection); + } + } else { + baseEach(collection, function(value, key, collection) { + setter(result, value, callback(value, key, collection), collection); + }); + } + return result; + }; + } + + /** + * Creates a function that, when called, either curries or invokes `func` + * with an optional `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to reference. + * @param {number} bitmask The bitmask of method flags to compose. + * The bitmask may be composed of the following flags: + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` + * 8 - `_.curry` (bound) + * 16 - `_.partial` + * 32 - `_.partialRight` + * @param {Array} [partialArgs] An array of arguments to prepend to those + * provided to the new function. + * @param {Array} [partialRightArgs] An array of arguments to append to those + * provided to the new function. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new function. + */ + function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { + var isBind = bitmask & 1, + isBindKey = bitmask & 2, + isCurry = bitmask & 4, + isCurryBound = bitmask & 8, + isPartial = bitmask & 16, + isPartialRight = bitmask & 32; + + if (!isBindKey && !isFunction(func)) { + throw new TypeError; + } + if (isPartial && !partialArgs.length) { + bitmask &= ~16; + isPartial = partialArgs = false; + } + if (isPartialRight && !partialRightArgs.length) { + bitmask &= ~32; + isPartialRight = partialRightArgs = false; + } + var bindData = func && func.__bindData__; + if (bindData && bindData !== true) { + bindData = bindData.slice(); + + // set `thisBinding` is not previously bound + if (isBind && !(bindData[1] & 1)) { + bindData[4] = thisArg; + } + // set if previously bound but not currently (subsequent curried functions) + if (!isBind && bindData[1] & 1) { + bitmask |= 8; + } + // set curried arity if not yet set + if (isCurry && !(bindData[1] & 4)) { + bindData[5] = arity; + } + // append partial left arguments + if (isPartial) { + push.apply(bindData[2] || (bindData[2] = []), partialArgs); + } + // append partial right arguments + if (isPartialRight) { + push.apply(bindData[3] || (bindData[3] = []), partialRightArgs); + } + // merge flags + bindData[1] |= bitmask; + return createWrapper.apply(null, bindData); + } + // fast path for `_.bind` + var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; + return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); + } + + /** + * Creates compiled iteration functions. + * + * @private + * @param {...Object} [options] The compile options object(s). + * @param {string} [options.array] Code to determine if the iterable is an array or array-like. + * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop. + * @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration. + * @param {string} [options.args] A comma separated string of iteration function arguments. + * @param {string} [options.top] Code to execute before the iteration branches. + * @param {string} [options.loop] Code to execute in the object loop. + * @param {string} [options.bottom] Code to execute after the iteration branches. + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + // data properties + iteratorData.shadowedProps = shadowedProps; + + // iterator options + iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = ''; + iteratorData.init = 'iterable'; + iteratorData.useHas = true; + + // merge options into a template data object + for (var object, index = 0; object = arguments[index]; index++) { + for (var key in object) { + iteratorData[key] = object[key]; + } + } + var args = iteratorData.args; + iteratorData.firstArg = /^[^,]+/.exec(args)[0]; + + // create the function factory + var factory = Function( + 'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' + + 'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' + + 'objectTypes, nonEnumProps, stringClass, stringProto, toString', + 'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}' + ); + + // return the compiled function + return factory( + baseCreateCallback, errorClass, errorProto, hasOwnProperty, + indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto, + objectTypes, nonEnumProps, stringClass, stringProto, toString + ); + } + + /** + * Gets the appropriate "indexOf" function. If the `_.indexOf` method is + * customized, this method returns the custom method, otherwise it returns + * the `baseIndexOf` function. + * + * @private + * @returns {Function} Returns the "indexOf" function. + */ + function getIndexOf() { + var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result; + return result; + } + + /** + * Sets `this` binding data on a given function. + * + * @private + * @param {Function} func The function to set data on. + * @param {Array} value The data array to set. + */ + var setBindData = !defineProperty ? noop : function(func, value) { + descriptor.value = value; + defineProperty(func, '__bindData__', descriptor); + }; + + /** + * A fallback implementation of `isPlainObject` which checks if a given value + * is an object created by the `Object` constructor, assuming objects created + * by the `Object` constructor have no inherited enumerable properties and that + * there are no `Object.prototype` extensions. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + */ + function shimIsPlainObject(value) { + var ctor, + result; + + // avoid non Object objects, `arguments` objects, and DOM elements + if (!(value && toString.call(value) == objectClass) || + (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor)) || + (!support.argsClass && isArguments(value)) || + (!support.nodeClass && isNode(value))) { + return false; + } + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + if (support.ownLast) { + forIn(value, function(value, key, object) { + result = hasOwnProperty.call(object, key); + return false; + }); + return result !== false; + } + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + forIn(value, function(value, key) { + result = key; + }); + return typeof result == 'undefined' || hasOwnProperty.call(value, result); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if `value` is an `arguments` object. * * @static * @memberOf _ * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`. * @example * - * _.isFunction(_); + * (function() { return _.isArguments(arguments); })(1, 2, 3); * // => true + * + * _.isArguments([1, 2, 3]); + * // => false */ - function isFunction(value) { - return typeof value == 'function'; + function isArguments(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == argsClass || false; } - // fallback for older versions of Chrome and Safari - if (isFunction(/x/)) { - isFunction = function(value) { - return value instanceof Function || toString.call(value) == funcClass; + // fallback for browsers that can't detect `arguments` objects by [[Class]] + if (!support.argsClass) { + isArguments = function(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false; }; } /** - * Checks if `value` is the language type of Object. - * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * Checks if `value` is an array. * * @static * @memberOf _ + * @type Function * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an array, else `false`. * @example * - * _.isObject({}); - * // => true + * (function() { return _.isArray(arguments); })(); + * // => false * - * _.isObject([1, 2, 3]); + * _.isArray([1, 2, 3]); * // => true + */ + var isArray = nativeIsArray || function(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == arrayClass || false; + }; + + /** + * A fallback implementation of `Object.keys` which produces an array of the + * given object's own enumerable property names. * - * _.isObject(1); - * // => false + * @private + * @type Function + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names. */ - function isObject(value) { - // check if the value is the ECMAScript language type of Object - // http://es5.github.com/#x8 - // and avoid a V8 bug - // http://code.google.com/p/v8/issues/detail?id=2291 - return value ? objectTypes[typeof value] : false; - } + var shimKeys = createIterator({ + 'args': 'object', + 'init': '[]', + 'top': 'if (!(objectTypes[typeof object])) return result', + 'loop': 'result.push(index)' + }); + + /** + * Creates an array composed of the own enumerable property names of an object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (property order is not guaranteed across environments) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + if (!isObject(object)) { + return []; + } + if ((support.enumPrototypes && typeof object == 'function') || + (support.nonEnumArgs && object.length && isArguments(object))) { + return shimKeys(object); + } + return nativeKeys(object); + }; + + /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */ + var eachIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)", + 'array': "typeof length == 'number'", + 'keys': keys, + 'loop': 'if (callback(iterable[index], index, collection) === false) return result' + }; + + /** Reusable iterator options for `assign` and `defaults` */ + var defaultsIteratorOptions = { + 'args': 'object, source, guard', + 'top': + 'var args = arguments,\n' + + ' argsIndex = 0,\n' + + " argsLength = typeof guard == 'number' ? 2 : args.length;\n" + + 'while (++argsIndex < argsLength) {\n' + + ' iterable = args[argsIndex];\n' + + ' if (iterable && objectTypes[typeof iterable]) {', + 'keys': keys, + 'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]", + 'bottom': ' }\n}' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top, + 'array': false + }; /** - * Checks if `value` is `NaN`. + * A function compiled to iterate `arguments` objects, arrays, objects, and + * strings consistenly across environments, executing the callback for each + * element in the collection. The callback is bound to `thisArg` and invoked + * with three arguments; (value, index|key, collection). Callbacks may exit + * iteration early by explicitly returning `false`. * - * Note: This is not the same as native `isNaN`, which will return `true` for - * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * @private + * @type Function + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|string} Returns `collection`. + */ + var baseEach = createIterator(eachIteratorOptions); + + /*--------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources will overwrite property assignments of previous + * sources. If a callback is provided it will be executed to produce the + * assigned values. The callback is bound to `thisArg` and invoked with two + * arguments; (objectValue, sourceValue). * * @static * @memberOf _ + * @type Function + * @alias extend * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @param {Object} object The destination object. + * @param {...Object} [source] The source objects. + * @param {Function} [callback] The function to customize assigning values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the destination object. * @example * - * _.isNaN(NaN); - * // => true + * _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); + * // => { 'name': 'fred', 'employer': 'slate' } * - * _.isNaN(new Number(NaN)); - * // => true + * var defaults = _.partialRight(_.assign, function(a, b) { + * return typeof a == 'undefined' ? b : a; + * }); + * + * var object = { 'name': 'barney' }; + * defaults(object, { 'name': 'fred', 'employer': 'slate' }); + * // => { 'name': 'barney', 'employer': 'slate' } + */ + var assign = createIterator(defaultsIteratorOptions, { + 'top': + defaultsIteratorOptions.top.replace(';', + ';\n' + + "if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" + + ' var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n' + + "} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" + + ' callback = args[--argsLength];\n' + + '}' + ), + 'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]' + }); + + /** + * Creates a clone of `value`. If `isDeep` is `true` nested objects will also + * be cloned, otherwise they will be assigned by reference. If a callback + * is provided it will be executed to produce the cloned values. If the + * callback returns `undefined` cloning will be handled by the method instead. + * The callback is bound to `thisArg` and invoked with one argument; (value). + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to clone. + * @param {boolean} [isDeep=false] Specify a deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the cloned value. + * @example * - * isNaN(undefined); + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } + * ]; + * + * var shallow = _.clone(characters); + * shallow[0] === characters[0]; * // => true * - * _.isNaN(undefined); + * var deep = _.clone(characters, true); + * deep[0] === characters[0]; * // => false + * + * _.mixin({ + * 'clone': _.partialRight(_.clone, function(value) { + * return _.isElement(value) ? value.cloneNode(false) : undefined; + * }) + * }); + * + * var clone = _.clone(document.body); + * clone.childNodes.length; + * // => 0 */ - function isNaN(value) { - // `NaN` as a primitive is the only value that is not equal to itself - // (perform the [[Class]] check first to avoid errors with some host objects in IE) - return isNumber(value) && value != +value + function clone(value, isDeep, callback, thisArg) { + // allows working with "Collections" methods without using their `index` + // and `collection` arguments for `isDeep` and `callback` + if (typeof isDeep != 'boolean' && isDeep != null) { + thisArg = callback; + callback = isDeep; + isDeep = false; + } + return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); } /** - * Checks if `value` is `null`. + * Creates a deep clone of `value`. If a callback is provided it will be + * executed to produce the cloned values. If the callback returns `undefined` + * cloning will be handled by the method instead. The callback is bound to + * `thisArg` and invoked with one argument; (value). + * + * Note: This method is loosely based on the structured clone algorithm. Functions + * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and + * objects created by constructors other than `Object` are cloned to plain `Object` objects. + * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm. * * @static * @memberOf _ * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @param {*} value The value to deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the deep cloned value. * @example * - * _.isNull(null); - * // => true + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } + * ]; + * + * var deep = _.cloneDeep(characters); + * deep[0] === characters[0]; + * // => false + * + * var view = { + * 'label': 'docs', + * 'node': element + * }; + * + * var clone = _.cloneDeep(view, function(value) { + * return _.isElement(value) ? value.cloneNode(true) : undefined; + * }); * - * _.isNull(undefined); + * clone.node == view.node; * // => false */ - function isNull(value) { - return value === null; + function cloneDeep(value, callback, thisArg) { + return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); } /** - * Checks if `value` is a number. + * Iterates over own and inherited enumerable properties of an object, + * executing the callback for each property. The callback is bound to `thisArg` + * and invoked with three arguments; (value, key, object). Callbacks may exit + * iteration early by explicitly returning `false`. * * @static * @memberOf _ + * @type Function * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. * @example * - * _.isNumber(8.4 * 5); - * // => true + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * Shape.prototype.move = function(x, y) { + * this.x += x; + * this.y += y; + * }; + * + * _.forIn(new Shape, function(value, key) { + * console.log(key); + * }); + * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) */ - function isNumber(value) { - return typeof value == 'number' || toString.call(value) == numberClass; - } + var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); /** - * Checks if a given `value` is an object created by the `Object` constructor. + * Iterates over own enumerable properties of an object, executing the callback + * for each property. The callback is bound to `thisArg` and invoked with three + * arguments; (value, key, object). Callbacks may exit iteration early by + * explicitly returning `false`. * * @static * @memberOf _ + * @type Function * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. * @example * - * function Stooge(name, age) { - * this.name = name; - * this.age = age; - * } + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * console.log(key); + * }); + * // => logs '0', '1', and 'length' (property order is not guaranteed across environments) + */ + var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); + + /** + * Creates a sorted array of property names of all enumerable properties, + * own and inherited, of `object` that have function values. * - * _.isPlainObject(new Stooge('moe', 40)); - * // => false + * @static + * @memberOf _ + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names that have function values. + * @example * - * _.isPlainObject([1, 2, 3]); + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + */ + function functions(object) { + var result = []; + forIn(object, function(value, key) { + if (isFunction(value)) { + result.push(key); + } + }); + return result.sort(); + } + + /** + * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a + * length of `0` and objects with no own enumerable properties are considered + * "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|string} value The value to inspect. + * @returns {boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); * // => false * - * _.isPlainObject({ 'name': 'moe', 'age': 40 }); + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); * // => true */ - var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { - if (!(value && typeof value == 'object')) { - return false; + function isEmpty(value) { + var result = true; + if (!value) { + return result; } - var valueOf = value.valueOf, - objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); + var className = toString.call(value), + length = value.length; - return objProto - ? value == objProto || (getPrototypeOf(value) == objProto && !isArguments(value)) - : shimIsPlainObject(value); - }; + if ((className == arrayClass || className == stringClass || + (support.argsClass ? className == argsClass : isArguments(value))) || + (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { + return !length; + } + forOwn(value, function() { + return (result = false); + }); + return result; + } /** - * Checks if `value` is a regular expression. + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. If a callback is provided it will be executed + * to compare values. If the callback returns `undefined` comparisons will + * be handled by the method instead. The callback is bound to `thisArg` and + * invoked with two arguments; (a, b). * * @static * @memberOf _ * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @param {*} a The value to compare. + * @param {*} b The other value to compare. + * @param {Function} [callback] The function to customize comparing values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * - * _.isRegExp(/moe/); + * var object = { 'name': 'fred' }; + * var copy = { 'name': 'fred' }; + * + * object == copy; + * // => false + * + * _.isEqual(object, copy); + * // => true + * + * var words = ['hello', 'goodbye']; + * var otherWords = ['hi', 'goodbye']; + * + * _.isEqual(words, otherWords, function(a, b) { + * var reGreet = /^(?:hello|hi)$/i, + * aGreet = _.isString(a) && reGreet.test(a), + * bGreet = _.isString(b) && reGreet.test(b); + * + * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined; + * }); * // => true */ - function isRegExp(value) { - return value instanceof RegExp || toString.call(value) == regexpClass; + function isEqual(a, b, callback, thisArg) { + return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2)); } /** - * Checks if `value` is a string. + * Checks if `value` is a function. * * @static * @memberOf _ * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a function, else `false`. * @example * - * _.isString('moe'); + * _.isFunction(_); * // => true */ - function isString(value) { - return typeof value == 'string' || toString.call(value) == stringClass; + function isFunction(value) { + return typeof value == 'function'; + } + // fallback for older versions of Chrome and Safari + if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value == 'function' && toString.call(value) == funcClass; + }; } /** - * Checks if `value` is `undefined`. + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an object, else `false`. * @example * - * _.isUndefined(void 0); + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); * // => true + * + * _.isObject(1); + * // => false */ - function isUndefined(value) { - return typeof value == 'undefined'; + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.io/#x8 + // and avoid a V8 bug + // http://code.google.com/p/v8/issues/detail?id=2291 + return !!(value && objectTypes[typeof value]); } /** - * Creates an array composed of the own enumerable property names of `object`. + * Checks if `value` is an object created by the `Object` constructor. * * @static * @memberOf _ * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. * @example * - * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); - * // => ['one', 'two', 'three'] (order is not guaranteed) + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * _.isPlainObject(new Shape); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true */ - var keys = !nativeKeys ? shimKeys : function(object) { - // avoid iterating over the `prototype` property - return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') - ? shimKeys(object) - : (isObject(object) ? nativeKeys(object) : []); + var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { + if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) { + return false; + } + var valueOf = value.valueOf, + objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); + + return objProto + ? (value == objProto || getPrototypeOf(value) == objProto) + : shimIsPlainObject(value); }; /** - * Merges enumerable properties of the source object(s) into the `destination` - * object. Subsequent sources will overwrite propery assignments of previous - * sources. + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('fred'); + * // => true + */ + function isString(value) { + return typeof value == 'string' || + value && typeof value == 'object' && toString.call(value) == stringClass || false; + } + + /** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * will overwrite property assignments of previous sources. If a callback is + * provided it will be executed to produce the merged values of the destination + * and source properties. If the callback returns `undefined` merging will + * be handled by the method instead. The callback is bound to `thisArg` and + * invoked with two arguments; (objectValue, sourceValue). * * @static * @memberOf _ * @category Objects * @param {Object} object The destination object. - * @param {Object} [source1, source2, ...] The source objects. - * @param- {Object} [indicator] Internally used to indicate that the `stack` - * argument is an array of traversed objects instead of another source object. - * @param- {Array} [stackA=[]] Internally used to track traversed source objects. - * @param- {Array} [stackB=[]] Internally used to associate values with their - * source counterparts. + * @param {...Object} [source] The source objects. + * @param {Function} [callback] The function to customize merging properties. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the destination object. * @example * - * var stooges = [ - * { 'name': 'moe' }, - * { 'name': 'larry' } - * ]; + * var names = { + * 'characters': [ + * { 'name': 'barney' }, + * { 'name': 'fred' } + * ] + * }; * - * var ages = [ - * { 'age': 40 }, - * { 'age': 50 } - * ]; + * var ages = { + * 'characters': [ + * { 'age': 36 }, + * { 'age': 40 } + * ] + * }; * - * _.merge(stooges, ages); - * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] + * _.merge(names, ages); + * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] } + * + * var food = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var otherFood = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(food, otherFood, function(a, b) { + * return _.isArray(a) ? a.concat(b) : undefined; + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] } */ - function merge(object, source, indicator) { + function merge(object) { var args = arguments, - index = 0, - length = 2, - stackA = args[3], - stackB = args[4]; - - if (indicator !== indicatorObject) { - stackA = []; - stackB = []; - - // work with `_.reduce` by only using its callback `accumulator` and `value` arguments - if (typeof indicator != 'number') { - length = args.length; - } + length = 2; + + if (!isObject(object)) { + return object; } + + // allows working with `_.reduce` and `_.reduceRight` without using + // their `index` and `collection` arguments + if (typeof args[2] != 'number') { + length = args.length; + } + if (length > 3 && typeof args[length - 2] == 'function') { + var callback = baseCreateCallback(args[--length - 1], args[length--], 2); + } else if (length > 2 && typeof args[length - 1] == 'function') { + callback = args[--length]; + } + var sources = slice(arguments, 1, length), + index = -1, + stackA = getArray(), + stackB = getArray(); + while (++index < length) { - forOwn(args[index], function(source, key) { - var found, isArr, value; - if (source && ((isArr = isArray(source)) || isPlainObject(source))) { - // avoid merging previously merged cyclic sources - var stackLength = stackA.length; - while (stackLength--) { - found = stackA[stackLength] == source; - if (found) { - break; - } - } - if (found) { - object[key] = stackB[stackLength]; - } - else { - // add `source` and associated `value` to the stack of traversed objects - stackA.push(source); - stackB.push(value = (value = object[key], isArr) - ? (isArray(value) ? value : []) - : (isPlainObject(value) ? value : {}) - ); - // recursively merge objects and arrays (susceptible to call stack limits) - object[key] = merge(value, source, indicatorObject, stackA, stackB); - } - } else if (source != null) { - object[key] = source; - } - }); + baseMerge(object, sources[index], callback, stackA, stackB); } + releaseArray(stackA); + releaseArray(stackB); return object; } /** * Creates a shallow clone of `object` excluding the specified properties. * Property names may be specified as individual arguments or as arrays of - * property names. If `callback` is passed, it will be executed for each property - * in the `object`, omitting the properties `callback` returns truthy for. The - * `callback` is bound to `thisArg` and invoked with three arguments; (value, key, object). + * property names. If a callback is provided it will be executed for each + * property of `object` omitting the properties the callback returns truey + * for. The callback is bound to `thisArg` and invoked with three arguments; + * (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. - * @param {Function|String} callback|[prop1, prop2, ...] The properties to omit - * or the function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Function|...string|string[]} [callback] The properties to omit or the + * function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns an object without the omitted properties. * @example * - * _.omit({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid'); - * // => { 'name': 'moe', 'age': 40 } + * _.omit({ 'name': 'fred', 'age': 40 }, 'age'); + * // => { 'name': 'fred' } * - * _.omit({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { - * return key.charAt(0) == '_'; + * _.omit({ 'name': 'fred', 'age': 40 }, function(value) { + * return typeof value == 'number'; * }); - * // => { 'name': 'moe' } + * // => { 'name': 'fred' } */ function omit(object, callback, thisArg) { - var isFunc = typeof callback == 'function', - result = {}; + var result = {}; + if (typeof callback != 'function') { + var props = []; + forIn(object, function(value, key) { + props.push(key); + }); + props = baseDifference(props, baseFlatten(arguments, true, false, 1)); + + var index = -1, + length = props.length; - if (isFunc) { - callback = createCallback(callback, thisArg); + while (++index < length) { + var key = props[index]; + result[key] = object[key]; + } } else { - var props = concat.apply(arrayRef, arguments); + callback = lodash.createCallback(callback, thisArg, 3); + forIn(object, function(value, key, object) { + if (!callback(value, key, object)) { + result[key] = value; + } + }); } - forIn(object, function(value, key, object) { - if (isFunc - ? !callback(value, key, object) - : indexOf(props, key, 1) < 0 - ) { - result[key] = value; - } - }); return result; } /** - * Creates a two dimensional array of the given object's key-value pairs, + * Creates a two dimensional array of an object's key-value pairs, * i.e. `[[key1, value1], [key2, value2]]`. * * @static @@ -8742,48 +9903,55 @@ var JXON = new (function () { * @returns {Array} Returns new array of key-value pairs. * @example * - * _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 }); - * // => [['moe', 30], ['larry', 40], ['curly', 50]] (order is not guaranteed) + * _.pairs({ 'barney': 36, 'fred': 40 }); + * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments) */ function pairs(object) { - var result = []; - forOwn(object, function(value, key) { - result.push([key, value]); - }); + var index = -1, + props = keys(object), + length = props.length, + result = Array(length); + + while (++index < length) { + var key = props[index]; + result[index] = [key, object[key]]; + } return result; } /** * Creates a shallow clone of `object` composed of the specified properties. * Property names may be specified as individual arguments or as arrays of - * property names. If `callback` is passed, it will be executed for each property - * in the `object`, picking the properties `callback` returns truthy for. The - * `callback` is bound to `thisArg` and invoked with three arguments; (value, key, object). + * property names. If a callback is provided it will be executed for each + * property of `object` picking the properties the callback returns truey + * for. The callback is bound to `thisArg` and invoked with three arguments; + * (value, key, object). * * @static * @memberOf _ * @category Objects * @param {Object} object The source object. - * @param {Function|String} callback|[prop1, prop2, ...] The properties to pick - * or the function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Function|...string|string[]} [callback] The function called per + * iteration or property names to pick, specified as individual property + * names or arrays of property names. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns an object composed of the picked properties. * @example * - * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); - * // => { 'name': 'moe', 'age': 40 } + * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name'); + * // => { 'name': 'fred' } * - * _.pick({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { + * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) { * return key.charAt(0) != '_'; * }); - * // => { 'name': 'moe' } + * // => { 'name': 'fred' } */ function pick(object, callback, thisArg) { var result = {}; if (typeof callback != 'function') { - var index = 0, - props = concat.apply(arrayRef, arguments), - length = props.length; + var index = -1, + props = baseFlatten(arguments, true, false, 1), + length = isObject(object) ? props.length : 0; while (++index < length) { var key = props[index]; @@ -8792,7 +9960,7 @@ var JXON = new (function () { } } } else { - callback = createCallback(callback, thisArg); + callback = lodash.createCallback(callback, thisArg, 3); forIn(object, function(value, key, object) { if (callback(value, key, object)) { result[key] = value; @@ -8809,35 +9977,39 @@ var JXON = new (function () { * @memberOf _ * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property values. + * @returns {Array} Returns an array of property values. * @example * * _.values({ 'one': 1, 'two': 2, 'three': 3 }); - * // => [1, 2, 3] + * // => [1, 2, 3] (property order is not guaranteed across environments) */ function values(object) { - var result = []; - forOwn(object, function(value) { - result.push(value); - }); + var index = -1, + props = keys(object), + length = props.length, + result = Array(length); + + while (++index < length) { + result[index] = object[props[index]]; + } return result; } /*--------------------------------------------------------------------------*/ /** - * Checks if a given `target` element is present in a `collection` using strict - * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used - * as the offset from the end of the collection. + * Checks if a given value is present in a collection using strict equality + * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the + * offset from the end of the collection. * * @static * @memberOf _ * @alias include * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Mixed} target The value to check for. - * @param {Number} [fromIndex=0] The index to search from. - * @returns {Boolean} Returns `true` if the `target` element is found, else `false`. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {*} target The value to check for. + * @param {number} [fromIndex=0] The index to search from. + * @returns {boolean} Returns `true` if the `target` element is found, else `false`. * @example * * _.contains([1, 2, 3], 1); @@ -8846,25 +10018,25 @@ var JXON = new (function () { * _.contains([1, 2, 3], 1, 2); * // => false * - * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * _.contains({ 'name': 'fred', 'age': 40 }, 'fred'); * // => true * - * _.contains('curly', 'ur'); + * _.contains('pebbles', 'eb'); * // => true */ function contains(collection, target, fromIndex) { var index = -1, + indexOf = getIndexOf(), length = collection ? collection.length : 0, result = false; fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0; - if (typeof length == 'number') { - result = (isString(collection) - ? collection.indexOf(target, fromIndex) - : indexOf(collection, target, fromIndex) - ) > -1; + if (isArray(collection)) { + result = indexOf(collection, target, fromIndex) > -1; + } else if (typeof length == 'number') { + result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1; } else { - each(collection, function(value) { + baseEach(collection, function(value) { if (++index >= fromIndex) { return !(result = value === target); } @@ -8874,64 +10046,49 @@ var JXON = new (function () { } /** - * Creates an object composed of keys returned from running each element of - * `collection` through a `callback`. The corresponding value of each key is - * the number of times the key was returned by `callback`. The `callback` is - * bound to `thisArg` and invoked with three arguments; (value, index|key, collection). - * The `callback` argument may also be the name of a property to count by (e.g. 'length'). - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function|String} callback|property The function called per iteration - * or property name to count by. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); - * // => { '4': 1, '6': 2 } + * Checks if the given callback returns truey value for **all** elements of + * a collection. The callback is bound to `thisArg` and invoked with three + * arguments; (value, index|key, collection). * - * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); - * // => { '4': 1, '6': 2 } + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. * - * _.countBy(['one', 'two', 'three'], 'length'); - * // => { '3': 2, '5': 1 } - */ - function countBy(collection, callback, thisArg) { - var result = {}; - callback = createCallback(callback, thisArg); - - forEach(collection, function(value, key, collection) { - key = callback(value, key, collection); - (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); - }); - return result; - } - - /** - * Checks if the `callback` returns a truthy value for **all** elements of a - * `collection`. The `callback` is bound to `thisArg` and invoked with three - * arguments; (value, index|key, collection). + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @alias all * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Boolean} Returns `true` if all elements pass the callback check, + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {boolean} Returns `true` if all elements passed the callback check, * else `false`. * @example * - * _.every([true, 1, null, 'yes'], Boolean); + * _.every([true, 1, null, 'yes']); + * // => false + * + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } + * ]; + * + * // using "_.pluck" callback shorthand + * _.every(characters, 'age'); + * // => true + * + * // using "_.where" callback shorthand + * _.every(characters, { 'age': 36 }); * // => false */ function every(collection, callback, thisArg) { var result = true; - callback = createCallback(callback, thisArg); + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, @@ -8943,7 +10100,7 @@ var JXON = new (function () { } } } else { - each(collection, function(value, index, collection) { + baseEach(collection, function(value, index, collection) { return (result = !!callback(value, index, collection)); }); } @@ -8951,26 +10108,48 @@ var JXON = new (function () { } /** - * Examines each element in a `collection`, returning an array of all elements - * the `callback` returns truthy for. The `callback` is bound to `thisArg` and + * Iterates over elements of a collection, returning an array of all elements + * the callback returns truey for. The callback is bound to `thisArg` and * invoked with three arguments; (value, index|key, collection). * + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. + * * @static * @memberOf _ * @alias select * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new array of elements that passed the callback check. * @example * * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [2, 4, 6] + * + * var characters = [ + * { 'name': 'barney', 'age': 36, 'blocked': false }, + * { 'name': 'fred', 'age': 40, 'blocked': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.filter(characters, 'blocked'); + * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] + * + * // using "_.where" callback shorthand + * _.filter(characters, { 'age': 36 }); + * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] */ function filter(collection, callback, thisArg) { var result = []; - callback = createCallback(callback, thisArg); + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, @@ -8983,7 +10162,7 @@ var JXON = new (function () { } } } else { - each(collection, function(value, index, collection) { + baseEach(collection, function(value, index, collection) { if (callback(value, index, collection)) { result.push(value); } @@ -8993,59 +10172,98 @@ var JXON = new (function () { } /** - * Examines each element in a `collection`, returning the first one the `callback` - * returns truthy for. The function returns as soon as it finds an acceptable - * element, and does not iterate over the entire `collection`. The `callback` is - * bound to `thisArg` and invoked with three arguments; (value, index|key, collection). + * Iterates over elements of a collection, returning the first element that + * the callback returns truey for. The callback is bound to `thisArg` and + * invoked with three arguments; (value, index|key, collection). * - * @static - * @memberOf _ - * @alias detect - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the element that passed the callback check, - * else `undefined`. - * @example + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. * - * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => 2 - */ - function find(collection, callback, thisArg) { - var result; - callback = createCallback(callback, thisArg); - - forEach(collection, function(value, index, collection) { - if (callback(value, index, collection)) { - result = value; - return false; - } - }); - return result; - } - - /** - * Iterates over a `collection`, executing the `callback` for each element in - * the `collection`. The `callback` is bound to `thisArg` and invoked with three - * arguments; (value, index|key, collection). Callbacks may exit iteration early - * by explicitly returning `false`. + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ - * @alias each + * @alias detect, findWhere * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Array|Object|String} Returns `collection`. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the found element, else `undefined`. * @example * - * _([1, 2, 3]).forEach(alert).join(','); - * // => alerts each number and returns '1,2,3' + * var characters = [ + * { 'name': 'barney', 'age': 36, 'blocked': false }, + * { 'name': 'fred', 'age': 40, 'blocked': true }, + * { 'name': 'pebbles', 'age': 1, 'blocked': false } + * ]; + * + * _.find(characters, function(chr) { + * return chr.age < 40; + * }); + * // => { 'name': 'barney', 'age': 36, 'blocked': false } + * + * // using "_.where" callback shorthand + * _.find(characters, { 'age': 1 }); + * // => { 'name': 'pebbles', 'age': 1, 'blocked': false } * - * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); - * // => alerts each number value (order is not guaranteed) + * // using "_.pluck" callback shorthand + * _.find(characters, 'blocked'); + * // => { 'name': 'fred', 'age': 40, 'blocked': true } + */ + function find(collection, callback, thisArg) { + callback = lodash.createCallback(callback, thisArg, 3); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + var value = collection[index]; + if (callback(value, index, collection)) { + return value; + } + } + } else { + var result; + baseEach(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result = value; + return false; + } + }); + return result; + } + } + + /** + * Iterates over elements of a collection, executing the callback for each + * element. The callback is bound to `thisArg` and invoked with three arguments; + * (value, index|key, collection). Callbacks may exit iteration early by + * explicitly returning `false`. + * + * Note: As with other "Collections" methods, objects with a `length` property + * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` + * may be used for object iteration. + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); + * // => logs each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); + * // => logs each number and returns the object (property order is not guaranteed across environments) */ function forEach(collection, callback, thisArg) { if (callback && typeof thisArg == 'undefined' && isArray(collection)) { @@ -9058,25 +10276,33 @@ var JXON = new (function () { } } } else { - each(collection, callback, thisArg); + baseEach(collection, callback, thisArg); } return collection; } /** - * Creates an object composed of keys returned from running each element of - * `collection` through a `callback`. The corresponding value of each key is an - * array of elements passed to `callback` that returned the key. The `callback` - * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection). - * The `callback` argument may also be the name of a property to group by (e.g. 'length'). + * Creates an object composed of keys generated from the results of running + * each element of a collection through the callback. The corresponding value + * of each key is an array of the elements responsible for generating the key. + * The callback is bound to `thisArg` and invoked with three arguments; + * (value, index|key, collection). + * + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false` * * @static * @memberOf _ * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function|String} callback|property The function called per iteration - * or property name to group by. - * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Object} Returns the composed aggregate object. * @example * @@ -9086,65 +10312,35 @@ var JXON = new (function () { * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); * // => { '4': [4.2], '6': [6.1, 6.4] } * + * // using "_.pluck" callback shorthand * _.groupBy(['one', 'two', 'three'], 'length'); * // => { '3': ['one', 'two'], '5': ['three'] } */ - function groupBy(collection, callback, thisArg) { - var result = {}; - callback = createCallback(callback, thisArg); - - forEach(collection, function(value, key, collection) { - key = callback(value, key, collection); - (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); - }); - return result; - } + var groupBy = createAggregator(function(result, value, key) { + (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); + }); /** - * Invokes the method named by `methodName` on each element in the `collection`, - * returning an array of the results of each invoked method. Additional arguments - * will be passed to each invoked method. If `methodName` is a function it will - * be invoked for, and `this` bound to, each element in the `collection`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function|String} methodName The name of the method to invoke or - * the function invoked per iteration. - * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. - * @returns {Array} Returns a new array of the results of each invoked method. - * @example + * Creates an array of values by running each element in the collection + * through the callback. The callback is bound to `thisArg` and invoked with + * three arguments; (value, index|key, collection). * - * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. * - * _.invoke([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ - function invoke(collection, methodName) { - var args = slice(arguments, 2), - isFunc = typeof methodName == 'function', - result = []; - - forEach(collection, function(value) { - result.push((isFunc ? methodName : value[methodName]).apply(value, args)); - }); - return result; - } - - /** - * Creates an array of values by running each element in the `collection` - * through a `callback`. The `callback` is bound to `thisArg` and invoked with - * three arguments; (value, index|key, collection). + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @alias collect * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new array of the results of each `callback` execution. * @example * @@ -9152,20 +10348,29 @@ var JXON = new (function () { * // => [3, 6, 9] * * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); - * // => [3, 6, 9] (order is not guaranteed) + * // => [3, 6, 9] (property order is not guaranteed across environments) + * + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } + * ]; + * + * // using "_.pluck" callback shorthand + * _.map(characters, 'name'); + * // => ['barney', 'fred'] */ function map(collection, callback, thisArg) { var index = -1, length = collection ? collection.length : 0, result = Array(typeof length == 'number' ? length : 0); - callback = createCallback(callback, thisArg); + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { while (++index < length) { result[index] = callback(collection[index], index, collection); } } else { - each(collection, function(value, key, collection) { + baseEach(collection, function(value, key, collection) { result[++index] = callback(value, key, collection); }); } @@ -9173,151 +10378,60 @@ var JXON = new (function () { } /** - * Retrieves the maximum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to - * `thisArg` and invoked with three arguments; (value, index, collection). - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the maximum value. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * _.max(stooges, function(stooge) { return stooge.age; }); - * // => { 'name': 'curly', 'age': 60 }; - */ - function max(collection, callback, thisArg) { - var computed = -Infinity, - index = -1, - length = collection ? collection.length : 0, - result = computed; - - if (callback || !isArray(collection)) { - callback = !callback && isString(collection) - ? charAtCallback - : createCallback(callback, thisArg); - - each(collection, function(value, index, collection) { - var current = callback(value, index, collection); - if (current > computed) { - computed = current; - result = value; - } - }); - } else { - while (++index < length) { - if (collection[index] > result) { - result = collection[index]; - } - } - } - return result; - } - - /** - * Retrieves the minimum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to `thisArg` - * and invoked with three arguments; (value, index, collection). - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the minimum value. - * @example - * - * _.min([10, 5, 100, 2, 1000]); - * // => 2 - */ - function min(collection, callback, thisArg) { - var computed = Infinity, - index = -1, - length = collection ? collection.length : 0, - result = computed; - - if (callback || !isArray(collection)) { - callback = !callback && isString(collection) - ? charAtCallback - : createCallback(callback, thisArg); - - each(collection, function(value, index, collection) { - var current = callback(value, index, collection); - if (current < computed) { - computed = current; - result = value; - } - }); - } else { - while (++index < length) { - if (collection[index] < result) { - result = collection[index]; - } - } - } - return result; - } - - /** - * Retrieves the value of a specified property from all elements in - * the `collection`. + * Retrieves the value of a specified property from all elements in the collection. * * @static * @memberOf _ + * @type Function * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {String} property The property to pluck. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {string} property The property to pluck. * @returns {Array} Returns a new array of property values. * @example * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } * ]; * - * _.pluck(stooges, 'name'); - * // => ['moe', 'larry', 'curly'] + * _.pluck(characters, 'name'); + * // => ['barney', 'fred'] */ - function pluck(collection, property) { - return map(collection, property + ''); - } + var pluck = map; /** - * Boils down a `collection` to a single value. The initial state of the - * reduction is `accumulator` and each successive step of it should be returned - * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 - * arguments; for arrays they are (accumulator, value, index|key, collection). + * Reduces a collection to a value which is the accumulated result of running + * each element in the collection through the callback, where each successive + * callback execution consumes the return value of the previous execution. If + * `accumulator` is not provided the first element of the collection will be + * used as the initial `accumulator` value. The callback is bound to `thisArg` + * and invoked with four arguments; (accumulator, value, index|key, collection). * * @static * @memberOf _ * @alias foldl, inject * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. + * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [accumulator] Initial value of the accumulator. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the accumulated value. + * @param {*} [accumulator] Initial value of the accumulator. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the accumulated value. * @example * - * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * var sum = _.reduce([1, 2, 3], function(sum, num) { + * return sum + num; + * }); * // => 6 + * + * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { + * result[key] = num * 3; + * return result; + * }, {}); + * // => { 'a': 3, 'b': 6, 'c': 9 } */ function reduce(collection, callback, accumulator, thisArg) { var noaccum = arguments.length < 3; - callback = createCallback(callback, thisArg, indicatorObject); + callback = lodash.createCallback(callback, thisArg, 4); if (isArray(collection)) { var index = -1, @@ -9330,7 +10444,7 @@ var JXON = new (function () { accumulator = callback(accumulator, collection[index], index, collection); } } else { - each(collection, function(value, index, collection) { + baseEach(collection, function(value, index, collection) { accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, value, index, collection) @@ -9340,142 +10454,95 @@ var JXON = new (function () { } /** - * The right-associative version of `_.reduce`. + * The opposite of `_.filter` this method returns the elements of a + * collection that the callback does **not** return truey for. * - * @static - * @memberOf _ - * @alias foldr - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [accumulator] Initial value of the accumulator. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the accumulated value. - * @example + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. * - * var list = [[0, 1], [2, 3], [4, 5]]; - * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); - * // => [4, 5, 2, 3, 0, 1] - */ - function reduceRight(collection, callback, accumulator, thisArg) { - var iteratee = collection, - length = collection ? collection.length : 0, - noaccum = arguments.length < 3; - - if (typeof length != 'number') { - var props = keys(collection); - length = props.length; - } else if (noCharByIndex && isString(collection)) { - iteratee = collection.split(''); - } - callback = createCallback(callback, thisArg, indicatorObject); - forEach(collection, function(value, index, collection) { - index = props ? props[--length] : --length; - accumulator = noaccum - ? (noaccum = false, iteratee[index]) - : callback(accumulator, iteratee[index], index, collection); - }); - return accumulator; - } - - /** - * The opposite of `_.filter`, this method returns the values of a - * `collection` that `callback` does **not** return truthy for. + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of elements that did **not** pass the - * callback check. + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of elements that failed the callback check. * @example * * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); * // => [1, 3, 5] + * + * var characters = [ + * { 'name': 'barney', 'age': 36, 'blocked': false }, + * { 'name': 'fred', 'age': 40, 'blocked': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.reject(characters, 'blocked'); + * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] + * + * // using "_.where" callback shorthand + * _.reject(characters, { 'age': 36 }); + * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] */ function reject(collection, callback, thisArg) { - callback = createCallback(callback, thisArg); + callback = lodash.createCallback(callback, thisArg, 3); return filter(collection, function(value, index, collection) { return !callback(value, index, collection); }); } /** - * Creates an array of shuffled `array` values, using a version of the - * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to shuffle. - * @returns {Array} Returns a new shuffled collection. - * @example - * - * _.shuffle([1, 2, 3, 4, 5, 6]); - * // => [4, 1, 6, 3, 5, 2] - */ - function shuffle(collection) { - var index = -1, - result = Array(collection ? collection.length : 0); - - forEach(collection, function(value) { - var rand = floor(nativeRandom() * (++index + 1)); - result[index] = result[rand]; - result[rand] = value; - }); - return result; - } - - /** - * Gets the size of the `collection` by returning `collection.length` for arrays - * and array-like objects or the number of own enumerable properties for objects. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to inspect. - * @returns {Number} Returns `collection.length` or number of own enumerable properties. - * @example - * - * _.size([1, 2]); - * // => 2 + * Checks if the callback returns a truey value for **any** element of a + * collection. The function returns as soon as it finds a passing value and + * does not iterate over the entire collection. The callback is bound to + * `thisArg` and invoked with three arguments; (value, index|key, collection). * - * _.size({ 'one': 1, 'two': 2, 'three': 3 }); - * // => 3 + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. * - * _.size('curly'); - * // => 5 - */ - function size(collection) { - var length = collection ? collection.length : 0; - return typeof length == 'number' ? length : keys(collection).length; - } - - /** - * Checks if the `callback` returns a truthy value for **any** element of a - * `collection`. The function returns as soon as it finds passing value, and - * does not iterate over the entire `collection`. The `callback` is bound to - * `thisArg` and invoked with three arguments; (value, index|key, collection). + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @alias any * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Boolean} Returns `true` if any element passes the callback check, + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {boolean} Returns `true` if any element passed the callback check, * else `false`. * @example * * _.some([null, 0, 'yes', false], Boolean); * // => true + * + * var characters = [ + * { 'name': 'barney', 'age': 36, 'blocked': false }, + * { 'name': 'fred', 'age': 40, 'blocked': true } + * ]; + * + * // using "_.pluck" callback shorthand + * _.some(characters, 'blocked'); + * // => true + * + * // using "_.where" callback shorthand + * _.some(characters, { 'age': 1 }); + * // => false */ function some(collection, callback, thisArg) { var result; - callback = createCallback(callback, thisArg); + callback = lodash.createCallback(callback, thisArg, 3); if (isArray(collection)) { var index = -1, @@ -9487,127 +10554,24 @@ var JXON = new (function () { } } } else { - each(collection, function(value, index, collection) { + baseEach(collection, function(value, index, collection) { return !(result = callback(value, index, collection)); }); } return !!result; } - /** - * Creates an array, stable sorted in ascending order by the results of - * running each element of `collection` through a `callback`. The `callback` - * is bound to `thisArg` and invoked with three arguments; (value, index|key, collection). - * The `callback` argument may also be the name of a property to sort by (e.g. 'length'). - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function|String} callback|property The function called per iteration - * or property name to sort by. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of sorted elements. - * @example - * - * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); - * // => [3, 1, 2] - * - * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); - * // => [3, 1, 2] - * - * _.sortBy(['larry', 'brendan', 'moe'], 'length'); - * // => ['moe', 'larry', 'brendan'] - */ - function sortBy(collection, callback, thisArg) { - var result = []; - callback = createCallback(callback, thisArg); - - forEach(collection, function(value, index, collection) { - result.push({ - 'criteria': callback(value, index, collection), - 'index': index, - 'value': value - }); - }); - - var length = result.length; - result.sort(compareAscending); - while (length--) { - result[length] = result[length].value; - } - return result; - } - - /** - * Converts the `collection` to an array. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to convert. - * @returns {Array} Returns the new converted array. - * @example - * - * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); - * // => [2, 3, 4] - */ - function toArray(collection) { - var length = collection ? collection.length : 0; - if (typeof length == 'number') { - return noCharByIndex && isString(collection) - ? collection.split('') - : slice(collection); - } - return values(collection); - } - - /** - * Examines each element in a `collection`, returning an array of all elements - * that contain the given `properties`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Object} properties The object of property values to filter by. - * @returns {Array} Returns a new array of elements that contain the given `properties`. - * @example - * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * _.where(stooges, { 'age': 40 }); - * // => [{ 'name': 'moe', 'age': 40 }] - */ - function where(collection, properties) { - var props = keys(properties); - return filter(collection, function(object) { - var length = props.length; - while (length--) { - var result = object[props[length]] === properties[props[length]]; - if (!result) { - break; - } - } - return !!result; - }); - } - /*--------------------------------------------------------------------------*/ /** - * Creates an array with all falsey values of `array` removed. The values - * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are all falsey. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to compact. - * @returns {Array} Returns a new filtered array. + * @returns {Array} Returns a new array of filtered values. * @example * * _.compact([0, 1, false, 2, '', 3]); @@ -9628,74 +10592,117 @@ var JXON = new (function () { } /** - * Creates an array of `array` elements not present in the other arrays - * using strict equality for comparisons, i.e. `===`. + * Creates an array excluding all values of the provided arrays using strict + * equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to process. - * @param {Array} [array1, array2, ...] Arrays to check. - * @returns {Array} Returns a new array of `array` elements not present in the - * other arrays. + * @param {...Array} [values] The arrays of values to exclude. + * @returns {Array} Returns a new array of filtered values. * @example * * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); * // => [1, 3, 4] */ function difference(array) { - var index = -1, - length = array ? array.length : 0, - flattened = concat.apply(arrayRef, arguments), - contains = cachedContains(flattened, length), - result = []; - - while (++index < length) { - var value = array[index]; - if (!contains(value)) { - result.push(value); - } - } - return result; + return baseDifference(array, baseFlatten(arguments, true, true, 1)); } /** - * Gets the first element of the `array`. Pass `n` to return the first `n` - * elements of the `array`. + * Gets the first element or first `n` elements of an array. If a callback + * is provided elements at the beginning of the array are returned as long + * as the callback returns truey. The callback is bound to `thisArg` and + * invoked with three arguments; (value, index, array). + * + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @alias head, take * @category Arrays * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param- {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the first element, or an array of the first `n` - * elements, of `array`. + * @param {Function|Object|number|string} [callback] The function called + * per element or the number of elements to return. If a property name or + * object is provided it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the first element(s) of `array`. * @example * - * _.first([5, 4, 3, 2, 1]); - * // => 5 + * _.first([1, 2, 3]); + * // => 1 + * + * _.first([1, 2, 3], 2); + * // => [1, 2] + * + * _.first([1, 2, 3], function(num) { + * return num < 3; + * }); + * // => [1, 2] + * + * var characters = [ + * { 'name': 'barney', 'blocked': true, 'employer': 'slate' }, + * { 'name': 'fred', 'blocked': false, 'employer': 'slate' }, + * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } + * ]; + * + * // using "_.pluck" callback shorthand + * _.first(characters, 'blocked'); + * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }] + * + * // using "_.where" callback shorthand + * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name'); + * // => ['barney', 'fred'] */ - function first(array, n, guard) { - if (array) { - var length = array.length; - return (n == null || guard) - ? array[0] - : slice(array, 0, nativeMin(nativeMax(0, n), length)); + function first(array, callback, thisArg) { + var n = 0, + length = array ? array.length : 0; + + if (typeof callback != 'number' && callback != null) { + var index = -1; + callback = lodash.createCallback(callback, thisArg, 3); + while (++index < length && callback(array[index], index, array)) { + n++; + } + } else { + n = callback; + if (n == null || thisArg) { + return array ? array[0] : undefined; + } } + return slice(array, 0, nativeMin(nativeMax(0, n), length)); } /** - * Flattens a nested array (the nesting can be to any depth). If `shallow` is - * truthy, `array` will only be flattened a single level. + * Flattens a nested array (the nesting can be to any depth). If `isShallow` + * is truey, the array will only be flattened a single level. If a callback + * is provided each element of the array is passed through the callback before + * flattening. The callback is bound to `thisArg` and invoked with three + * arguments; (value, index, array). + * + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @category Arrays - * @param {Array} array The array to compact. - * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @param {Array} array The array to flatten. + * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a new flattened array. * @example * @@ -9704,38 +10711,42 @@ var JXON = new (function () { * * _.flatten([1, [2], [3, [[4]]]], true); * // => [1, 2, 3, [[4]]]; + * + * var characters = [ + * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] }, + * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } + * ]; + * + * // using "_.pluck" callback shorthand + * _.flatten(characters, 'pets'); + * // => ['hoppy', 'baby puss', 'dino'] */ - function flatten(array, shallow) { - var index = -1, - length = array ? array.length : 0, - result = []; - - while (++index < length) { - var value = array[index]; - - // recursively flatten arrays (susceptible to call stack limits) - if (isArray(value)) { - push.apply(result, shallow ? value : flatten(value)); - } else { - result.push(value); - } + function flatten(array, isShallow, callback, thisArg) { + // juggle arguments + if (typeof isShallow != 'boolean' && isShallow != null) { + thisArg = callback; + callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow; + isShallow = false; } - return result; + if (callback != null) { + array = map(array, callback, thisArg); + } + return baseFlatten(array, isShallow); } /** * Gets the index at which the first occurrence of `value` is found using - * strict equality for comparisons, i.e. `===`. If the `array` is already - * sorted, passing `true` for `fromIndex` will run a faster binary search. + * strict equality for comparisons, i.e. `===`. If the array is already sorted + * providing `true` for `fromIndex` will run a faster binary search. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to search. - * @param {Mixed} value The value to search for. - * @param {Boolean|Number} [fromIndex=0] The index to search from or `true` to - * perform a binary search on a sorted `array`. - * @returns {Number} Returns the index of the matched value or `-1`. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=0] The index to search from or `true` + * to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value or `-1`. * @example * * _.indexOf([1, 2, 3, 1, 2, 3], 2); @@ -9748,59 +10759,25 @@ var JXON = new (function () { * // => 2 */ function indexOf(array, value, fromIndex) { - var index = -1, - length = array ? array.length : 0; - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0) - 1; + var length = array ? array.length : 0; + fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0); } else if (fromIndex) { - index = sortedIndex(array, value); + var index = sortedIndex(array, value); return array[index] === value ? index : -1; } - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * Gets all but the last element of `array`. Pass `n` to exclude the last `n` - * elements from the result. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to query. - * @param {Number} [n=1] The number of elements to exclude. - * @param- {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the last element, or `n` elements, of `array`. - * @example - * - * _.initial([3, 2, 1]); - * // => [3, 2] - */ - function initial(array, n, guard) { - if (!array) { - return []; - } - var length = array.length; - n = n == null || guard ? 1 : n || 0; - return slice(array, 0, nativeMin(nativeMax(0, length - n), length)); + return baseIndexOf(array, value, fromIndex); } /** - * Computes the intersection of all the passed-in arrays using strict equality - * for comparisons, i.e. `===`. + * Creates an array of unique values present in all provided arrays using + * strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays - * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique elements that are present - * in **all** of the arrays. + * @param {...Array} [array] The arrays to inspect. + * @returns {Array} Returns an array of composite values. * @example * * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); @@ -9809,224 +10786,148 @@ var JXON = new (function () { function intersection(array) { var args = arguments, argsLength = args.length, - cache = { '0': {} }, + argsIndex = -1, + caches = getArray(), index = -1, + indexOf = getIndexOf(), length = array ? array.length : 0, - isLarge = length >= 100, result = [], - seen = result; + seen = getArray(); + while (++argsIndex < argsLength) { + var value = args[argsIndex]; + caches[argsIndex] = indexOf === baseIndexOf && + (value ? value.length : 0) >= largeArraySize && + createCache(argsIndex ? args[argsIndex] : seen); + } outer: while (++index < length) { - var value = array[index]; - if (isLarge) { - var key = value + ''; - var inited = hasOwnProperty.call(cache[0], key) - ? !(seen = cache[0][key]) - : (seen = cache[0][key] = []); - } - if (inited || indexOf(seen, value) < 0) { - if (isLarge) { - seen.push(value); - } - var argsIndex = argsLength; + var cache = caches[0]; + value = array[index]; + + if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) { + argsIndex = argsLength; + (cache || seen).push(value); while (--argsIndex) { - if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0, 100)))(value)) { + cache = caches[argsIndex]; + if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) { continue outer; } } result.push(value); } } - return result; - } - - /** - * Gets the last element of the `array`. Pass `n` to return the last `n` - * elements of the `array`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param- {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the last element, or an array of the last `n` - * elements, of `array`. - * @example - * - * _.last([3, 2, 1]); - * // => 1 - */ - function last(array, n, guard) { - if (array) { - var length = array.length; - return (n == null || guard) ? array[length - 1] : slice(array, nativeMax(0, length - n)); - } - } - - /** - * Gets the index at which the last occurrence of `value` is found using strict - * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used - * as the offset from the end of the collection. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to search. - * @param {Mixed} value The value to search for. - * @param {Number} [fromIndex=array.length-1] The index to search from. - * @returns {Number} Returns the index of the matched value or `-1`. - * @example - * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); - * // => 4 - * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); - * // => 1 - */ - function lastIndexOf(array, value, fromIndex) { - var index = array ? array.length : 0; - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1; - } - while (index--) { - if (array[index] === value) { - return index; + while (argsLength--) { + cache = caches[argsLength]; + if (cache) { + releaseObject(cache); } } - return -1; + releaseArray(caches); + releaseArray(seen); + return result; } /** - * Creates an object composed from arrays of `keys` and `values`. Pass either - * a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`, or - * two arrays, one of `keys` and one of corresponding `values`. + * Gets the last element or last `n` elements of an array. If a callback is + * provided elements at the end of the array are returned as long as the + * callback returns truey. The callback is bound to `thisArg` and invoked + * with three arguments; (value, index, array). * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} keys The array of keys. - * @param {Array} [values=[]] The array of values. - * @returns {Object} Returns an object composed of the given keys and - * corresponding values. - * @example + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. * - * _.object(['moe', 'larry', 'curly'], [30, 40, 50]); - * // => { 'moe': 30, 'larry': 40, 'curly': 50 } - */ - function object(keys, values) { - var index = -1, - length = keys ? keys.length : 0, - result = {}; - - while (++index < length) { - var key = keys[index]; - if (values) { - result[key] = values[index]; - } else { - result[key[0]] = key[1]; - } - } - return result; - } - - /** - * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to but not including `stop`. This method is a port of Python's - * `range()` function. See http://docs.python.org/library/functions.html#range. + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @category Arrays - * @param {Number} [start=0] The start of the range. - * @param {Number} end The end of the range. - * @param {Number} [step=1] The value to increment or descrement by. - * @returns {Array} Returns a new range array. + * @param {Array} array The array to query. + * @param {Function|Object|number|string} [callback] The function called + * per element or the number of elements to return. If a property name or + * object is provided it will be used to create a "_.pluck" or "_.where" + * style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the last element(s) of `array`. * @example * - * _.range(10); - * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * _.last([1, 2, 3]); + * // => 3 + * + * _.last([1, 2, 3], 2); + * // => [2, 3] * - * _.range(1, 11); - * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * _.last([1, 2, 3], function(num) { + * return num > 1; + * }); + * // => [2, 3] * - * _.range(0, 30, 5); - * // => [0, 5, 10, 15, 20, 25] + * var characters = [ + * { 'name': 'barney', 'blocked': false, 'employer': 'slate' }, + * { 'name': 'fred', 'blocked': true, 'employer': 'slate' }, + * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } + * ]; * - * _.range(0, -10, -1); - * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * // using "_.pluck" callback shorthand + * _.pluck(_.last(characters, 'blocked'), 'name'); + * // => ['fred', 'pebbles'] * - * _.range(0); - * // => [] + * // using "_.where" callback shorthand + * _.last(characters, { 'employer': 'na' }); + * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }] */ - function range(start, end, step) { - start = +start || 0; - step = +step || 1; - - if (end == null) { - end = start; - start = 0; - } - // use `Array(length)` so V8 will avoid the slower "dictionary" mode - // http://youtu.be/XAqIpGU8ZZk#t=17m25s - var index = -1, - length = nativeMax(0, ceil((end - start) / step)), - result = Array(length); + function last(array, callback, thisArg) { + var n = 0, + length = array ? array.length : 0; - while (++index < length) { - result[index] = start; - start += step; + if (typeof callback != 'number' && callback != null) { + var index = length; + callback = lodash.createCallback(callback, thisArg, 3); + while (index-- && callback(array[index], index, array)) { + n++; + } + } else { + n = callback; + if (n == null || thisArg) { + return array ? array[length - 1] : undefined; + } } - return result; + return slice(array, nativeMax(0, length - n)); } /** - * The opposite of `_.initial`, this method gets all but the first value of - * `array`. Pass `n` to exclude the first `n` values from the result. + * Uses a binary search to determine the smallest index at which a value + * should be inserted into a given sorted array in order to maintain the sort + * order of the array. If a callback is provided it will be executed for + * `value` and each element of `array` to compute their sort ranking. The + * callback is bound to `thisArg` and invoked with one argument; (value). * - * @static - * @memberOf _ - * @alias drop, tail - * @category Arrays - * @param {Array} array The array to query. - * @param {Number} [n=1] The number of elements to exclude. - * @param- {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the first element, or `n` elements, of `array`. - * @example + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. * - * _.rest([3, 2, 1]); - * // => [2, 1] - */ - function rest(array, n, guard) { - return slice(array, (n == null || guard) ? 1 : nativeMax(0, n)); - } - - /** - * Uses a binary search to determine the smallest index at which the `value` - * should be inserted into `array` in order to maintain the sort order of the - * sorted `array`. If `callback` is passed, it will be executed for `value` and - * each element in `array` to compute their sort ranking. The `callback` is - * bound to `thisArg` and invoked with one argument; (value). The `callback` - * argument may also be the name of a property to order by. + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Mixed} value The value to evaluate. - * @param {Function|String} [callback=identity|property] The function called - * per iteration or property name to order by. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Number} Returns the index at which the value should be inserted + * @param {Array} array The array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedIndex([20, 30, 50], 40); * // => 2 * + * // using "_.pluck" callback shorthand * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); * // => 2 * @@ -10049,12 +10950,12 @@ var JXON = new (function () { high = array ? array.length : low; // explicitly reference `identity` for better inlining in Firefox - callback = callback ? createCallback(callback, thisArg) : identity; + callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity; value = callback(value); while (low < high) { var mid = (low + high) >>> 1; - callback(array[mid]) < value + (callback(array[mid]) < value) ? low = mid + 1 : high = mid; } @@ -10062,39 +10963,48 @@ var JXON = new (function () { } /** - * Computes the union of the passed-in arrays using strict equality for - * comparisons, i.e. `===`. + * Creates an array of unique values, in order, of the provided arrays using + * strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays - * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique values, in order, that are - * present in one or more of the arrays. + * @param {...Array} [array] The arrays to inspect. + * @returns {Array} Returns an array of composite values. * @example * * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); * // => [1, 2, 3, 101, 10] */ - function union() { - return uniq(concat.apply(arrayRef, arguments)); + function union(array) { + return baseUniq(baseFlatten(arguments, true, true)); } /** - * Creates a duplicate-value-free version of the `array` using strict equality - * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` - * for `isSorted` will run a faster algorithm. If `callback` is passed, each - * element of `array` is passed through a callback` before uniqueness is computed. - * The `callback` is bound to `thisArg` and invoked with three arguments; (value, index, array). + * Creates a duplicate-value-free version of an array using strict equality + * for comparisons, i.e. `===`. If the array is sorted, providing + * `true` for `isSorted` will use a faster algorithm. If a callback is provided + * each element of `array` is passed through the callback before uniqueness + * is computed. The callback is bound to `thisArg` and invoked with three + * arguments; (value, index, array). + * + * If a property name is provided for `callback` the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is provided for `callback` the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. * * @static * @memberOf _ * @alias unique * @category Arrays * @param {Array} array The array to process. - * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. + * @param {Function|Object|string} [callback=identity] The function called + * per iteration. If a property name or object is provided it will be used + * to create a "_.pluck" or "_.where" style callback, respectively. + * @param {*} [thisArg] The `this` binding of `callback`. * @returns {Array} Returns a duplicate-value-free array. * @example * @@ -10104,157 +11014,61 @@ var JXON = new (function () { * _.uniq([1, 1, 2, 2, 3], true); * // => [1, 2, 3] * - * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); - * // => [1, 2, 3] + * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); }); + * // => ['A', 'b', 'C'] * - * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); - * // => [1, 2, 3] + * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2.5, 3] + * + * // using "_.pluck" callback shorthand + * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] */ function uniq(array, isSorted, callback, thisArg) { - var index = -1, - length = array ? array.length : 0, - result = [], - seen = result; - // juggle arguments - if (typeof isSorted == 'function') { + if (typeof isSorted != 'boolean' && isSorted != null) { thisArg = callback; - callback = isSorted; + callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted; isSorted = false; } - // init value cache for large arrays - var isLarge = !isSorted && length >= 75; - if (isLarge) { - var cache = {}; - } - if (callback) { - seen = []; - callback = createCallback(callback, thisArg); - } - while (++index < length) { - var value = array[index], - computed = callback ? callback(value, index, array) : value; - - if (isLarge) { - var key = computed + ''; - var inited = hasOwnProperty.call(cache, key) - ? !(seen = cache[key]) - : (seen = cache[key] = []); - } - if (isSorted - ? !index || seen[seen.length - 1] !== computed - : inited || indexOf(seen, computed) < 0 - ) { - if (callback || isLarge) { - seen.push(computed); - } - result.push(value); - } + if (callback != null) { + callback = lodash.createCallback(callback, thisArg, 3); } - return result; + return baseUniq(array, isSorted, callback); } /** - * Creates an array with all occurrences of the passed values removed using - * strict equality for comparisons, i.e. `===`. + * Creates an array excluding all provided values using strict equality for + * comparisons, i.e. `===`. * * @static * @memberOf _ * @category Arrays * @param {Array} array The array to filter. - * @param {Mixed} [value1, value2, ...] Values to remove. - * @returns {Array} Returns a new filtered array. + * @param {...*} [value] The values to exclude. + * @returns {Array} Returns a new array of filtered values. * @example * * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); * // => [2, 3, 4] */ function without(array) { - var index = -1, - length = array ? array.length : 0, - contains = cachedContains(arguments, 1, 20), - result = []; - - while (++index < length) { - var value = array[index]; - if (!contains(value)) { - result.push(value); - } - } - return result; - } - - /** - * Groups the elements of each array at their corresponding indexes. Useful for - * separate data sources that are coordinated through matching array indexes. - * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix - * in a similar fashion. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of grouped elements. - * @example - * - * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); - * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] - */ - function zip(array) { - var index = -1, - length = array ? max(pluck(arguments, 'length')) : 0, - result = Array(length); - - while (++index < length) { - result[index] = pluck(arguments, index); - } - return result; + return baseDifference(array, slice(arguments, 1)); } /*--------------------------------------------------------------------------*/ - /** - * Creates a function that is restricted to executing `func` only after it is - * called `n` times. The `func` is executed with the `this` binding of the - * created function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Number} n The number of times the function must be called before - * it is executed. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var renderNotes = _.after(notes.length, render); - * _.forEach(notes, function(note) { - * note.asyncSave({ 'success': renderNotes }); - * }); - * // `renderNotes` is run once, after all notes have saved - */ - function after(n, func) { - if (n < 1) { - return func(); - } - return function() { - if (--n < 1) { - return func.apply(this, arguments); - } - }; - } - /** * Creates a function that, when called, invokes `func` with the `this` * binding of `thisArg` and prepends any additional `bind` arguments to those - * passed to the bound function. + * provided to the bound function. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to bind. - * @param {Mixed} [thisArg] The `this` binding of `func`. - * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {...*} [arg] Arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * @@ -10262,407 +11076,289 @@ var JXON = new (function () { * return greeting + ' ' + this.name; * }; * - * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func = _.bind(func, { 'name': 'fred' }, 'hi'); * func(); - * // => 'hi moe' + * // => 'hi fred' */ function bind(func, thisArg) { - // use `Function#bind` if it exists and is fast - // (in V8 `Function#bind` is slower except when partially applied) - return isBindFast || (nativeBind && arguments.length > 2) - ? nativeBind.call.apply(nativeBind, arguments) - : createBound(func, thisArg, slice(arguments, 2)); - } - - /** - * Binds methods on `object` to `object`, overwriting the existing method. - * If no method names are provided, all the function properties of `object` - * will be bound. - * - * @static - * @memberOf _ - * @category Functions - * @param {Object} object The object to bind and assign the bound methods to. - * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. - * @returns {Object} Returns `object`. - * @example - * - * var buttonView = { - * 'label': 'lodash', - * 'onClick': function() { alert('clicked: ' + this.label); } - * }; - * - * _.bindAll(buttonView); - * jQuery('#lodash_button').on('click', buttonView.onClick); - * // => When the button is clicked, `this.label` will have the correct value - */ - function bindAll(object) { - var funcs = arguments, - index = funcs.length > 1 ? 0 : (funcs = functions(object), -1), - length = funcs.length; - - while (++index < length) { - var key = funcs[index]; - object[key] = bind(object[key], object); - } - return object; + return arguments.length > 2 + ? createWrapper(func, 17, slice(arguments, 2), null, thisArg) + : createWrapper(func, 1, null, null, thisArg); } /** - * Creates a function that, when called, invokes the method at `object[key]` - * and prepends any additional `bindKey` arguments to those passed to the bound - * function. This method differs from `_.bind` by allowing bound functions to - * reference methods that will be redefined or don't yet exist. - * See http://michaux.ca/articles/lazy-function-definition-pattern. + * Produces a callback bound to an optional `thisArg`. If `func` is a property + * name the created callback will return the property value for a given element. + * If `func` is an object the created callback will return `true` for elements + * that contain the equivalent object properties, otherwise it will return `false`. * * @static * @memberOf _ * @category Functions - * @param {Object} object The object the method belongs to. - * @param {String} key The key of the method. - * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. - * @returns {Function} Returns the new bound function. + * @param {*} [func=identity] The value to convert to a callback. + * @param {*} [thisArg] The `this` binding of the created callback. + * @param {number} [argCount] The number of arguments the callback accepts. + * @returns {Function} Returns a callback function. * @example * - * var object = { - * 'name': 'moe', - * 'greet': function(greeting) { - * return greeting + ' ' + this.name; - * } - * }; - * - * var func = _.bindKey(object, 'greet', 'hi'); - * func(); - * // => 'hi moe' + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } + * ]; * - * object.greet = function(greeting) { - * return greeting + ', ' + this.name + '!'; - * }; + * // wrap to create custom callback shorthands + * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) { + * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback); + * return !match ? func(callback, thisArg) : function(object) { + * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3]; + * }; + * }); * - * func(); - * // => 'hi, moe!' + * _.filter(characters, 'age__gt38'); + * // => [{ 'name': 'fred', 'age': 40 }] */ - function bindKey(object, key) { - return createBound(object, key, slice(arguments, 2)); - } + function createCallback(func, thisArg, argCount) { + var type = typeof func; + if (func == null || type == 'function') { + return baseCreateCallback(func, thisArg, argCount); + } + // handle "_.pluck" style callback shorthands + if (type != 'object') { + return function(object) { + return object[func]; + }; + } + var props = keys(func), + key = props[0], + a = func[key]; - /** - * Creates a function that is the composition of the passed functions, - * where each function consumes the return value of the function that follows. - * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. - * Each function is executed with the `this` binding of the composed function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} [func1, func2, ...] Functions to compose. - * @returns {Function} Returns the new composed function. - * @example - * - * var greet = function(name) { return 'hi: ' + name; }; - * var exclaim = function(statement) { return statement + '!'; }; - * var welcome = _.compose(exclaim, greet); - * welcome('moe'); - * // => 'hi: moe!' - */ - function compose() { - var funcs = arguments; - return function() { - var args = arguments, - length = funcs.length; + // handle "_.where" style callback shorthands + if (props.length == 1 && a === a && !isObject(a)) { + // fast path the common case of providing an object with a single + // property containing a primitive value + return function(object) { + var b = object[key]; + return a === b && (a !== 0 || (1 / a == 1 / b)); + }; + } + return function(object) { + var length = props.length, + result = false; while (length--) { - args = [funcs[length].apply(this, args)]; + if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) { + break; + } } - return args[0]; + return result; }; } /** * Creates a function that will delay the execution of `func` until after - * `wait` milliseconds have elapsed since the last time it was invoked. Pass - * `true` for `immediate` to cause debounce to invoke `func` on the leading, - * instead of the trailing, edge of the `wait` timeout. Subsequent calls to - * the debounced function will return the result of the last `func` call. + * `wait` milliseconds have elapsed since the last time it was invoked. + * Provide an options object to indicate that `func` should be invoked on + * the leading and/or trailing edge of the `wait` timeout. Subsequent calls + * to the debounced function will return the result of the last `func` call. + * + * Note: If `leading` and `trailing` options are `true` `func` will be called + * on the trailing edge of the timeout only if the the debounced function is + * invoked more than once during the `wait` timeout. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to debounce. - * @param {Number} wait The number of milliseconds to delay. - * @param {Boolean} immediate A flag to indicate execution is on the leading - * edge of the timeout. + * @param {number} wait The number of milliseconds to delay. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout. + * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called. + * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * - * var lazyLayout = _.debounce(calculateLayout, 300); + * // avoid costly calculations while the window size is in flux + * var lazyLayout = _.debounce(calculateLayout, 150); * jQuery(window).on('resize', lazyLayout); + * + * // execute `sendMail` when the click event is fired, debouncing subsequent calls + * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * }); + * + * // ensure `batchLog` is executed once after 1 second of debounced calls + * var source = new EventSource('/stream'); + * source.addEventListener('message', _.debounce(batchLog, 250, { + * 'maxWait': 1000 + * }, false); */ - function debounce(func, wait, immediate) { + function debounce(func, wait, options) { var args, + maxTimeoutId, result, + stamp, thisArg, - timeoutId; + timeoutId, + trailingCall, + lastCalled = 0, + maxWait = false, + trailing = true; + + if (!isFunction(func)) { + throw new TypeError; + } + wait = nativeMax(0, wait) || 0; + if (options === true) { + var leading = true; + trailing = false; + } else if (isObject(options)) { + leading = options.leading; + maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0); + trailing = 'trailing' in options ? options.trailing : trailing; + } + var delayed = function() { + var remaining = wait - (now() - stamp); + if (remaining <= 0) { + if (maxTimeoutId) { + clearTimeout(maxTimeoutId); + } + var isCalled = trailingCall; + maxTimeoutId = timeoutId = trailingCall = undefined; + if (isCalled) { + lastCalled = now(); + result = func.apply(thisArg, args); + if (!timeoutId && !maxTimeoutId) { + args = thisArg = null; + } + } + } else { + timeoutId = setTimeout(delayed, remaining); + } + }; - function delayed() { - timeoutId = null; - if (!immediate) { + var maxDelayed = function() { + if (timeoutId) { + clearTimeout(timeoutId); + } + maxTimeoutId = timeoutId = trailingCall = undefined; + if (trailing || (maxWait !== wait)) { + lastCalled = now(); result = func.apply(thisArg, args); + if (!timeoutId && !maxTimeoutId) { + args = thisArg = null; + } } - } + }; + return function() { - var isImmediate = immediate && !timeoutId; args = arguments; + stamp = now(); thisArg = this; + trailingCall = trailing && (timeoutId || !leading); - clearTimeout(timeoutId); - timeoutId = setTimeout(delayed, wait); + if (maxWait === false) { + var leadingCall = leading && !timeoutId; + } else { + if (!maxTimeoutId && !leading) { + lastCalled = stamp; + } + var remaining = maxWait - (stamp - lastCalled), + isCalled = remaining <= 0; - if (isImmediate) { + if (isCalled) { + if (maxTimeoutId) { + maxTimeoutId = clearTimeout(maxTimeoutId); + } + lastCalled = stamp; + result = func.apply(thisArg, args); + } + else if (!maxTimeoutId) { + maxTimeoutId = setTimeout(maxDelayed, remaining); + } + } + if (isCalled && timeoutId) { + timeoutId = clearTimeout(timeoutId); + } + else if (!timeoutId && wait !== maxWait) { + timeoutId = setTimeout(delayed, wait); + } + if (leadingCall) { + isCalled = true; result = func.apply(thisArg, args); } + if (isCalled && !timeoutId && !maxTimeoutId) { + args = thisArg = null; + } return result; }; } /** - * Executes the `func` function after `wait` milliseconds. Additional arguments - * will be passed to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to delay. - * @param {Number} wait The number of milliseconds to delay execution. - * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. - * @returns {Number} Returns the `setTimeout` timeout id. - * @example - * - * var log = _.bind(console.log, console); - * _.delay(log, 1000, 'logged later'); - * // => 'logged later' (Appears after one second.) - */ - function delay(func, wait) { - var args = slice(arguments, 2); - return setTimeout(function() { func.apply(undefined, args); }, wait); - } - - /** - * Defers executing the `func` function until the current call stack has cleared. - * Additional arguments will be passed to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to defer. - * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. - * @returns {Number} Returns the `setTimeout` timeout id. - * @example - * - * _.defer(function() { alert('deferred'); }); - * // returns from the function before `alert` is called - */ - function defer(func) { - var args = slice(arguments, 1); - return setTimeout(function() { func.apply(undefined, args); }, 1); - } - - /** - * Creates a function that memoizes the result of `func`. If `resolver` is - * passed, it will be used to determine the cache key for storing the result - * based on the arguments passed to the memoized function. By default, the first - * argument passed to the memoized function is used as the cache key. The `func` - * is executed with the `this` binding of the memoized function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] A function used to resolve the cache key. - * @returns {Function} Returns the new memoizing function. - * @example - * - * var fibonacci = _.memoize(function(n) { - * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); - * }); - */ - function memoize(func, resolver) { - var cache = {}; - return function() { - var key = resolver ? resolver.apply(this, arguments) : arguments[0]; - return hasOwnProperty.call(cache, key) - ? cache[key] - : (cache[key] = func.apply(this, arguments)); - }; - } - - /** - * Creates a function that is restricted to execute `func` once. Repeat calls to - * the function will return the value of the first call. The `func` is executed - * with the `this` binding of the created function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // Application is only created once. - */ - function once(func) { - var result, - ran = false; - - return function() { - if (ran) { - return result; - } - ran = true; - result = func.apply(this, arguments); - - // clear the `func` variable so the function may be garbage collected - func = null; - return result; - }; - } - - /** - * Creates a function that, when called, invokes `func` with any additional - * `partial` arguments prepended to those passed to the new function. This - * method is similar to `bind`, except it does **not** alter the `this` binding. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to partially apply arguments to. - * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * var greet = function(greeting, name) { return greeting + ': ' + name; }; - * var hi = _.partial(greet, 'hi'); - * hi('moe'); - * // => 'hi: moe' - */ - function partial(func) { - return createBound(func, slice(arguments, 1)); - } - - /** - * Creates a function that, when executed, will only call the `func` - * function at most once per every `wait` milliseconds. If the throttled - * function is invoked more than once during the `wait` timeout, `func` will - * also be called on the trailing edge of the timeout. Subsequent calls to the - * throttled function will return the result of the last `func` call. + * Creates a function that, when executed, will only call the `func` function + * at most once per every `wait` milliseconds. Provide an options object to + * indicate that `func` should be invoked on the leading and/or trailing edge + * of the `wait` timeout. Subsequent calls to the throttled function will + * return the result of the last `func` call. + * + * Note: If `leading` and `trailing` options are `true` `func` will be called + * on the trailing edge of the timeout only if the the throttled function is + * invoked more than once during the `wait` timeout. * * @static * @memberOf _ * @category Functions * @param {Function} func The function to throttle. - * @param {Number} wait The number of milliseconds to throttle executions to. + * @param {number} wait The number of milliseconds to throttle executions to. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * + * // avoid excessively updating the position while scrolling * var throttled = _.throttle(updatePosition, 100); * jQuery(window).on('scroll', throttled); + * + * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes + * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { + * 'trailing': false + * })); */ - function throttle(func, wait) { - var args, - result, - thisArg, - timeoutId, - lastCalled = 0; + function throttle(func, wait, options) { + var leading = true, + trailing = true; - function trailingCall() { - lastCalled = new Date; - timeoutId = null; - result = func.apply(thisArg, args); + if (!isFunction(func)) { + throw new TypeError; } - return function() { - var now = new Date, - remaining = wait - (now - lastCalled); - - args = arguments; - thisArg = this; - - if (remaining <= 0) { - clearTimeout(timeoutId); - timeoutId = null; - lastCalled = now; - result = func.apply(thisArg, args); - } - else if (!timeoutId) { - timeoutId = setTimeout(trailingCall, remaining); - } - return result; - }; - } + if (options === false) { + leading = false; + } else if (isObject(options)) { + leading = 'leading' in options ? options.leading : leading; + trailing = 'trailing' in options ? options.trailing : trailing; + } + debounceOptions.leading = leading; + debounceOptions.maxWait = wait; + debounceOptions.trailing = trailing; - /** - * Creates a function that passes `value` to the `wrapper` function as its - * first argument. Additional arguments passed to the function are appended - * to those passed to the `wrapper` function. The `wrapper` is executed with - * the `this` binding of the created function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Mixed} value The value to wrap. - * @param {Function} wrapper The wrapper function. - * @returns {Function} Returns the new function. - * @example - * - * var hello = function(name) { return 'hello ' + name; }; - * hello = _.wrap(hello, function(func) { - * return 'before, ' + func('moe') + ', after'; - * }); - * hello(); - * // => 'before, hello moe, after' - */ - function wrap(value, wrapper) { - return function() { - var args = [value]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; + return debounce(func, wait, debounceOptions); } /*--------------------------------------------------------------------------*/ /** - * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their - * corresponding HTML entities. + * This method returns the first argument provided to it. * * @static * @memberOf _ * @category Utilities - * @param {String} string The string to escape. - * @returns {String} Returns the escaped string. + * @param {*} value Any value. + * @returns {*} Returns `value`. * @example * - * _.escape('Moe, Larry & Curly'); - * // => 'Moe, Larry & Curly' - */ - function escape(string) { - return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); - } - - /** - * This function returns the first argument passed to it. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Mixed} value Any value. - * @returns {Mixed} Returns `value`. - * @example - * - * var moe = { 'name': 'moe' }; - * moe === _.identity(moe); + * var object = { 'name': 'fred' }; + * _.identity(object) === object; * // => true */ function identity(value) { @@ -10670,13 +11366,14 @@ var JXON = new (function () { } /** - * Adds functions properties of `object` to the `lodash` function and chainable - * wrapper. + * Adds function properties of a source object to the `lodash` function and + * chainable wrapper. * * @static * @memberOf _ * @category Utilities * @param {Object} object The object of function properties to add to `lodash`. + * @param {Object} object The object of function properties to add to `lodash`. * @example * * _.mixin({ @@ -10685,391 +11382,117 @@ var JXON = new (function () { * } * }); * - * _.capitalize('larry'); - * // => 'Larry' - * - * _('curly').capitalize(); - * // => 'Curly' - */ - function mixin(object) { - forEach(functions(object), function(methodName) { - var func = lodash[methodName] = object[methodName]; - - lodash.prototype[methodName] = function() { - var args = [this.__wrapped__]; - push.apply(args, arguments); - - var result = func.apply(lodash, args); - return new lodash(result); - }; - }); - } - - /** - * Reverts the '_' variable to its previous value and returns a reference to - * the `lodash` function. - * - * @static - * @memberOf _ - * @category Utilities - * @returns {Function} Returns the `lodash` function. - * @example - * - * var lodash = _.noConflict(); - */ - function noConflict() { - window._ = oldDash; - return this; - } - - /** - * Produces a random number between `min` and `max` (inclusive). If only one - * argument is passed, a number between `0` and the given number will be returned. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Number} [min=0] The minimum possible value. - * @param {Number} [max=1] The maximum possible value. - * @returns {Number} Returns a random number. - * @example - * - * _.random(0, 5); - * // => a number between 1 and 5 - * - * _.random(5); - * // => also a number between 1 and 5 - */ - function random(min, max) { - if (min == null && max == null) { - max = 1; - } - min = +min || 0; - if (max == null) { - max = min; - min = 0; - } - return min + floor(nativeRandom() * ((+max || 0) - min + 1)); - } - - /** - * Resolves the value of `property` on `object`. If `property` is a function - * it will be invoked and its result returned, else the property value is - * returned. If `object` is falsey, then `null` is returned. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Object} object The object to inspect. - * @param {String} property The property to get the value of. - * @returns {Mixed} Returns the resolved value. - * @example - * - * var object = { - * 'cheese': 'crumpets', - * 'stuff': function() { - * return 'nonsense'; - * } - * }; - * - * _.result(object, 'cheese'); - * // => 'crumpets' - * - * _.result(object, 'stuff'); - * // => 'nonsense' - */ - function result(object, property) { - // based on Backbone's private `getValue` function - // https://github.com/documentcloud/backbone/blob/0.9.2/backbone.js#L1419-1424 - var value = object ? object[property] : null; - return isFunction(value) ? object[property]() : value; - } - - /** - * A micro-templating method that handles arbitrary delimiters, preserves - * whitespace, and correctly escapes quotes within interpolated code. - * - * Note: In the development build `_.template` utilizes sourceURLs for easier - * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl - * - * Note: Lo-Dash may be used in Chrome extensions by either creating a `lodash csp` - * build and avoiding `_.template` use, or loading Lo-Dash in a sandboxed page. - * See http://developer.chrome.com/trunk/extensions/sandboxingEval.html - * - * @static - * @memberOf _ - * @category Utilities - * @param {String} text The template text. - * @param {Obect} data The data object used to populate the text. - * @param {Object} options The options object. - * escape - The "escape" delimiter regexp. - * evaluate - The "evaluate" delimiter regexp. - * interpolate - The "interpolate" delimiter regexp. - * sourceURL - The sourceURL of the template's compiled source. - * variable - The data object variable name. - * - * @returns {Function|String} Returns a compiled function when no `data` object - * is given, else it returns the interpolated text. - * @example - * - * // using a compiled template - * var compiled = _.template('hello <%= name %>'); - * compiled({ 'name': 'moe' }); - * // => 'hello moe' - * - * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; - * _.template(list, { 'people': ['moe', 'larry', 'curly'] }); - * // => '
  • moe
  • larry
  • curly
  • ' + * _.capitalize('fred'); + * // => 'Fred' * - * // using the "escape" delimiter to escape HTML in data property values - * _.template('<%- value %>', { 'value': '