X-Git-Url: https://git.openstreetmap.org./rails.git/blobdiff_plain/1779347849622ade2abf4926a2db867d69f72033..cdc4e3f8d1fd6071151327389c08db911de3bcf4:/vendor/assets/iD/iD.js diff --git a/vendor/assets/iD/iD.js b/vendor/assets/iD/iD.js index 71379443b..8f8f661ac 100644 --- a/vendor/assets/iD/iD.js +++ b/vendor/assets/iD/iD.js @@ -174,11 +174,13 @@ } })(this); -d3 = (function(){ - var d3 = {version: "3.1.5"}; // 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,32 +339,42 @@ 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_bisect = d3_bisector(d3_ascending); +d3.bisectLeft = d3_bisect.left; +d3.bisect = d3.bisectRight = d3_bisect.right; -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; +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; }; d3.permute = function(array, indexes) { - var permutes = [], - i = -1, - n = indexes.length; - while (++i < n) permutes[i] = array[indexes[i]]; + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; return permutes; }; +d3.pairs = function(array) { + var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = [p0 = p1, p1 = array[++i]]; + return pairs; +}; d3.zip = function() { if (!(n = arguments.length)) return []; @@ -353,8 +409,28 @@ d3.entries = function(map) { return entries; }; d3.merge = function(arrays) { - return Array.prototype.concat.apply([], arrays); + var n = arrays.length, + m, + i = -1, + j = 0, + merged, + array; + + while (++i < n) j += arrays[i].length; + merged = new Array(j); + + while (--n >= 0) { + array = arrays[n]; + m = array.length; + while (--m >= 0) { + merged[--j] = array[m]; + } + } + + return merged; }; +var abs = Math.abs; + d3.range = function(start, stop, step) { if (arguments.length < 3) { step = 1; @@ -365,7 +441,7 @@ d3.range = function(start, stop, step) { } if ((stop - start) / step === Infinity) throw new Error("infinite range"); var range = [], - k = d3_range_integerScale(Math.abs(step)), + k = d3_range_integerScale(abs(step)), i = -1, j; start *= k, stop *= k, step *= k; @@ -380,66 +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; - 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 = {}, @@ -538,43 +644,45 @@ d3.nest = function() { }; d3.set = function(array) { - var set = new d3_Set(); - if (array) for (var i = 0; i < array.length; i++) set.add(array[i]); + var set = new d3_Set; + if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); 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_document = document, - d3_window = window; +var d3_document = this.document; + +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) { var i = 1, n = arguments.length, method; @@ -591,6 +699,19 @@ 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.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; + } +} + +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() { var dispatch = new d3_dispatch, @@ -608,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 @@ -662,9 +783,13 @@ function d3_dispatch_event(dispatch) { d3.event = null; +function d3_eventPreventDefault() { + d3.event.preventDefault(); +} + function d3_eventCancel() { - d3.event.stopPropagation(); d3.event.preventDefault(); + d3.event.stopPropagation(); } function d3_eventSource() { @@ -673,15 +798,6 @@ function d3_eventSource() { return e; } -// Registers an event listener for the specified target that cancels the next -// event for the specified type, but only if it occurs immediately. This is -// useful to disambiguate dragging from clicking. -function d3_eventSuppress(target, type) { - function off() { target.on(type, null); } - target.on(type, function() { d3_eventCancel(); off(); }, true); - setTimeout(off, 0); // clear the handler if it doesn't fire -} - // Like d3.dispatch, but for custom events abstracting native UI events. These // events have a target component (such as a brush), a target element (such as // the svg:g element containing the brush) and the standard arguments `d` (the @@ -718,105 +834,50 @@ function d3_eventDispatch(target) { return dispatch; } - -d3.mouse = function(container) { - return d3_mousePoint(container, d3_eventSource()); -}; - -// https://bugs.webkit.org/show_bug.cgi?id=44083 -var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0; - -function d3_mousePoint(container, e) { - 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(d3_document.body).append("svg") - .style("position", "absolute") - .style("top", 0) - .style("left", 0); - 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; - } - point = point.matrixTransform(container.getScreenCTM().inverse()); - return [point.x, point.y]; - } - var rect = container.getBoundingClientRect(); - return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop]; +d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); }; -var d3_array = d3_arraySlice; // conversion for NodeLists - -function d3_arrayCopy(pseudoarray) { - var i = -1, n = pseudoarray.length, array = []; - while (++i < n) array.push(pseudoarray[i]); - return array; -} - -function d3_arraySlice(pseudoarray) { - return Array.prototype.slice.call(pseudoarray); -} - -try { - d3_array(d3_document.documentElement.childNodes)[0].nodeType; -} catch(e) { - d3_array = d3_arrayCopy; -} - -var d3_arraySubclass = [].__proto__? +var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; +var d3_subclass = {}.__proto__? // Until ECMAScript supports array subclassing, prototype injection works well. -function(array, prototype) { - array.__proto__ = prototype; +function(object, prototype) { + object.__proto__ = prototype; }: // And if your browser doesn't support __proto__, we'll use direct extension. -function(array, prototype) { - for (var property in prototype) array[property] = prototype[property]; -}; - -d3.touches = function(container, touches) { - if (arguments.length < 2) touches = d3_eventSource().touches; - return touches ? d3_array(touches).map(function(touch) { - var point = d3_mousePoint(container, touch); - point.identifier = touch.identifier; - return point; - }) : []; +function(object, prototype) { + for (var property in prototype) object[property] = prototype[property]; }; function d3_selection(groups) { - d3_arraySubclass(groups, d3_selectionPrototype); + d3_subclass(groups, d3_selectionPrototype); return groups; } var d3_select = function(s, n) { return n.querySelector(s); }, d3_selectAll = function(s, n) { return n.querySelectorAll(s); }, - d3_selectRoot = d3_document.documentElement, - d3_selectMatcher = d3_selectRoot.matchesSelector || d3_selectRoot.webkitMatchesSelector || d3_selectRoot.mozMatchesSelector || d3_selectRoot.msMatchesSelector || d3_selectRoot.oMatchesSelector, - 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; } -var d3_selectionPrototype = []; - d3.selection = function() { - return d3_selectionRoot; + return d3.select(d3_document.documentElement); }; -d3.selection.prototype = d3_selectionPrototype; +var d3_selectionPrototype = d3.selection.prototype = []; d3_selectionPrototype.select = function(selector) { @@ -826,14 +887,14 @@ d3_selectionPrototype.select = function(selector) { group, node; - if (typeof selector !== "function") selector = d3_selection_selector(selector); + selector = d3_selection_selector(selector); for (var j = -1, m = this.length; ++j < m;) { subgroups.push(subgroup = []); subgroup.parentNode = (group = this[j]).parentNode; for (var i = -1, n = group.length; ++i < n;) { if (node = group[i]) { - subgroup.push(subnode = selector.call(node, node.__data__, i)); + subgroup.push(subnode = selector.call(node, node.__data__, i, j)); if (subnode && "__data__" in node) subnode.__data__ = node.__data__; } else { subgroup.push(null); @@ -845,7 +906,7 @@ d3_selectionPrototype.select = function(selector) { }; function d3_selection_selector(selector) { - return function() { + return typeof selector === "function" ? selector : function() { return d3_select(selector, this); }; } @@ -855,12 +916,12 @@ d3_selectionPrototype.selectAll = function(selector) { subgroup, node; - if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + selector = d3_selection_selectorAll(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]) { - subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i))); + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); subgroup.parentNode = node; } } @@ -870,7 +931,7 @@ d3_selectionPrototype.selectAll = function(selector) { }; function d3_selection_selectorAll(selector) { - return function() { + return typeof selector === "function" ? selector : function() { return d3_selectAll(selector, this); }; } @@ -888,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} @@ -959,11 +1020,6 @@ function d3_selection_attr(name, value) { function d3_collapse(s) { return s.trim().replace(/\s+/g, " "); } -d3.requote = function(s) { - return s.replace(d3_requote_re, "\\$&"); -}; - -var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; d3_selectionPrototype.classed = function(name, value) { if (arguments.length < 2) { @@ -973,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; @@ -998,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() { @@ -1049,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). @@ -1134,8 +1197,8 @@ d3_selectionPrototype.text = function(value) { return arguments.length ? this.each(typeof value === "function" ? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null - ? function() { this.textContent = ""; } - : function() { this.textContent = value; }) + ? function() { if (this.textContent !== "") this.textContent = ""; } + : function() { if (this.textContent !== value) this.textContent = value; }) : this.node().textContent; }; @@ -1148,52 +1211,52 @@ d3_selectionPrototype.html = function(value) { : this.node().innerHTML; }; -// TODO append(node)? -// TODO append(function)? d3_selectionPrototype.append = function(name) { - name = d3.ns.qualify(name); - - function append() { - return this.appendChild(d3_document.createElementNS(this.namespaceURI, name)); - } - - function appendNS() { - return this.appendChild(d3_document.createElementNS(name.space, name.local)); - } - - return this.select(name.local ? appendNS : append); + name = d3_selection_creator(name); + return this.select(function() { + return this.appendChild(name.apply(this, arguments)); + }); }; -d3_selectionPrototype.insert = function(name, before) { - name = d3.ns.qualify(name); - - if (typeof before !== "function") before = d3_selection_selector(before); +function d3_selection_creator(name) { - function insert(d, i) { - return this.insertBefore( - d3_document.createElementNS(this.namespaceURI, name), - before.call(this, d, i)); + function create() { + var document = this.ownerDocument, + namespace = this.namespaceURI; + return namespace + ? document.createElementNS(namespace, name) + : document.createElement(name); } - function insertNS(d, i) { - return this.insertBefore( - d3_document.createElementNS(name.space, name.local), - before.call(this, d, i)); + function createNS() { + return this.ownerDocument.createElementNS(name.space, name.local); } - return this.select(name.local ? insertNS : insert); + return typeof name === "function" ? name + : (name = d3.ns.qualify(name)).local ? createNS + : create; +} + +d3_selectionPrototype.insert = function(name, before) { + name = d3_selection_creator(name); + before = d3_selection_selector(before); + return this.select(function() { + return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); + }); }; // TODO remove(selector)? // 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, @@ -1224,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]; } } @@ -1328,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); } } @@ -1362,69 +1421,194 @@ 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__); + return a && b ? comparator(a.__data__, b.__data__) : !a - !b; }; } -function d3_noop() {} -d3_selectionPrototype.on = function(type, listener, capture) { - var n = arguments.length; - if (n < 3) { +d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); +}; - // For on(object) or on(object, boolean), the object specifies the event - // types and listeners to add or remove. The optional boolean specifies - // whether the listener captures events. - if (typeof type !== "string") { - if (n < 2) listener = false; - for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); - return this; +function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); } - - // For on(string), return the listener for the first node. - if (n < 2) return (n = this.node()["__on" + type]) && n._; - - // For on(string, function), use the default capture. - capture = false; } + return groups; +} - // Otherwise, a type, listener and capture are specified, and handled as below. - return this.each(d3_selection_on(type, listener, capture)); +d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; }; -function d3_selection_on(type, listener, capture) { - var name = "__on" + type, - i = type.indexOf("."), - wrap = d3_selection_onListener; - - if (i > 0) type = type.substring(0, i); - var filter = d3_selection_onFilters.get(type); - if (filter) type = filter, wrap = d3_selection_onFilter; +d3_selectionPrototype.empty = function() { + return !this.node(); +}; - function onRemove() { - var l = this[name]; - if (l) { - this.removeEventListener(type, l, l.$); - delete this[name]; +d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; } } + return null; +}; - function onAdd() { - var l = wrap(listener, d3_array(arguments)); - if (typeof Raven !== 'undefined') l = Raven.wrap(l); - onRemove.call(this); - this.addEventListener(type, this[name] = l, l.$ = capture); - l._ = listener; - } +d3_selectionPrototype.size = function() { + var n = 0; + d3_selection_each(this, function() { ++n; }); + return n; +}; - function removeAll() { - var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), - match; - for (var name in this) { - if (match = name.match(re)) { - var l = this[name]; - this.removeEventListener(match[1], l, l.$); +function d3_selection_enter(selection) { + d3_subclass(selection, d3_selection_enterPrototype); + return selection; +} + +var d3_selection_enterPrototype = []; + +d3.selection.enter = d3_selection_enter; +d3.selection.enter.prototype = d3_selection_enterPrototype; + +d3_selection_enterPrototype.append = d3_selectionPrototype.append; +d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; +d3_selection_enterPrototype.node = d3_selectionPrototype.node; +d3_selection_enterPrototype.call = d3_selectionPrototype.call; +d3_selection_enterPrototype.size = d3_selectionPrototype.size; + + +d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], + subgroup, + subnode, + upgroup, + group, + node; + + for (var j = -1, m = this.length; ++j < m;) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n;) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + + return d3_selection(subgroups); +}; + +d3_selection_enterPrototype.insert = function(name, before) { + if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); + return d3_selectionPrototype.insert.call(this, name, before); +}; + +function d3_selection_enterInsertBefore(enter) { + var i0, j0; + return function(d, i, j) { + var group = enter[j].update, + n = group.length, + node; + if (j != j0) j0 = j, i0 = 0; + if (i >= i0) i0 = i + 1; + while (!(node = group[i0]) && ++i0 < n); + return node; + }; +} + +// TODO fast singleton implementation? +d3.select = function(node) { + 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; + 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]); +}; + +d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + + // For on(object) or on(object, boolean), the object specifies the event + // types and listeners to add or remove. The optional boolean specifies + // whether the listener captures events. + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + + // For on(string), return the listener for the first node. + if (n < 2) return (n = this.node()["__on" + type]) && n._; + + // For on(string, function), use the default capture. + capture = false; + } + + // Otherwise, a type, listener and capture are specified, and handled as below. + return this.each(d3_selection_on(type, listener, capture)); +}; + +function d3_selection_on(type, listener, capture) { + var name = "__on" + type, + i = type.indexOf("."), + wrap = d3_selection_onListener; + + if (i > 0) type = type.slice(0, i); + var filter = d3_selection_onFilters.get(type); + if (filter) type = filter, wrap = d3_selection_onFilter; + + function onRemove() { + var l = this[name]; + if (l) { + this.removeEventListener(type, l, l.$); + delete this[name]; + } + } + + function onAdd() { + var l = wrap(listener, d3_array(arguments)); + if (typeof Raven !== 'undefined') l = Raven.wrap(l); + onRemove.call(this); + this.addEventListener(type, this[name] = l, l.$ = capture); + l._ = listener; + } + + function removeAll() { + var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), + match; + for (var name in this) { + if (match = name.match(re)) { + var l = this[name]; + this.removeEventListener(match[1], l, l.$); delete this[name]; } } @@ -1440,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) { @@ -1467,161 +1653,286 @@ function d3_selection_onFilter(listener, argumentz) { }; } -d3_selectionPrototype.each = function(callback) { - return d3_selection_each(this, function(node, i, j) { - callback.call(node, node.__data__, i, j); - }); -}; +var d3_event_dragSelect, + d3_event_dragId = 0; -function d3_selection_each(groups, callback) { - for (var j = 0, m = groups.length; j < m; j++) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { - if (node = group[i]) callback(node, i, j); - } +function d3_event_dragSuppress(node) { + var name = ".dragsuppress-" + ++d3_event_dragId, + click = "click" + name, + 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"); } - return groups; + + 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); + if (d3_event_dragSelect) style[d3_event_dragSelect] = select; + if (suppressClick) { // suppress the next click, but only if it’s immediate + var off = function() { w.on(click, null); }; + w.on(click, function() { d3_eventCancel(); off(); }, true); + setTimeout(off, 0); + } + }; } -d3_selectionPrototype.call = function(callback) { - var args = d3_array(arguments); - callback.apply(args[0] = this, args); - return this; +d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); }; -d3_selectionPrototype.empty = function() { - return !this.node(); -}; +// https://bugs.webkit.org/show_bug.cgi?id=44083 +var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; -d3_selectionPrototype.node = function() { - for (var j = 0, m = this.length; j < m; j++) { - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - var node = group[i]; - if (node) return node; +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) { + 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; + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [point.x, point.y]; } - return null; + var rect = container.getBoundingClientRect(); + return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop]; }; -function d3_selection_enter(selection) { - d3_arraySubclass(selection, d3_selection_enterPrototype); - return selection; -} - -var d3_selection_enterPrototype = []; +d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; +}; +var ε = 1e-6, + ε2 = ε * ε, + π = Math.PI, + τ = 2 * π, + τε = τ - ε, + halfπ = π / 2, + d3_radians = π / 180, + d3_degrees = 180 / π; -d3.selection.enter = d3_selection_enter; -d3.selection.enter.prototype = d3_selection_enterPrototype; +function d3_sgn(x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; +} -d3_selection_enterPrototype.append = d3_selectionPrototype.append; -d3_selection_enterPrototype.insert = d3_selectionPrototype.insert; -d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; -d3_selection_enterPrototype.node = d3_selectionPrototype.node; +// 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); +} -d3_selection_enterPrototype.select = function(selector) { - var subgroups = [], - subgroup, - subnode, - upgroup, - group, - node; +function d3_asin(x) { + return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); +} - for (var j = -1, m = this.length; ++j < m;) { - upgroup = (group = this[j]).update; - subgroups.push(subgroup = []); - subgroup.parentNode = group.parentNode; - for (var i = -1, n = group.length; ++i < n;) { - if (node = group[i]) { - subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i)); - subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } +function d3_sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; +} - return d3_selection(subgroups); -}; +function d3_cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; +} -d3_selectionPrototype.transition = function() { - var id = d3_transitionInheritId || ++d3_transitionId, - subgroups = [], - subgroup, - node, - transition = Object.create(d3_transitionInherit); +function d3_tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); +} - transition.time = Date.now(); +function d3_haversin(x) { + return (x = Math.sin(x / 2)) * x; +} - 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); - } +var ρ = Math.SQRT2, + ρ2 = 2, + ρ4 = 4; + +// p0 = [ux0, uy0, w0] +// p1 = [ux1, uy1, w1] +d3.interpolateZoom = function(p0, p1) { + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], + ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; + + var dx = ux1 - ux0, + dy = uy1 - uy0, + d2 = dx * dx + dy * dy, + d1 = Math.sqrt(d2), + b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), + b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), + r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), + r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), + dr = r1 - r0, + S = (dr || Math.log(w1 / w0)) / ρ; + + function interpolate(t) { + var s = t * S; + if (dr) { + // General case. + var coshr0 = d3_cosh(r0), + u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); + return [ + ux0 + u * dx, + uy0 + u * dy, + w0 * coshr0 / d3_cosh(ρ * s + r0) + ]; + } + // Special case for u0 ~= u1. + return [ + ux0 + t * dx, + uy0 + t * dy, + w0 * Math.exp(ρ * s) + ]; } - return d3_transition(subgroups, id); -}; - -var d3_selectionRoot = d3_selection([[d3_document]]); - -d3_selectionRoot[0].parentNode = d3_selectRoot; - -// TODO fast singleton implementation! -// TODO select(function) -d3.select = function(selector) { - return typeof selector === "string" - ? d3_selectionRoot.select(selector) - : d3_selection([[selector]]); // assume node -}; + interpolate.duration = S * 1000; -// TODO selectAll(function) -d3.selectAll = function(selector) { - return typeof selector === "string" - ? d3_selectionRoot.selectAll(selector) - : d3_selection([d3_array(selector)]); // assume node[] + return interpolate; }; d3.behavior.zoom = function() { - var translate = [0, 0], + var view = {x: 0, y: 0, k: 1}, translate0, // translate when we started zooming (to avoid drift) - scale = 1, - scale0, // scale when we started touching + 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, - event = d3_eventDispatch(zoom, "zoom"), + duration = 250, + zooming = 0, + mousedown = "mousedown.zoom", + mousemove = "mousemove.zoom", + mouseup = "mouseup.zoom", + mousewheelTimer, + touchstart = "touchstart.zoom", + touchtime, // time of last touchstart (to detect double-tap) + event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, - y1, - touchtime; // time of last touchstart (to detect double-tap) - - function zoom() { - this.on("mousedown.zoom", mousedown) - .on("mousemove.zoom", mousemove) - .on(d3_behavior_zoomWheel + ".zoom", mousewheel) - .on("dblclick.zoom", dblclick) - .on("touchstart.zoom", touchstart) - .on("touchmove.zoom", touchmove) - .on("touchend.zoom", touchstart); + 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("dblclick.zoom", dblclicked) + .on(touchstart, touchstarted); + } + + zoom.event = function(g) { + g.each(function() { + 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(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(dispatch); + zoomed(dispatch); + zoomended(dispatch); + } + }); } - zoom.translate = function(x) { - if (!arguments.length) return translate; - translate = x.map(Number); + zoom.translate = function(_) { + if (!arguments.length) return [view.x, view.y]; + view = {x: +_[0], y: +_[1], k: view.k}; // copy-on-write rescale(); return zoom; }; - zoom.scale = function(x) { - if (!arguments.length) return scale; - scale = +x; + zoom.scale = function(_) { + if (!arguments.length) return view.k; + view = {x: view.x, y: view.y, k: +_}; // copy-on-write rescale(); return zoom; }; - zoom.scaleExtent = function(x) { + zoom.scaleExtent = function(_) { if (!arguments.length) return scaleExtent; - scaleExtent = x == null ? d3_behavior_zoomInfinity : x.map(Number); + scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]]; + return zoom; + }; + + zoom.center = function(_) { + if (!arguments.length) return center; + center = _ && [+_[0], +_[1]]; + return zoom; + }; + + zoom.size = function(_) { + if (!arguments.length) return size; + size = _ && [+_[0], +_[1]]; + return zoom; + }; + + zoom.duration = function(_) { + if (!arguments.length) return duration; + duration = +_; // TODO function based on interpolateZoom distance? return zoom; }; @@ -1629,8 +1940,7 @@ d3.behavior.zoom = function() { if (!arguments.length) return x1; x1 = z; x0 = z.copy(); - translate = [0, 0]; - scale = 1; + view = {x: 0, y: 0, k: 1}; // copy-on-write return zoom; }; @@ -1638,165 +1948,251 @@ d3.behavior.zoom = function() { if (!arguments.length) return y1; y1 = z; y0 = z.copy(); - translate = [0, 0]; - scale = 1; + view = {x: 0, y: 0, k: 1}; // copy-on-write return zoom; }; function location(p) { - return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale]; + return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k]; } function point(l) { - return [l[0] * scale + translate[0], l[1] * scale + translate[1]]; + return [l[0] * view.k + view.x, l[1] * view.k + view.y]; } function scaleTo(s) { - scale = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); } function translateTo(p, l) { l = point(l); - translate[0] += p[0] - l[0]; - translate[1] += p[1] - l[1]; + view.x += p[0] - l[0]; + 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 - translate[0]) / scale; }).map(x0.invert)); - if (y1) y1.domain(y0.range().map(function(y) { return (y - translate[1]) / scale; }).map(y0.invert)); + 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(dispatch) { + if (!zooming++) dispatch({type: "zoomstart"}); } - function dispatch(event) { + function zoomed(dispatch) { rescale(); - d3.event.preventDefault(); - event({type: "zoom", scale: scale, translate: translate}); + dispatch({type: "zoom", scale: view.k, translate: [view.x, view.y]}); + } + + function zoomended(dispatch) { + if (!--zooming) dispatch({type: "zoomend"}), center0 = null; } - function mousedown() { - var target = this, - event_ = event.of(target, arguments), - eventTarget = d3.event.target, - moved = 0, - w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup), - l = location(d3.mouse(target)); + function mousedowned() { + var that = this, + target = d3.event.target, + dispatch = event.of(that, arguments), + dragged = 0, + subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), + location0 = location(d3.mouse(that)), + dragRestore = d3_event_dragSuppress(that); - d3_window.focus(); - d3_eventCancel(); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); - function mousemove() { - if (d3.event.which === 0) { - mouseup(); - return; - } - moved = 1; - translateTo(d3.mouse(target), l); - dispatch(event_); + function moved() { + dragged = 1; + translateTo(d3.mouse(that), location0); + zoomed(dispatch); } - function mouseup() { - if (moved) d3_eventCancel(); - w.on("mousemove.zoom", null).on("mouseup.zoom", null); - if (moved && d3.event.target === eventTarget) d3_eventSuppress(w, "click.zoom"); + function ended() { + subject.on(mousemove, null).on(mouseup, null); + dragRestore(dragged && d3.event.target === target); + zoomended(dispatch); } } - function mousewheel() { - if (!translate0) translate0 = location(d3.mouse(this)); - scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale); - translateTo(d3.mouse(this), translate0); - dispatch(event.of(this, arguments)); - } + // These closures persist for as long as at least one touch is active. + function touchstarted() { + var that = this, + dispatch = event.of(that, arguments), + locations0 = {}, // touchstart locations + distance0 = 0, // distance² between initial touches + scale0, // scale when we started touching + zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, + touchmove = "touchmove" + zoomName, + touchend = "touchend" + zoomName, + targets = [], + subject = d3.select(that), + dragRestore = d3_event_dragSuppress(that); - function mousemove() { - translate0 = null; - } + started(); + zoomstarted(dispatch); - function dblclick() { - var p = d3.mouse(this), l = location(p), k = Math.log(scale) / Math.LN2; - scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); - translateTo(p, l); - dispatch(event.of(this, arguments)); - } + // 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(that); + scale0 = view.k; + touches.forEach(function(t) { + if (t.identifier in locations0) locations0[t.identifier] = location(t); + }); + return touches; + } + + // Temporarily override touchstart while gesture is active. + function started() { + + // 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); - function touchstart() { - var touches = d3.touches(this), - now = Date.now(); + // 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; + } - scale0 = scale; - translate0 = {}; - touches.forEach(function(t) { translate0[t.identifier] = location(t); }); - d3_eventCancel(); + var touches = relocate(), + now = Date.now(); - if (touches.length === 1) { - if (now - touchtime < 500) { // dbltap - var p = touches[0], l = location(touches[0]); - scaleTo(scale * 2); - translateTo(p, l); - dispatch(event.of(this, arguments)); + if (touches.length === 1) { + if (now - touchtime < 500) { // dbltap + var p = touches[0]; + zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); + d3_eventPreventDefault(); + } + touchtime = now; + } else if (touches.length > 1) { + var p = touches[0], q = touches[1], + dx = p[0] - q[0], dy = p[1] - q[1]; + distance0 = dx * dx + dy * dy; } - touchtime = now; } - } - function touchmove() { - var touches = d3.touches(this), - p0 = touches[0], - l0 = translate0[p0.identifier]; - if (p1 = touches[1]) { - var p1, l1 = translate0[p1.identifier]; - p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; - l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; - scaleTo(d3.event.scale * scale0); + function moved() { + 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]) { + if (l0) break; + p0 = p1, l0 = l1; + } + } + + if (l1) { + var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, + scale1 = distance0 && Math.sqrt(distance1 / distance0); + p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; + l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; + scaleTo(scale1 * scale0); + } + + touchtime = null; + translateTo(p0, l0); + zoomed(dispatch); + } + + function ended() { + // If there are any globally-active touches remaining, remove the ended + // touches from locations0. + if (d3.event.touches.length) { + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + delete locations0[changed[i].identifier]; + } + // If locations0 is not empty, then relocate and continue listening for + // touchmove and touchend. + for (var identifier in locations0) { + return void relocate(); // locations may have detached due to rotation + } + } + // Otherwise, remove touchmove and touchend listeners. + d3.selectAll(targets).on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); + dragRestore(); + zoomended(dispatch); } - translateTo(p0, l0); - touchtime = null; - dispatch(event.of(this, arguments)); + } + + function mousewheeled() { + var dispatch = event.of(this, arguments); + if (mousewheelTimer) clearTimeout(mousewheelTimer); + else d3_selection_interrupt.call(this), translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(dispatch); }, 50); + d3_eventPreventDefault(); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); + translateTo(center0, translate0); + zoomed(dispatch); + } + + function dblclicked() { + var p = d3.mouse(this), + k = Math.log(view.k) / Math.LN2; + + 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; -var d3_timer_id = 0, - d3_timer_byId = {}, - d3_timer_queue = null, +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_timeout, // is a timeout active? + d3_timer_active, // active timer object + 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) { - if (arguments.length < 3) { - if (arguments.length < 2) delay = 0; - else if (!isFinite(delay)) return; - then = Date.now(); - } - - // If the callback's already in the queue, update it. - var timer = d3_timer_byId[callback.id]; - if (timer && timer.callback === callback) { - timer.then = then; - timer.delay = delay; - } + var n = arguments.length; + if (n < 2) delay = 0; + if (n < 3) then = Date.now(); - // Otherwise, add the callback to the queue. - else d3_timer_byId[callback.id = ++d3_timer_id] = d3_timer_queue = { - callback: callback, - then: then, - delay: delay, - next: d3_timer_queue - }; + // Add the callback to the tail of the queue. + var time = then + delay, timer = {c: callback, t: time, f: false, n: null}; + if (d3_timer_queueTail) d3_timer_queueTail.n = timer; + else d3_timer_queueHead = timer; + d3_timer_queueTail = timer; // Start animatin'! if (!d3_timer_interval) { @@ -1807,17 +2203,8 @@ d3.timer = function(callback, delay, then) { }; function d3_timer_step() { - var elapsed, - now = Date.now(), - t1 = d3_timer_queue; - - while (t1) { - elapsed = now - t1.then; - if (elapsed >= t1.delay) t1.flush = t1.callback(elapsed); - t1 = t1.next; - } - - var delay = d3_timer_flush() - now; + var now = d3_timer_mark(), + delay = d3_timer_sweep() - now; if (delay > 24) { if (isFinite(delay)) { clearTimeout(d3_timer_timeout); @@ -1831,71 +2218,147 @@ function d3_timer_step() { } d3.timer.flush = function() { - var elapsed, - now = Date.now(), - t1 = d3_timer_queue; + d3_timer_mark(); + d3_timer_sweep(); +}; - while (t1) { - elapsed = now - t1.then; - if (!t1.delay) t1.flush = t1.callback(elapsed); - t1 = t1.next; +function d3_timer_mark() { + var now = Date.now(); + d3_timer_active = d3_timer_queueHead; + while (d3_timer_active) { + if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t); + d3_timer_active = d3_timer_active.n; } - - d3_timer_flush(); -}; + return now; +} // Flush after callbacks to avoid concurrent queue modification. -function d3_timer_flush() { - var t0 = null, - t1 = d3_timer_queue, - then = Infinity; +// Returns the time of the earliest active timer, post-sweep. +function d3_timer_sweep() { + var t0, + t1 = d3_timer_queueHead, + time = Infinity; while (t1) { - if (t1.flush) { - delete d3_timer_byId[t1.callback.id]; - t1 = t0 ? t0.next = t1.next : d3_timer_queue = t1.next; + if (t1.f) { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; } else { - then = Math.min(then, t1.then + t1.delay); - t1 = (t0 = t1).next; + if (t1.t < time) time = t1.t; + t1 = (t0 = t1).n; } } - return then; + d3_timer_queueTail = t0; + return time; } +d3.geo = {}; -var d3_timer_frame = d3_window.requestAnimationFrame - || d3_window.webkitRequestAnimationFrame - || d3_window.mozRequestAnimationFrame - || d3_window.oRequestAnimationFrame - || d3_window.msRequestAnimationFrame - || function(callback) { setTimeout(callback, 17); }; -var π = Math.PI, - ε = 1e-6, - d3_radians = π / 180, - d3_degrees = 180 / π; +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_sgn(x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; +function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } } -function d3_acos(x) { - return Math.acos(Math.max(-1, Math.min(1, x))); -} +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); + } +}; -function d3_asin(x) { - return x > 1 ? π / 2 : x < -1 ? -π / 2 : Math.asin(x); -} +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_sinh(x) { - return (Math.exp(x) - Math.exp(-x)) / 2; +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_cosh(x) { - return (Math.exp(x) + Math.exp(-x)) / 2; +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_haversin(x) { - return (x = Math.sin(x / 2)) * x; +d3.geo.length = function(object) { + d3_geo_lengthSum = 0; + d3.geo.stream(object, d3_geo_length); + return d3_geo_lengthSum; +}; + +var d3_geo_lengthSum; + +var d3_geo_length = { + sphere: d3_noop, + point: d3_noop, + lineStart: d3_geo_lengthLineStart, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop +}; + +function d3_geo_lengthLineStart() { + var λ0, sinφ0, cosφ0; + + d3_geo_length.point = function(λ, φ) { + λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); + d3_geo_length.point = nextPoint; + }; + + d3_geo_length.lineEnd = function() { + d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; + }; + + function nextPoint(λ, φ) { + var sinφ = Math.sin(φ *= d3_radians), + cosφ = Math.cos(φ), + t = abs((λ *= d3_radians) - λ0), + cosΔλ = Math.cos(t); + d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; + } } -d3.geo = {}; function d3_identity(d) { return d; } @@ -1906,18 +2369,18 @@ function d3_true() { function d3_geo_spherical(cartesian) { return [ Math.atan2(cartesian[1], cartesian[0]), - Math.asin(Math.max(-1, Math.min(1, cartesian[2]))) + d3_asin(cartesian[2]) ]; } function d3_geo_sphericalEqual(a, b) { - return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε; + return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; } // General spherical polygon clipping algorithm: takes a polygon, cuts it into // visible line segments and rejoins the segments by interpolating along the // clip edge. -function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) { +function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { var subject = [], clip = []; @@ -1936,14 +2399,14 @@ function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) { return; } - var a = {point: p0, points: segment, other: null, visited: false, entry: true, subject: true}, - b = {point: p0, points: [p0], other: a, visited: false, entry: false, subject: false}; - a.other = b; + var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), + b = new d3_geo_clipPolygonIntersection(p0, null, a, false); + a.o = b; subject.push(a); clip.push(b); - a = {point: p1, points: [p1], other: null, visited: false, entry: false, subject: true}; - b = {point: p1, points: [p1], other: a, visited: false, entry: true, subject: false}; - a.other = b; + a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); + b = new d3_geo_clipPolygonIntersection(p1, null, a, true); + a.o = b; subject.push(a); clip.push(b); }); @@ -1952,41 +2415,42 @@ function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) { d3_geo_clipPolygonLinkCircular(clip); if (!subject.length) return; - if (inside) for (var i = 1, e = !inside(clip[0].point), n = clip.length; i < n; ++i) { - clip[i].entry = (e = !e); + for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { + clip[i].e = entry = !entry; } var start = subject[0], - current, points, point; while (1) { // Find first unvisited intersection. - current = start; - while (current.visited) if ((current = current.next) === start) return; - points = current.points; + var current = start, + isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; listener.lineStart(); do { - current.visited = current.other.visited = true; - if (current.entry) { - if (current.subject) { - for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]); + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); } else { - interpolate(current.point, current.next.point, 1, listener); + interpolate(current.x, current.n.x, 1, listener); } - current = current.next; + current = current.n; } else { - if (current.subject) { - points = current.prev.points; - for (var i = points.length; --i >= 0;) listener.point((point = points[i])[0], point[1]); + if (isSubject) { + points = current.p.z; + for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); } else { - interpolate(current.point, current.prev.point, -1, listener); + interpolate(current.x, current.p.x, -1, listener); } - current = current.prev; + current = current.p; } - current = current.other; - points = current.points; - } while (!current.visited); + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); listener.lineEnd(); } } @@ -1998,17 +2462,27 @@ function d3_geo_clipPolygonLinkCircular(array) { a = array[0], b; while (++i < n) { - a.next = b = array[i]; - b.prev = a; + a.n = b = array[i]; + b.p = a; a = b; } - a.next = b = array[0]; - b.prev = a; + a.n = b = array[0]; + b.p = a; } -function d3_geo_clip(pointVisible, clipLine, interpolate) { - return function(listener) { - var line = clipLine(listener); +function d3_geo_clipPolygonIntersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; // another intersection + this.e = entry; // is an entry? + this.v = false; // visited + this.n = this.p = null; // next & previous +} + +function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { + return function(rotate, listener) { + var line = clipLine(listener), + rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); var clip = { point: point, @@ -2018,10 +2492,8 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) { clip.point = pointRing; clip.lineStart = ringStart; clip.lineEnd = ringEnd; - invisible = false; - invisibleArea = visibleArea = 0; segments = []; - listener.polygonStart(); + polygon = []; }, polygonEnd: function() { clip.point = point; @@ -2029,15 +2501,18 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) { clip.lineEnd = lineEnd; segments = d3.merge(segments); + var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); if (segments.length) { - d3_geo_clipPolygon(segments, d3_geo_clipSort, null, interpolate, listener); - } else if (visibleArea < -ε || invisible && invisibleArea < -ε) { + 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(); - segments = null; + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; + segments = polygon = null; }, sphere: function() { listener.polygonStart(); @@ -2048,23 +2523,29 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) { } }; - function point(λ, φ) { if (pointVisible(λ, φ)) listener.point(λ, φ); } - function pointLine(λ, φ) { line.point(λ, φ); } + function point(λ, φ) { + var point = rotate(λ, φ); + if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); + } + function pointLine(λ, φ) { + var point = rotate(λ, φ); + line.point(point[0], point[1]); + } function lineStart() { clip.point = pointLine; line.lineStart(); } function lineEnd() { clip.point = point; line.lineEnd(); } - var segments, - visibleArea, - invisibleArea, - invisible; + var segments; var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), + polygonStarted = false, + polygon, ring; function pointRing(λ, φ) { - ringListener.point(λ, φ); ring.push([λ, φ]); + var point = rotate(λ, φ); + ringListener.point(point[0], point[1]); } function ringStart() { @@ -2081,26 +2562,24 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) { segment, n = ringSegments.length; - // TODO compute on-the-fly? - if (!n) { - invisible = true; - invisibleArea += d3_geo_clipAreaRing(ring, -1); - ring = null; - return; - } + ring.pop(); + polygon.push(ring); ring = null; + if (!n) return; + // No intersections. - // TODO compute on-the-fly? if (clean & 1) { segment = ringSegments[0]; - visibleArea += d3_geo_clipAreaRing(segment, 1); 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; } @@ -2138,67 +2617,18 @@ function d3_geo_clipBufferListener() { }; } -// Approximate polygon ring area (×2, since we only need the sign). -// For an invisible polygon ring, we rotate longitudinally by 180°. -// The invisible parameter should be 1, or -1 to rotate longitudinally. -// Based on Robert. G. Chamberlain and William H. Duquette, -// “Some Algorithms for Polygons on a Sphere”, -// http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 -function d3_geo_clipAreaRing(ring, invisible) { - if (!(n = ring.length)) return 0; - var n, - i = 0, - area = 0, - p = ring[0], - λ = p[0], - φ = p[1], - cosφ = Math.cos(φ), - x0 = Math.atan2(invisible * Math.sin(λ) * cosφ, Math.sin(φ)), - y0 = 1 - invisible * Math.cos(λ) * cosφ, - x1 = x0, - x, // λ'; λ rotated to south pole. - y; // φ' = 1 + sin(φ); φ rotated to south pole. - while (++i < n) { - p = ring[i]; - cosφ = Math.cos(φ = p[1]); - x = Math.atan2(invisible * Math.sin(λ = p[0]) * cosφ, Math.sin(φ)); - y = 1 - invisible * Math.cos(λ) * cosφ; - - // If both the current point and the previous point are at the north pole, - // skip this point. - if (Math.abs(y0 - 2) < ε && Math.abs(y - 2) < ε) continue; - - // If this or the previous point is at the south pole, or if this segment - // goes through the south pole, the area is 0. - if (Math.abs(y) < ε || Math.abs(y0) < ε) {} - - // If this segment goes through either pole… - else if (Math.abs(Math.abs(x - x0) - π) < ε) { - // For the north pole, compute lune area. - if (y + y0 > 2) area += 4 * (x - x0); - // For the south pole, the area is zero. - } - - // If the previous point is at the north pole, then compute lune area. - else if (Math.abs(y0 - 2) < ε) area += 4 * (x - x1); - - // Otherwise, the spherical triangle area is approximately - // δλ * (1 + sinφ0 + 1 + sinφ) / 2. - else area += ((3 * π + x - x0) % (2 * π) - π) * (y0 + y); - - x1 = x0, x0 = x, y0 = y; - } - return area; -} - // Intersection points are sorted along the clip edge. For both antimeridian // cutting and circle clipping, the same comparison is used. function d3_geo_clipSort(a, b) { - return ((a = a.point)[0] < 0 ? a[1] - π / 2 - ε : π / 2 - a[1]) - - ((b = b.point)[0] < 0 ? b[1] - π / 2 - ε : π / 2 - b[1]); + return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) + - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); } -var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate); +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. @@ -2218,19 +2648,19 @@ function d3_geo_clipAntimeridianLine(listener) { }, point: function(λ1, φ1) { var sλ1 = λ1 > 0 ? π : -π, - dλ = Math.abs(λ1 - λ0); - if (Math.abs(dλ - π) < ε) { // line crosses a pole - listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2); + 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); + listener.point(λ1, φ0); clean = 0; } else if (sλ0 !== sλ1 && dλ >= π) { // line crosses antimeridian // handle degeneracies - if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; - if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + 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(); @@ -2254,7 +2684,7 @@ function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); - return Math.abs(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)) @@ -2264,7 +2694,7 @@ function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { var φ; if (from == null) { - φ = direction * π / 2; + φ = direction * halfπ; listener.point(-π, φ); listener.point( 0, φ); listener.point( π, φ); @@ -2274,8 +2704,8 @@ function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { listener.point(-π, -φ); listener.point(-π, 0); listener.point(-π, φ); - } else if (Math.abs(from[0] - to[0]) > ε) { - var s = (from[0] < to[0] ? 1 : -1) * π; + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? π : -π; φ = direction * s / 2; listener.point(-s, φ); listener.point( 0, φ); @@ -2331,6 +2761,18 @@ function d3_geo_cartesianNormalize(d) { d[1] /= l; d[2] /= l; } +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_equirectangular(λ, φ) { return [λ, φ]; @@ -2356,17 +2798,23 @@ d3.geo.rotation = function(rotate) { return forward; }; +function d3_geo_identityRotation(λ, φ) { + return [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ]; +} + +d3_geo_identityRotation.invert = d3_geo_equirectangular; + // Note: |δλ| must be < 2π function d3_geo_rotation(δλ, δφ, δγ) { return δλ ? (δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ)) : (δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) - : d3_geo_equirectangular); + : d3_geo_identityRotation); } function d3_geo_forwardRotationλ(δλ) { return function(λ, φ) { - return λ += δλ, [λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ, φ]; + return λ += δλ, [λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ]; }; } @@ -2390,7 +2838,7 @@ function d3_geo_rotationφγ(δφ, δγ) { k = z * cosδφ + x * sinδφ; return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), - Math.asin(Math.max(-1, Math.min(1, k * cosδγ + y * sinδγ))) + d3_asin(k * cosδγ + y * sinδγ) ]; } @@ -2402,7 +2850,7 @@ function d3_geo_rotationφγ(δφ, δγ) { k = z * cosδγ - y * sinδγ; return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), - Math.asin(Math.max(-1, Math.min(1, k * cosδφ - x * sinδφ))) + d3_asin(k * cosδφ - x * sinδφ) ]; }; @@ -2457,16 +2905,16 @@ function d3_geo_circleInterpolate(radius, precision) { var cr = Math.cos(radius), sr = Math.sin(radius); return function(from, to, direction, listener) { + var step = direction * precision; if (from != null) { from = d3_geo_circleAngle(cr, from); to = d3_geo_circleAngle(cr, to); - if (direction > 0 ? from < to: from > to) from += direction * 2 * π; + if (direction > 0 ? from < to: from > to) from += direction * τ; } else { - from = radius + direction * 2 * π; - to = radius; + from = radius + direction * τ; + to = radius - .5 * step; } - var point; - for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) { + for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), @@ -2484,15 +2932,179 @@ 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.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) { var cr = Math.cos(radius), smallRadius = cr > 0, - notHemisphere = Math.abs(cr) > ε, // TODO optimise for this common case + notHemisphere = abs(cr) > ε, // TODO optimise for this common case interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); - return d3_geo_clip(visible, clipLine, interpolate); + return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-π, radius - π]); function visible(λ, φ) { return Math.cos(λ) * Math.cos(φ) > cr; @@ -2626,7 +3238,7 @@ function d3_geo_clipCircle(radius) { z; if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; var δλ = λ1 - λ0, - polar = Math.abs(δλ - π) < ε, + polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; @@ -2634,7 +3246,7 @@ function d3_geo_clipCircle(radius) { // Check that the first point is between a and b. if (meridian ? polar - ? φ0 + φ1 > 0 ^ q[1] < (Math.abs(q[0] - λ0) < ε ? φ0 : φ1) + ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); @@ -2656,12 +3268,99 @@ function d3_geo_clipCircle(radius) { } } -var d3_geo_clipViewMAX = 1e9; +// Liang–Barsky line clipping. +function d3_geom_clipLine(x0, y0, x1, y1) { + return function(line) { + var a = line.a, + b = line.b, + ax = a.x, + ay = a.y, + bx = b.x, + by = b.y, + t0 = 0, + t1 = 1, + dx = bx - ax, + dy = by - ay, + r; + + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy}; + if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy}; + return line; + }; +} + +var d3_geo_clipExtentMAX = 1e9; + +d3.geo.clipExtent = function() { + var x0, y0, x1, y1, + stream, + clip, + clipExtent = { + stream: function(output) { + if (stream) stream.valid = false; + stream = clip(output); + stream.valid = true; // allow caching by d3.geo.path + return stream; + }, + extent: function(_) { + if (!arguments.length) return [[x0, y0], [x1, y1]]; + clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); + if (stream) stream.valid = false, stream = null; + return clipExtent; + } + }; + return clipExtent.extent([[0, 0], [960, 500]]); +}; -function d3_geo_clipView(x0, y0, x1, y1) { +function d3_geo_clipExtent(x0, y0, x1, y1) { return function(listener) { var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), + clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; @@ -2674,40 +3373,42 @@ function d3_geo_clipView(x0, y0, x1, y1) { listener = bufferListener; segments = []; polygon = []; + clean = true; }, polygonEnd: function() { listener = listener_; - if ((segments = d3.merge(segments)).length) { + segments = d3.merge(segments); + var clipStartInside = insidePolygon([x0, y1]), + inside = clean && clipStartInside, + visible = segments.length; + if (inside || visible) { listener.polygonStart(); - d3_geo_clipPolygon(segments, compare, inside, interpolate, listener); + if (inside) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (visible) { + d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); + } listener.polygonEnd(); - } else if (insidePolygon([x0, y0])) { - listener.polygonStart(), listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(), listener.polygonEnd(); } segments = polygon = ring = null; } }; - function inside(point) { - var a = corner(point, -1), - i = insidePolygon([a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0]); - return i; - } - function insidePolygon(p) { var wn = 0, // the winding number counter n = polygon.length, y = p[1]; for (var i = 0; i < n; ++i) { - for (var j = 1, v = polygon[i], m = v.length, a = v[0]; j < m; ++j) { + 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; } @@ -2715,10 +3416,6 @@ function d3_geo_clipView(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 || @@ -2732,17 +3429,18 @@ function d3_geo_clipView(x0, y0, x1, y1) { } } - function visible(x, y) { + function pointVisible(x, y) { return x0 <= x && x <= x1 && y0 <= y && y <= y1; } function point(x, y) { - if (visible(x, y)) listener.point(x, y); + if (pointVisible(x, y)) listener.point(x, y); } var x__, y__, v__, // first point x_, y_, v_, // previous point - first; + first, + clean; function lineStart() { clip.point = linePoint; @@ -2766,9 +3464,9 @@ function d3_geo_clipView(x0, y0, x1, y1) { } function linePoint(x, y) { - x = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, x)); - y = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, y)); - var v = visible(x, y); + x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); + y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); + var v = pointVisible(x, y); if (polygon) ring.push([x, y]); if (first) { x__ = x, y__ = y, v__ = v; @@ -2780,18 +3478,19 @@ function d3_geo_clipView(x0, y0, x1, y1) { } else { if (v && v_) listener.point(x, y); else { - var a = [x_, y_], - b = [x, y]; - if (clipLine(a, b)) { + var l = {a: {x: x_, y: y_}, b: {x: x, y: y}}; + if (clipLine(l)) { if (!v_) { listener.lineStart(); - listener.point(a[0], a[1]); + listener.point(l.a.x, l.a.y); } - listener.point(b[0], b[1]); + listener.point(l.b.x, l.b.y); if (!v) listener.lineEnd(); + clean = false; } else if (v) { listener.lineStart(); listener.point(x, y); + clean = false; } } } @@ -2802,14 +3501,14 @@ function d3_geo_clipView(x0, y0, x1, y1) { }; function corner(p, direction) { - return Math.abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 - : Math.abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 - : Math.abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 - : direction > 0 ? 3 : 2; // Math.abs(p[1] - y1) < ε + return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 + : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 + : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 + : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε } function compare(a, b) { - return comparePoints(a.point, b.point); + return comparePoints(a.x, b.x); } function comparePoints(a, b) { @@ -2821,780 +3520,690 @@ function d3_geo_clipView(x0, y0, x1, y1) { : ca === 2 ? a[1] - b[1] : b[0] - a[0]; } +} - // Liang–Barsky line clipping. - function clipLine(a, b) { - var dx = b[0] - a[0], - dy = b[1] - a[1], - t = [0, 1]; +function d3_geo_conic(projectAt) { + var φ0 = 0, + φ1 = π / 3, + m = d3_geo_projectionMutator(projectAt), + p = m(φ0, φ1); - if (Math.abs(dx) < ε && Math.abs(dy) < ε) return x0 <= a[0] && a[0] <= x1 && y0 <= a[1] && a[1] <= y1; + p.parallels = function(_) { + if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180]; + return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); + }; - if (d3_geo_clipViewT(x0 - a[0], dx, t) && - d3_geo_clipViewT(a[0] - x1, -dx, t) && - d3_geo_clipViewT(y0 - a[1], dy, t) && - d3_geo_clipViewT(a[1] - y1, -dy, t)) { - if (t[1] < 1) { - b[0] = a[0] + t[1] * dx; - b[1] = a[1] + t[1] * dy; - } - if (t[0] > 0) { - a[0] += t[0] * dx; - a[1] += t[0] * dy; - } - return true; - } + return p; +} - return false; +function d3_geo_conicEqualArea(φ0, φ1) { + var sinφ0 = Math.sin(φ0), + n = (sinφ0 + Math.sin(φ1)) / 2, + C = 1 + sinφ0 * (2 * n - sinφ0), + ρ0 = Math.sqrt(C) / n; + + function forward(λ, φ) { + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ + ρ * Math.sin(λ *= n), + ρ0 - ρ * Math.cos(λ) + ]; } + + forward.invert = function(x, y) { + var ρ0_y = ρ0 - y; + return [ + Math.atan2(x, ρ0_y) / n, + d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) + ]; + }; + + return forward; } -function d3_geo_clipViewT(num, denominator, t) { - if (Math.abs(denominator) < ε) return num <= 0; +(d3.geo.conicEqualArea = function() { + return d3_geo_conic(d3_geo_conicEqualArea); +}).raw = d3_geo_conicEqualArea; - var u = num / denominator; +// ESRI:102003 +d3.geo.albers = function() { + return d3.geo.conicEqualArea() + .rotate([96, 0]) + .center([-.6, 38.7]) + .parallels([29.5, 45.5]) + .scale(1070); +}; - if (denominator > 0) { - if (u > t[1]) return false; - if (u > t[0]) t[0] = u; - } else { - if (u < t[0]) return false; - if (u < t[1]) t[1] = u; - } - return true; -} -function d3_geo_compose(a, b) { +// A composite projection for the United States, configured by default for +// 960×500. Also works quite well at 960×600 with scale 1285. The set of +// standard parallels for each region comes from USGS, which is published here: +// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers +d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); + // EPSG:3338 + var alaska = d3.geo.conicEqualArea() + .rotate([154, 0]) + .center([-2, 58.5]) + .parallels([55, 65]); + + // ESRI:102007 + var hawaii = d3.geo.conicEqualArea() + .rotate([157, 0]) + .center([-3, 19.9]) + .parallels([8, 18]); + + var point, + pointStream = {point: function(x, y) { point = [x, y]; }}, + lower48Point, + alaskaPoint, + hawaiiPoint; + + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + point = null; + (lower48Point(x, y), point) + || (alaskaPoint(x, y), point) + || hawaiiPoint(x, y); + return point; } - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); + albersUsa.invert = function(coordinates) { + var k = lower48.scale(), + t = lower48.translate(), + x = (coordinates[0] - t[0]) / k, + y = (coordinates[1] - t[1]) / k; + return (y >= .120 && y < .234 && x >= -.425 && x < -.214 ? alaska + : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii + : lower48).invert(coordinates); }; - return compose; -} + // A naïve multi-projection stream. + // The projections must have mutually exclusive clip regions on the sphere, + // as this will avoid emitting interleaving lines and polygons. + albersUsa.stream = function(stream) { + var lower48Stream = lower48.stream(stream), + alaskaStream = alaska.stream(stream), + hawaiiStream = hawaii.stream(stream); + return { + point: function(x, y) { + lower48Stream.point(x, y); + alaskaStream.point(x, y); + hawaiiStream.point(x, y); + }, + sphere: function() { + lower48Stream.sphere(); + alaskaStream.sphere(); + hawaiiStream.sphere(); + }, + lineStart: function() { + lower48Stream.lineStart(); + alaskaStream.lineStart(); + hawaiiStream.lineStart(); + }, + lineEnd: function() { + lower48Stream.lineEnd(); + alaskaStream.lineEnd(); + hawaiiStream.lineEnd(); + }, + polygonStart: function() { + lower48Stream.polygonStart(); + alaskaStream.polygonStart(); + hawaiiStream.polygonStart(); + }, + polygonEnd: function() { + lower48Stream.polygonEnd(); + alaskaStream.polygonEnd(); + hawaiiStream.polygonEnd(); + } + }; + }; -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); - } -}; + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_); + alaska.precision(_); + hawaii.precision(_); + return albersUsa; + }; -function d3_geo_streamGeometry(geometry, listener) { - if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { - d3_geo_streamGeometryType[geometry.type](geometry, listener); - } -} + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_); + alaska.scale(_ * .35); + hawaii.scale(_); + return albersUsa.translate(lower48.translate()); + }; -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); - } -}; + albersUsa.translate = function(_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), x = +_[0], y = +_[1]; -var d3_geo_streamGeometryType = { - Sphere: function(object, listener) { - listener.sphere(); - }, - Point: function(object, listener) { - var coordinate = object.coordinates; - listener.point(coordinate[0], coordinate[1]); - }, - MultiPoint: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length, coordinate; - while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); - }, - 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); - } -}; + lower48Point = lower48 + .translate(_) + .clipExtent([[x - .455 * k, y - .238 * k], [x + .455 * k, y + .238 * k]]) + .stream(pointStream).point; -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]); - listener.lineEnd(); -} + alaskaPoint = alaska + .translate([x - .307 * k, y + .201 * k]) + .clipExtent([[x - .425 * k + ε, y + .120 * k + ε], [x - .214 * k - ε, y + .234 * k - ε]]) + .stream(pointStream).point; -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(); -} + hawaiiPoint = hawaii + .translate([x - .205 * k, y + .212 * k]) + .clipExtent([[x - .214 * k + ε, y + .166 * k + ε], [x - .115 * k - ε, y + .234 * k - ε]]) + .stream(pointStream).point; -function d3_geo_resample(project) { - var δ2 = .5, // precision, px² - maxDepth = 16; + return albersUsa; + }; - function resample(stream) { - var λ0, x0, y0, a0, b0, c0; // previous point + return albersUsa.scale(1070); +}; - var resample = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { stream.polygonStart(); resample.lineStart = polygonLineStart; }, - polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; } - }; +d3.geo.bounds = (function() { + var λ0, φ0, λ1, φ1, // bounds + λ_, // previous λ-coordinate + λ__, φ__, // first point + p0, // previous 3D point + dλSum, + ranges, + range; - function point(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - } + var bound = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, - function lineStart() { - x0 = NaN; - resample.point = linePoint; - stream.lineStart(); + polygonStart: function() { + bound.point = ringPoint; + bound.lineStart = ringStart; + bound.lineEnd = ringEnd; + dλSum = 0; + d3_geo_area.polygonStart(); + }, + polygonEnd: function() { + d3_geo_area.polygonEnd(); + bound.point = point; + bound.lineStart = lineStart; + bound.lineEnd = lineEnd; + if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); + else if (dλSum > ε) φ1 = 90; + else if (dλSum < -ε) φ0 = -90; + range[0] = λ0, range[1] = λ1; } + }; - function linePoint(λ, φ) { - var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ); - resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); - stream.point(x0, y0); + function point(λ, φ) { + ranges.push(range = [λ0 = λ, λ1 = λ]); + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + + function linePoint(λ, φ) { + var p = d3_geo_cartesian([λ * d3_radians, φ * d3_radians]); + if (p0) { + var normal = d3_geo_cartesianCross(p0, p), + equatorial = [normal[1], -normal[0], 0], + inflection = d3_geo_cartesianCross(equatorial, normal); + d3_geo_cartesianNormalize(inflection); + inflection = d3_geo_spherical(inflection); + var dλ = λ - λ_, + s = dλ > 0 ? 1 : -1, + λi = inflection[0] * d3_degrees * s, + antimeridian = abs(dλ) > 180; + if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = inflection[1] * d3_degrees; + if (φi > φ1) φ1 = φi; + } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = -inflection[1] * d3_degrees; + if (φi < φ0) φ0 = φi; + } else { + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + if (antimeridian) { + if (λ < λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } else { + if (λ1 >= λ0) { + if (λ < λ0) λ0 = λ; + if (λ > λ1) λ1 = λ; + } else { + if (λ > λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } + } + } else { + point(λ, φ); } + p0 = p, λ_ = λ; + } - function lineEnd() { - resample.point = point; - stream.lineEnd(); - } + function lineStart() { bound.point = linePoint; } + function lineEnd() { + range[0] = λ0, range[1] = λ1; + bound.point = point; + p0 = null; + } - function polygonLineStart() { - var λ00, φ00, x00, y00, a00, b00, c00; // first point + function ringPoint(λ, φ) { + if (p0) { + var dλ = λ - λ_; + dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; + } else λ__ = λ, φ__ = φ; + d3_geo_area.point(λ, φ); + linePoint(λ, φ); + } - lineStart(); + function ringStart() { + d3_geo_area.lineStart(); + } - resample.point = function(λ, φ) { - linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; - resample.point = linePoint; - }; + function ringEnd() { + ringPoint(λ__, φ__); + d3_geo_area.lineEnd(); + if (abs(dλSum) > ε) λ0 = -(λ1 = 180); + range[0] = λ0, range[1] = λ1; + p0 = null; + } - resample.lineEnd = function() { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); - resample.lineEnd = lineEnd; - lineEnd(); - }; - } + // Finds the left-right distance between two longitudes. + // This is almost the same as (λ1 - λ0 + 360°) % 360°, except that we want + // the distance between ±180° to be 360°. + function angle(λ0, λ1) { return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; } - return resample; + function compareRanges(a, b) { return a[0] - b[0]; } + + function withinRange(x, range) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; } - function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { - var dx = x1 - x0, - dy = y1 - y0, - d2 = dx * dx + dy * dy; - if (d2 > 4 * δ2 && depth--) { - var a = a0 + a1, - b = b0 + b1, - c = c0 + c1, - m = Math.sqrt(a * a + b * b + c * c), - φ2 = Math.asin(c /= m), - λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), - p = project(λ2, φ2), - x2 = p[0], - y2 = p[1], - dx2 = x2 - x0, - dy2 = y2 - y0, - dz = dy * dx2 - dx * dy2; - if (dz * dz / d2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3) { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); - stream.point(x2, y2); - resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + return function(feature) { + φ1 = λ1 = -(λ0 = φ0 = Infinity); + ranges = []; + + d3.geo.stream(feature, bound); + + var n = ranges.length; + if (n) { + // First, sort ranges by their minimum longitudes. + ranges.sort(compareRanges); + + // Then, merge any ranges that overlap. + for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) { + b = ranges[i]; + if (withinRange(b[0], a) || withinRange(b[1], a)) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + + // Finally, find the largest gap between the merged ranges. + // The final bounding box will be the inverse of this gap. + var best = -Infinity, dλ; + for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { + b = merged[i]; + if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; } } - } + ranges = range = null; - resample.precision = function(_) { - if (!arguments.length) return Math.sqrt(δ2); - maxDepth = (δ2 = _ * _) > 0 && 16; - return resample; + return λ0 === Infinity || φ0 === Infinity + ? [[NaN, NaN], [NaN, NaN]] + : [[λ0, φ0], [λ1, φ1]]; }; +})(); - return resample; -} +d3.geo.centroid = function(object) { + d3_geo_centroidW0 = d3_geo_centroidW1 = + d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = + d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = + d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, d3_geo_centroid); -d3.geo.projection = d3_geo_projection; -d3.geo.projectionMutator = d3_geo_projectionMutator; + var x = d3_geo_centroidX2, + y = d3_geo_centroidY2, + z = d3_geo_centroidZ2, + m = x * x + y * y + z * z; -function d3_geo_projection(project) { - return d3_geo_projectionMutator(function() { return project; })(); -} + // If the area-weighted centroid is undefined, fall back to length-weighted centroid. + if (m < ε2) { + x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; + // If the feature has zero length, fall back to arithmetic mean of point vectors. + if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; + m = x * x + y * y + z * z; + // If the feature still has an undefined centroid, then return. + if (m < ε2) return [NaN, NaN]; + } -function d3_geo_projectionMutator(projectAt) { - var project, - rotate, - projectRotate, - projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }), - k = 150, // scale - x = 480, y = 250, // translate - λ = 0, φ = 0, // center - δλ = 0, δφ = 0, δγ = 0, // rotate - δx, δy, // center - preclip = d3_geo_clipAntimeridian, - postclip = d3_identity, - clipAngle = null, - clipExtent = null; + return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees]; +}; - function projection(point) { - point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); - return [point[0] * k + δx, δy - point[1] * k]; - } +var d3_geo_centroidW0, + d3_geo_centroidW1, + d3_geo_centroidX0, + d3_geo_centroidY0, + d3_geo_centroidZ0, + d3_geo_centroidX1, + d3_geo_centroidY1, + d3_geo_centroidZ1, + d3_geo_centroidX2, + d3_geo_centroidY2, + d3_geo_centroidZ2; - function invert(point) { - point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); - return point && [point[0] * d3_degrees, point[1] * d3_degrees]; +var d3_geo_centroid = { + sphere: d3_noop, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; } +}; - projection.stream = function(stream) { - return d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(stream)))); - }; +// Arithmetic mean of Cartesian vectors. +function d3_geo_centroidPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); +} - projection.clipAngle = function(_) { - if (!arguments.length) return clipAngle; - preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); - return projection; - }; +function d3_geo_centroidPointXYZ(x, y, z) { + ++d3_geo_centroidW0; + d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; + d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; + d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; +} - projection.clipExtent = function(_) { - if (!arguments.length) return clipExtent; - clipExtent = _; - postclip = _ == null ? d3_identity : d3_geo_clipView(_[0][0], _[0][1], _[1][0], _[1][1]); - return projection; - }; +function d3_geo_centroidLineStart() { + var x0, y0, z0; // previous point - projection.scale = function(_) { - if (!arguments.length) return k; - k = +_; - return reset(); + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + d3_geo_centroidPointXYZ(x0, y0, z0); }; - projection.translate = function(_) { - if (!arguments.length) return [x, y]; - x = +_[0]; - y = +_[1]; - return reset(); - }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), + x = cosφ * Math.cos(λ), + y = cosφ * Math.sin(λ), + z = Math.sin(φ), + w = Math.atan2( + Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), + x0 * x + y0 * y + z0 * z); + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } +} - projection.center = function(_) { - if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees]; - λ = _[0] % 360 * d3_radians; - φ = _[1] % 360 * d3_radians; - return reset(); +function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; +} + +// See J. E. Brock, The Inertia Tensor for a Spherical Triangle, +// J. Applied Mechanics 42, 239 (1975). +function d3_geo_centroidRingStart() { + var λ00, φ00, // first point + x0, y0, z0; // previous point + + d3_geo_centroid.point = function(λ, φ) { + λ00 = λ, φ00 = φ; + d3_geo_centroid.point = nextPoint; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroidPointXYZ(x0, y0, z0); }; - projection.rotate = function(_) { - if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees]; - δλ = _[0] % 360 * d3_radians; - δφ = _[1] % 360 * d3_radians; - δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; - return reset(); + d3_geo_centroid.lineEnd = function() { + nextPoint(λ00, φ00); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + d3_geo_centroid.point = d3_geo_centroidPoint; }; - d3.rebind(projection, projectResample, "precision"); + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), + x = cosφ * Math.cos(λ), + y = cosφ * Math.sin(λ), + z = Math.sin(φ), + cx = y0 * z - z0 * y, + cy = z0 * x - x0 * z, + cz = x0 * y - y0 * x, + m = Math.sqrt(cx * cx + cy * cy + cz * cz), + u = x0 * x + y0 * y + z0 * z, + v = m && -d3_acos(u) / m, // area weight + w = Math.atan2(m, u); // line weight + d3_geo_centroidX2 += v * cx; + d3_geo_centroidY2 += v * cy; + d3_geo_centroidZ2 += v * cz; + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } +} - function reset() { - projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); - var center = project(λ, φ); - δx = x - center[0] * k; - δy = y + center[1] * k; - return projection; +// TODO Unify this code with d3.geom.polygon area? + +var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + + // Only count area for polygon rings. + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); } +}; - return function() { - project = projectAt.apply(this, arguments); - projection.invert = project.invert && invert; - return reset(); +function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + + // For the first point, … + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; }; -} -function d3_geo_projectionRadiansRotate(rotate, stream) { - return { - point: function(x, y) { - y = rotate(x * d3_radians, y * d3_radians), x = y[0]; - stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]); - }, - sphere: function() { stream.sphere(); }, - lineStart: function() { stream.lineStart(); }, - lineEnd: function() { stream.lineEnd(); }, - polygonStart: function() { stream.polygonStart(); }, - polygonEnd: function() { stream.polygonEnd(); } + // For subsequent points, … + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + + // For the last point, return to the start. + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); }; } -function d3_geo_mercator(λ, φ) { - return [λ, Math.log(Math.tan(π / 4 + φ / 2))]; -} +var d3_geo_pathBoundsX0, + d3_geo_pathBoundsY0, + d3_geo_pathBoundsX1, + d3_geo_pathBoundsY1; -d3_geo_mercator.invert = function(x, y) { - return [x, 2 * Math.atan(Math.exp(y)) - π / 2]; +var d3_geo_pathBounds = { + point: d3_geo_pathBoundsPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop }; -function d3_geo_mercatorProjection(project) { - var m = d3_geo_projection(project), - scale = m.scale, - translate = m.translate, - clipExtent = m.clipExtent, - clipAuto; +function d3_geo_pathBoundsPoint(x, y) { + if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; + if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; + if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; + if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; +} +function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathBufferCircle(4.5), + buffer = []; - m.scale = function() { - var v = scale.apply(m, arguments); - return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; - }; + var stream = { + point: point, - m.translate = function() { - var v = translate.apply(m, arguments); - return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; - }; + // While inside a line, override point to moveTo then lineTo. + lineStart: function() { stream.point = pointLineStart; }, + lineEnd: lineEnd, - m.clipExtent = function(_) { - var v = clipExtent.apply(m, arguments); - if (v === m) { - if (clipAuto = _ == null) { - var k = π * scale(), t = translate(); - clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]); + // While inside a polygon, override lineEnd to closePath. + polygonStart: function() { stream.lineEnd = lineEndPolygon; }, + polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; }, + + pointRadius: function(_) { + pointCircle = d3_geo_pathBufferCircle(_); + return stream; + }, + + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; } - } else if (clipAuto) { - v = null; } - return v; }; - return m.clipExtent(null); -} + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } -(d3.geo.mercator = function() { - return d3_geo_mercatorProjection(d3_geo_mercator); -}).raw = d3_geo_mercator; + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } -function d3_geo_conic(projectAt) { - var φ0 = 0, - φ1 = π / 3, - m = d3_geo_projectionMutator(projectAt), - p = m(φ0, φ1); + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } - p.parallels = function(_) { - if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180]; - return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); - }; + function lineEnd() { + stream.point = point; + } - return p; + function lineEndPolygon() { + buffer.push("Z"); + } + + return stream; } -function d3_geo_conicEqualArea(φ0, φ1) { - var sinφ0 = Math.sin(φ0), - n = (sinφ0 + Math.sin(φ1)) / 2, - C = 1 + sinφ0 * (2 * n - sinφ0), - ρ0 = Math.sqrt(C) / n; +function d3_geo_pathBufferCircle(radius) { + return "m0," + radius + + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + + "z"; +} - function forward(λ, φ) { - var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; - return [ - ρ * Math.sin(λ *= n), - ρ0 - ρ * Math.cos(λ) - ]; - } - - forward.invert = function(x, y) { - var ρ0_y = ρ0 - y; - return [ - Math.atan2(x, ρ0_y) / n, - Math.asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) - ]; - }; - - return forward; -} - -(d3.geo.conicEqualArea = function() { - return d3_geo_conic(d3_geo_conicEqualArea); -}).raw = d3_geo_conicEqualArea; - -// A composite projection for the United States, 960×500. The set of standard -// parallels for each region comes from USGS, which is published here: -// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers -d3.geo.albersUsa = function() { - var lower48 = d3.geo.conicEqualArea() - .rotate([98, 0]) - .center([0, 38]) - .parallels([29.5, 45.5]); - - var alaska = d3.geo.conicEqualArea() - .rotate([160, 0]) - .center([0, 60]) - .parallels([55, 65]); - - var hawaii = d3.geo.conicEqualArea() - .rotate([160, 0]) - .center([0, 20]) - .parallels([8, 18]); - - var puertoRico = d3.geo.conicEqualArea() - .rotate([60, 0]) - .center([0, 10]) - .parallels([8, 18]); - - var alaskaInvert, - hawaiiInvert, - puertoRicoInvert; - - function albersUsa(coordinates) { - return projection(coordinates)(coordinates); - } - - function projection(point) { - var lon = point[0], - lat = point[1]; - return lat > 50 ? alaska - : lon < -140 ? hawaii - : lat < 21 ? puertoRico - : lower48; - } - - albersUsa.invert = function(coordinates) { - return alaskaInvert(coordinates) || hawaiiInvert(coordinates) || puertoRicoInvert(coordinates) || lower48.invert(coordinates); - }; - - albersUsa.scale = function(x) { - if (!arguments.length) return lower48.scale(); - lower48.scale(x); - alaska.scale(x * .6); - hawaii.scale(x); - puertoRico.scale(x * 1.5); - return albersUsa.translate(lower48.translate()); - }; - - albersUsa.translate = function(x) { - if (!arguments.length) return lower48.translate(); - var dz = lower48.scale(), - dx = x[0], - dy = x[1]; - lower48.translate(x); - alaska.translate([dx - .40 * dz, dy + .17 * dz]); - hawaii.translate([dx - .19 * dz, dy + .20 * dz]); - puertoRico.translate([dx + .58 * dz, dy + .43 * dz]); - - alaskaInvert = d3_geo_albersUsaInvert(alaska, [[-180, 50], [-130, 72]]); - hawaiiInvert = d3_geo_albersUsaInvert(hawaii, [[-164, 18], [-154, 24]]); - puertoRicoInvert = d3_geo_albersUsaInvert(puertoRico, [[-67.5, 17.5], [-65, 19]]); - - return albersUsa; - }; - - return albersUsa.scale(1000); -}; - -function d3_geo_albersUsaInvert(projection, extent) { - var a = projection(extent[0]), - b = projection([.5 * (extent[0][0] + extent[1][0]), extent[0][1]]), - c = projection([extent[1][0], extent[0][1]]), - d = projection(extent[1]); - - var dya = b[1]- a[1], - dxa = b[0]- a[0], - dyb = c[1]- b[1], - dxb = c[0]- b[0]; - - var ma = dya / dxa, - mb = dyb / dxb; - - // Find center of circle going through points [a, b, c]. - var cx = .5 * (ma * mb * (a[1] - c[1]) + mb * (a[0] + b[0]) - ma * (b[0] + c[0])) / (mb - ma), - cy = (.5 * (a[0] + b[0]) - cx) / ma + .5 * (a[1] + b[1]); - - // Radial distance² from center. - var dx0 = d[0] - cx, - dy0 = d[1] - cy, - dx1 = a[0] - cx, - dy1 = a[1] - cy, - r0 = dx0 * dx0 + dy0 * dy0, - r1 = dx1 * dx1 + dy1 * dy1; - - // Angular extent. - var a0 = Math.atan2(dy0, dx0), - a1 = Math.atan2(dy1, dx1); - - return function(coordinates) { - var dx = coordinates[0] - cx, - dy = coordinates[1] - cy, - r = dx * dx + dy * dy, - a = Math.atan2(dy, dx); - if (r0 < r && r < r1 && a0 < a && a < a1) return projection.invert(coordinates); - }; -} - -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_areaRingU, - d3_geo_areaRingV; - -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_areaRingU = 1, d3_geo_areaRingV = 0; - d3_geo_area.lineStart = d3_geo_areaRingStart; - }, - polygonEnd: function() { - var area = 2 * Math.atan2(d3_geo_areaRingV, d3_geo_areaRingU); - 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 two previous points - - // 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φ, - u0 = d3_geo_areaRingU, - v0 = d3_geo_areaRingV, - u = cosφ0 * cosφ + k * Math.cos(dλ), - v = k * Math.sin(dλ); - // ∑ arg(z) = arg(∏ z), where z = u + iv. - d3_geo_areaRingU = u0 * u - v0 * v; - d3_geo_areaRingV = v0 * u + u0 * v; - - // 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); - }; -} - -d3.geo.bounds = d3_geo_bounds(d3_identity); - -function d3_geo_bounds(projectStream) { - var x0, y0, x1, y1; - - var bound = { - point: boundPoint, - lineStart: d3_noop, - lineEnd: d3_noop, - - // While inside a polygon, ignore points in holes. - polygonStart: function() { bound.lineEnd = boundPolygonLineEnd; }, - polygonEnd: function() { bound.point = boundPoint; } - }; - - function boundPoint(x, y) { - if (x < x0) x0 = x; - if (x > x1) x1 = x; - if (y < y0) y0 = y; - if (y > y1) y1 = y; - } - - function boundPolygonLineEnd() { - bound.point = bound.lineEnd = d3_noop; - } - - return function(feature) { - y1 = x1 = -(x0 = y0 = Infinity); - d3.geo.stream(feature, projectStream(bound)); - return [[x0, y0], [x1, y1]]; - }; -} +// TODO Unify this code with d3.geom.polygon centroid? +// TODO Enforce positive area for exterior, negative area for interior? -d3.geo.centroid = function(object) { - d3_geo_centroidDimension = d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; - d3.geo.stream(object, d3_geo_centroid); - var m; - if (d3_geo_centroidW && - Math.abs(m = Math.sqrt(d3_geo_centroidX * d3_geo_centroidX + d3_geo_centroidY * d3_geo_centroidY + d3_geo_centroidZ * d3_geo_centroidZ)) > ε) { - return [ - Math.atan2(d3_geo_centroidY, d3_geo_centroidX) * d3_degrees, - Math.asin(Math.max(-1, Math.min(1, d3_geo_centroidZ / m))) * d3_degrees - ]; - } -}; +var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, -var d3_geo_centroidDimension, - d3_geo_centroidW, - d3_geo_centroidX, - d3_geo_centroidY, - d3_geo_centroidZ; + // For lines, weight by length. + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, -var d3_geo_centroid = { - sphere: function() { - if (d3_geo_centroidDimension < 2) { - d3_geo_centroidDimension = 2; - d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; - } - }, - point: d3_geo_centroidPoint, - lineStart: d3_geo_centroidLineStart, - lineEnd: d3_geo_centroidLineEnd, + // For polygons, weight by area. polygonStart: function() { - if (d3_geo_centroidDimension < 2) { - d3_geo_centroidDimension = 2; - d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; - } - d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; }, polygonEnd: function() { - d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; } }; -// Arithmetic mean of Cartesian vectors. -function d3_geo_centroidPoint(λ, φ) { - if (d3_geo_centroidDimension) return; - ++d3_geo_centroidW; - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - d3_geo_centroidX += (cosφ * Math.cos(λ) - d3_geo_centroidX) / d3_geo_centroidW; - d3_geo_centroidY += (cosφ * Math.sin(λ) - d3_geo_centroidY) / d3_geo_centroidW; - d3_geo_centroidZ += (Math.sin(φ) - d3_geo_centroidZ) / d3_geo_centroidW; -} - -function d3_geo_centroidRingStart() { - var λ00, φ00; // first point - - d3_geo_centroidDimension = 1; - d3_geo_centroidLineStart(); - d3_geo_centroidDimension = 2; - - var linePoint = d3_geo_centroid.point; - d3_geo_centroid.point = function(λ, φ) { - linePoint(λ00 = λ, φ00 = φ); - }; - d3_geo_centroid.lineEnd = function() { - d3_geo_centroid.point(λ00, φ00); - d3_geo_centroidLineEnd(); - d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; - }; +function d3_geo_pathCentroidPoint(x, y) { + d3_geo_centroidX0 += x; + d3_geo_centroidY0 += y; + ++d3_geo_centroidZ0; } -function d3_geo_centroidLineStart() { - var x0, y0, z0; // previous point - - if (d3_geo_centroidDimension > 1) return; - if (d3_geo_centroidDimension < 1) { - d3_geo_centroidDimension = 1; - d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; - } +function d3_geo_pathCentroidLineStart() { + var x0, y0; - d3_geo_centroid.point = function(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroid.point = nextPoint; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); }; - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), - x = cosφ * Math.cos(λ), - y = cosφ * Math.sin(λ), - z = Math.sin(φ), - w = Math.atan2( - Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), - x0 * x + y0 * y + z0 * z); - d3_geo_centroidW += w; - d3_geo_centroidX += w * (x0 + (x0 = x)); - d3_geo_centroidY += w * (y0 + (y0 = y)); - d3_geo_centroidZ += w * (z0 + (z0 = z)); + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); } } -function d3_geo_centroidLineEnd() { - d3_geo_centroid.point = d3_geo_centroidPoint; +function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; } -// TODO Unify this code with d3.geom.polygon area? - -var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - - // Only count area for polygon rings. - polygonStart: function() { - d3_geo_pathAreaPolygon = 0; - d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; - }, - polygonEnd: function() { - d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; - d3_geo_pathAreaSum += Math.abs(d3_geo_pathAreaPolygon / 2); - } -}; - -function d3_geo_pathAreaRingStart() { +function d3_geo_pathCentroidRingStart() { var x00, y00, x0, y0; // For the first point, … - d3_geo_pathArea.point = function(x, y) { - d3_geo_pathArea.point = nextPoint; - x00 = x0 = x, y00 = y0 = y; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); }; // For subsequent points, … function nextPoint(x, y) { - d3_geo_pathAreaPolygon += y0 * x - x0 * y; - x0 = x, y0 = y; + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + + z = y0 * x - x0 * y; + d3_geo_centroidX2 += z * (x0 + x); + d3_geo_centroidY2 += z * (y0 + y); + d3_geo_centroidZ2 += z * 3; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); } // For the last point, return to the start. - d3_geo_pathArea.lineEnd = function() { + d3_geo_pathCentroid.lineEnd = function() { nextPoint(x00, y00); }; } -function d3_geo_pathBuffer() { - var pointCircle = d3_geo_pathCircle(4.5), - buffer = []; + +function d3_geo_pathContext(context) { + var pointRadius = 4.5; var stream = { point: point, @@ -3608,30 +4217,25 @@ function d3_geo_pathBuffer() { polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; }, pointRadius: function(_) { - pointCircle = d3_geo_pathCircle(_); + pointRadius = _; return stream; }, - result: function() { - if (buffer.length) { - var result = buffer.join(""); - buffer = []; - return result; - } - } + result: d3_noop }; function point(x, y) { - buffer.push("M", x, ",", y, pointCircle); + context.moveTo(x + pointRadius, y); + context.arc(x, y, pointRadius, 0, τ); } function pointLineStart(x, y) { - buffer.push("M", x, ",", y); + context.moveTo(x, y); stream.point = pointLine; } function pointLine(x, y) { - buffer.push("L", x, ",", y); + context.lineTo(x, y); } function lineEnd() { @@ -3639,142 +4243,116 @@ function d3_geo_pathBuffer() { } function lineEndPolygon() { - buffer.push("Z"); + context.closePath(); } return stream; } -// TODO Unify this code with d3.geom.polygon centroid? -// TODO Enforce positive area for exterior, negative area for interior? +function d3_geo_resample(project) { + var δ2 = .5, // precision, px² + cosMinDistance = Math.cos(30 * d3_radians), // cos(minimum angular distance) + maxDepth = 16; -var d3_geo_pathCentroid = { - point: d3_geo_pathCentroidPoint, + function resample(stream) { + return (maxDepth ? resampleRecursive : resampleNone)(stream); + } - // For lines, weight by length. - lineStart: d3_geo_pathCentroidLineStart, - lineEnd: d3_geo_pathCentroidLineEnd, + function resampleNone(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + }); + } - // For polygons, weight by area. - polygonStart: function() { - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; - }, - polygonEnd: function() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; - d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; - } -}; - -function d3_geo_pathCentroidPoint(x, y) { - if (d3_geo_centroidDimension) return; - d3_geo_centroidX += x; - d3_geo_centroidY += y; - ++d3_geo_centroidZ; -} - -function d3_geo_pathCentroidLineStart() { - var x0, y0; - - if (d3_geo_centroidDimension !== 1) { - if (d3_geo_centroidDimension < 1) { - d3_geo_centroidDimension = 1; - d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; - } else return; - } - - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - x0 = x, y0 = y; - }; - - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX += z * (x0 + x) / 2; - d3_geo_centroidY += z * (y0 + y) / 2; - d3_geo_centroidZ += z; - x0 = x, y0 = y; - } -} - -function d3_geo_pathCentroidLineEnd() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; -} - -function d3_geo_pathCentroidRingStart() { - var x00, y00, x0, y0; - - if (d3_geo_centroidDimension < 2) { - d3_geo_centroidDimension = 2; - d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; - } - - // For the first point, … - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - x00 = x0 = x, y00 = y0 = y; - }; - - // For subsequent points, … - function nextPoint(x, y) { - var z = y0 * x - x0 * y; - d3_geo_centroidX += z * (x0 + x); - d3_geo_centroidY += z * (y0 + y); - d3_geo_centroidZ += z * 3; - x0 = x, y0 = y; - } - - // For the last point, return to the start. - d3_geo_pathCentroid.lineEnd = function() { - nextPoint(x00, y00); - }; -} + function resampleRecursive(stream) { + var λ00, φ00, x00, y00, a00, b00, c00, // first point + λ0, x0, y0, a0, b0, c0; // previous point -function d3_geo_pathContext(context) { - var pointRadius = 4.5; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; }, + polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; } + }; - var stream = { - point: point, + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } - // While inside a line, override point to moveTo then lineTo. - lineStart: function() { stream.point = pointLineStart; }, - lineEnd: lineEnd, + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } - // While inside a polygon, override lineEnd to closePath. - polygonStart: function() { stream.lineEnd = lineEndPolygon; }, - polygonEnd: function() { stream.lineEnd = lineEnd; stream.point = point; }, + function linePoint(λ, φ) { + var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } - pointRadius: function(_) { - pointRadius = _; - return stream; - }, + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } - result: d3_noop - }; + function ringStart() { + lineStart(); + resample.point = ringPoint; + resample.lineEnd = ringEnd; + } - function point(x, y) { - context.moveTo(x, y); - context.arc(x, y, pointRadius, 0, 2 * π); - } + function ringPoint(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + } - function pointLineStart(x, y) { - context.moveTo(x, y); - stream.point = pointLine; - } + function ringEnd() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + } - function pointLine(x, y) { - context.lineTo(x, y); + return resample; } - function lineEnd() { - stream.point = point; + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, + dy = y1 - y0, + d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, + b = b0 + b1, + c = c0 + c1, + m = Math.sqrt(a * a + b * b + c * c), + φ2 = Math.asin(c /= m), + λ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], + dx2 = x2 - x0, + dy2 = y2 - y0, + dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 // perpendicular projected distance + || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end + || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } } - function lineEndPolygon() { - context.closePath(); - } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; - return stream; + return resample; } d3.geo.path = function() { @@ -3782,13 +4360,15 @@ d3.geo.path = function() { projection, context, projectStream, - contextStream; + contextStream, + cacheStream; function path(object) { - if (object) d3.geo.stream(object, projectStream( - contextStream.pointRadius(typeof pointRadius === "function" - ? +pointRadius.apply(this, arguments) - : pointRadius))); + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); + d3.geo.stream(object, cacheStream); + } return contextStream.result(); } @@ -3799,251 +4379,488 @@ d3.geo.path = function() { }; path.centroid = function(object) { - d3_geo_centroidDimension = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = + d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = + d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); - return d3_geo_centroidZ ? [d3_geo_centroidX / d3_geo_centroidZ, d3_geo_centroidY / d3_geo_centroidZ] : undefined; + return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2] + : d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1] + : d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0] + : [NaN, NaN]; }; path.bounds = function(object) { - return d3_geo_bounds(projectStream)(object); + d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); + d3.geo.stream(object, projectStream(d3_geo_pathBounds)); + return [[d3_geo_pathBoundsX0, d3_geo_pathBoundsY0], [d3_geo_pathBoundsX1, d3_geo_pathBoundsY1]]; }; path.projection = function(_) { if (!arguments.length) return projection; projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; - return path; + return reset(); }; path.context = function(_) { if (!arguments.length) return context; contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_); - return path; + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return reset(); }; path.pointRadius = function(_) { if (!arguments.length) return pointRadius; - pointRadius = typeof _ === "function" ? _ : +_; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); return path; }; + function reset() { + cacheStream = null; + return path; + } + return path.projection(d3.geo.albersUsa()).context(null); }; -function d3_geo_pathCircle(radius) { - return "m0," + radius - + "a" + radius + "," + radius + " 0 1,1 0," + (-2 * radius) - + "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius) - + "z"; -} - function d3_geo_pathProjectStream(project) { - var resample = d3_geo_resample(function(λ, φ) { return project([λ * d3_degrees, φ * d3_degrees]); }); - return function(stream) { - stream = resample(stream); - return { - point: function(λ, φ) { stream.point(λ * d3_radians, φ * d3_radians); }, - sphere: function() { stream.sphere(); }, - lineStart: function() { stream.lineStart(); }, - lineEnd: function() { stream.lineEnd(); }, - polygonStart: function() { stream.polygonStart(); }, - polygonEnd: function() { stream.polygonEnd(); } - }; - }; + var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); }); + return function(stream) { return d3_geo_projectionRadians(resample(stream)); }; } -d3.geom = {}; - -d3.geom.polygon = function(coordinates) { - - coordinates.area = function() { - var i = 0, - n = coordinates.length, - area = coordinates[n - 1][1] * coordinates[0][0] - coordinates[n - 1][0] * coordinates[0][1]; - while (++i < n) { - area += coordinates[i - 1][1] * coordinates[i][0] - coordinates[i - 1][0] * coordinates[i][1]; - } - return area * .5; - }; - coordinates.centroid = function(k) { - var i = -1, - n = coordinates.length, - x = 0, - y = 0, - a, - b = coordinates[n - 1], - c; - if (!arguments.length) k = -1 / (6 * coordinates.area()); - while (++i < n) { - a = b; - b = coordinates[i]; - c = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * c; - y += (a[1] + b[1]) * c; +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; } - return [x * k, y * k]; }; +}; - // The Sutherland-Hodgman clipping algorithm. - // Note: requires the clip polygon to be counterclockwise and convex. - coordinates.clip = function(subject) { - var input, - i = -1, - n = coordinates.length, - j, - m, - a = coordinates[n - 1], - b, - c, - d; - while (++i < n) { - input = subject.slice(); - subject.length = 0; - b = coordinates[i]; - c = input[(m = input.length) - 1]; - j = -1; - while (++j < m) { - d = input[j]; - if (d3_geom_polygonInside(d, a, b)) { - if (!d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - subject.push(d); - } else if (d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - c = d; - } - a = b; - } - return subject; - }; +function d3_geo_transform(stream) { + this.stream = stream; +} - return coordinates; +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_geom_polygonInside(p, a, b) { - return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); +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(); }, + }; } -// Intersect two infinite lines cd and ab. -function d3_geom_polygonIntersect(c, d, a, b) { - var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, - y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, - ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); - return [x1 + ua * x21, y1 + ua * y21]; +d3.geo.projection = d3_geo_projection; +d3.geo.projectionMutator = d3_geo_projectionMutator; + +function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { return project; })(); } -var d3_ease_default = function() { return d3_identity; }; +function d3_geo_projectionMutator(projectAt) { + var project, + rotate, + projectRotate, + projectResample = d3_geo_resample(function(x, y) { x = project(x, y); return [x[0] * k + δx, δy - x[1] * k]; }), + k = 150, // scale + x = 480, y = 250, // translate + λ = 0, φ = 0, // center + δλ = 0, δφ = 0, δγ = 0, // rotate + δx, δy, // center + preclip = d3_geo_clipAntimeridian, + postclip = d3_identity, + clipAngle = null, + clipExtent = null, + stream; -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; } -}); + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [point[0] * k + δx, δy - point[1] * k]; + } -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)); } -}); + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [point[0] * d3_degrees, point[1] * d3_degrees]; + } -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, Array.prototype.slice.call(arguments, 1)))); -}; + projection.stream = function(output) { + if (stream) stream.valid = false; + stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); + stream.valid = true; // allow caching by d3.geo.path + return stream; + }; -function d3_ease_clamp(f) { - return function(t) { - return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); + return invalidate(); }; -} -function d3_ease_reverse(f) { - return function(t) { - return 1 - f(1 - t); + projection.clipExtent = function(_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; + return invalidate(); }; -} -function d3_ease_reflect(f) { - return function(t) { - return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t))); + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); }; -} -function d3_ease_quad(t) { - return t * t; + projection.translate = function(_) { + if (!arguments.length) return [x, y]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + + projection.center = function(_) { + if (!arguments.length) return [λ * d3_degrees, φ * d3_degrees]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + + projection.rotate = function(_) { + if (!arguments.length) return [δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + + d3.rebind(projection, projectResample, "precision"); + + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return invalidate(); + } + + function invalidate() { + if (stream) stream.valid = false, stream = null; + return projection; + } + + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; } -function d3_ease_cubic(t) { - return t * t * t; +function d3_geo_projectionRadians(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + stream.point(x * d3_radians, y * d3_radians); + }); } -// 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_geo_mercator(λ, φ) { + return [λ, Math.log(Math.tan(π / 4 + φ / 2))]; } -function d3_ease_poly(e) { - return function(t) { - return Math.pow(t, e); +d3_geo_mercator.invert = function(x, y) { + return [x, 2 * Math.atan(Math.exp(y)) - halfπ]; +}; + +function d3_geo_mercatorProjection(project) { + var m = d3_geo_projection(project), + scale = m.scale, + translate = m.translate, + clipExtent = m.clipExtent, + clipAuto; + + m.scale = function() { + var v = scale.apply(m, arguments); + return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; + }; + + m.translate = function() { + var v = translate.apply(m, arguments); + return v === m ? (clipAuto ? m.clipExtent(null) : m) : v; + }; + + m.clipExtent = function(_) { + var v = clipExtent.apply(m, arguments); + if (v === m) { + if (clipAuto = _ == null) { + var k = π * scale(), t = translate(); + clipExtent([[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]]); + } + } else if (clipAuto) { + v = null; + } + return v; }; + + return m.clipExtent(null); } -function d3_ease_sin(t) { - return 1 - Math.cos(t * π / 2); +(d3.geo.mercator = function() { + return d3_geo_mercatorProjection(d3_geo_mercator); +}).raw = d3_geo_mercator; +d3.geom = {}; + +d3.geom.polygon = function(coordinates) { + d3_subclass(coordinates, d3_geom_polygonPrototype); + return coordinates; +}; + +var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; + +d3_geom_polygonPrototype.area = function() { + var i = -1, + n = this.length, + a, + b = this[n - 1], + area = 0; + + while (++i < n) { + a = b; + b = this[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + + return area * .5; +}; + +d3_geom_polygonPrototype.centroid = function(k) { + var i = -1, + n = this.length, + x = 0, + y = 0, + a, + b = this[n - 1], + c; + + if (!arguments.length) k = -1 / (6 * this.area()); + + while (++i < n) { + a = b; + b = this[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + + return [x * k, y * k]; +}; + +// The Sutherland-Hodgman clipping algorithm. +// Note: requires the clip polygon to be counterclockwise and convex. +d3_geom_polygonPrototype.clip = function(subject) { + var input, + closed = d3_geom_polygonClosed(subject), + i = -1, + n = this.length - d3_geom_polygonClosed(this), + j, + m, + a = this[n - 1], + b, + c, + d; + + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = this[i]; + c = input[(m = input.length - closed) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + if (closed) subject.push(subject[0]); + a = b; + } + + return subject; +}; + +function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); } -function d3_ease_exp(t) { - return Math.pow(2, 10 * (t - 1)); +// Intersect two infinite lines cd and ab. +function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, + y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, + ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [x1 + ua * x21, y1 + ua * y21]; } -function d3_ease_circle(t) { - return 1 - Math.sqrt(1 - t * t); +// Returns true if the polygon is closed. +function d3_geom_polygonClosed(coordinates) { + var a = coordinates[0], + b = coordinates[coordinates.length - 1]; + return !(a[0] - b[0] || a[1] - b[1]); +} +function d3_geom_pointX(d) { + return d[0]; } -function d3_ease_elastic(a, p) { - var s; - if (arguments.length < 2) p = 0.45; - if (arguments.length) s = p / (2 * π) * 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) * 2 * π / p); - }; +function d3_geom_pointY(d) { + return d[1]; } -function d3_ease_back(s) { - if (!s) s = 1.70158; - return function(t) { - return t * t * ((s + 1) * t - s); +/** + * 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; + + if (arguments.length) return hull(vertices); + + function hull(data) { + // Hull of < 3 points is not well-defined + if (data.length < 3) return []; + + var fx = d3_functor(x), + fy = d3_functor(y), + i, + n = data.length, + points = [], // of the form [[x0, y0, 0], ..., [xn, yn, n]] + flippedPoints = []; + + 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; + }; + + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; }; + + return hull; +}; + +// 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 + + 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; + } + + // we slice to make sure that the points we 'popped' from hull don't stay behind + return hull.slice(0, hs); } -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; +// 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"; + +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}; + + 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); + } + } + + return d3_transition(subgroups, ns, id); +}; +// import "../transition/transition"; + +// 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))); +}; + +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) { - d3_arraySubclass(groups, d3_transitionPrototype); +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; } @@ -4051,16 +4868,17 @@ function d3_transition(groups, id) { var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, - d3_transitionInherit = {ease: d3_ease_cubicInOut, delay: 0, duration: 250}; + d3_transitionInherit; d3_transitionPrototype.call = d3_selectionPrototype.call; 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; @@ -4068,19 +4886,20 @@ d3.transition.prototype = d3_transitionPrototype; d3_transitionPrototype.select = function(selector) { var id = this.id, + ns = this.namespace, subgroups = [], subgroup, subnode, node; - if (typeof selector !== "function") selector = d3_selection_selector(selector); + selector = d3_selection_selector(selector); 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]) && (subnode = selector.call(node, node.__data__, i))) { + 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); @@ -4088,11 +4907,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, @@ -4100,23 +4920,23 @@ d3_transitionPrototype.selectAll = function(selector) { subnode, transition; - if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + selector = d3_selection_selectorAll(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]; - subnodes = selector.call(node, node.__data__, i); + transition = node[ns][id]; + subnodes = selector.call(node, node.__data__, i, j); subgroups.push(subgroup = []); for (var k = -1, o = subnodes.length; ++k < o;) { - d3_transitionNode(subnode = subnodes[k], 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) { @@ -4130,47 +4950,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, this.time).ease(this.ease()); + 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() { @@ -4182,8 +4996,8 @@ function d3_hsl_rgb(h, s, l) { m2; /* Some simple corrections for h, s and l. */ - h = h % 360; if (h < 0) h += 360; - s = s < 0 ? 0 : s > 1 ? 1 : s; + h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; + s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; l = l < 0 ? 0 : l > 1 ? 1 : l; /* From FvD 13.37, CSS Color Module Level 3 */ @@ -4203,35 +5017,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() { @@ -4239,25 +5045,19 @@ d3_hclPrototype.rgb = function() { }; function d3_hcl_lab(h, c, l) { - return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + if (isNaN(h)) h = 0; + if (isNaN(c)) c = 0; + 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 @@ -4268,14 +5068,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() { @@ -4289,7 +5089,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) @@ -4297,7 +5097,9 @@ function d3_lab_rgb(l, a, b) { } function d3_lab_hcl(l, a, b) { - return d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l); + return l > 0 + ? 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) { @@ -4311,24 +5113,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_parse("" + r, d3_rgb, d3_hsl_rgb)) - : d3_rgb(~~r, ~~g, ~~b); -}; +d3.rgb = d3_rgb; function d3_rgb(r, g, b) { - return new 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)) + : new d3_rgb(r, g, b); +} + +function d3_rgbNumber(value) { + return new d3_rgb(value >> 16, value >> 8 & 0xff, value & 0xff); } -function d3_Rgb(r, g, b) { - this.r = r; - this.g = g; - this.b = b; +function d3_rgbString(value) { + return d3_rgbNumber(value) + ""; } -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); @@ -4336,22 +5138,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, Math.floor(r / k)), - Math.min(255, Math.floor(g / k)), - Math.min(255, Math.floor(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( - Math.floor(k * this.r), - Math.floor(k * this.g), - Math.floor(k * this.b)); + return new d3_rgb(k * this.r, k * this.g, k * this.b); }; d3_rgbPrototype.hsl = function() { @@ -4374,7 +5170,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); @@ -4399,22 +5195,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); @@ -4434,9 +5229,10 @@ function d3_rgb_hsl(r, g, b) { else h = (r - g) / d + 4; h *= 60; } else { - s = h = 0; + 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) { @@ -4459,157 +5255,158 @@ function d3_rgb_parseNumber(c) { // either integer or percentage } var d3_rgb_names = d3.map({ - aliceblue: "#f0f8ff", - antiquewhite: "#faebd7", - aqua: "#00ffff", - aquamarine: "#7fffd4", - azure: "#f0ffff", - beige: "#f5f5dc", - bisque: "#ffe4c4", - black: "#000000", - blanchedalmond: "#ffebcd", - blue: "#0000ff", - blueviolet: "#8a2be2", - brown: "#a52a2a", - burlywood: "#deb887", - cadetblue: "#5f9ea0", - chartreuse: "#7fff00", - chocolate: "#d2691e", - coral: "#ff7f50", - cornflowerblue: "#6495ed", - cornsilk: "#fff8dc", - crimson: "#dc143c", - cyan: "#00ffff", - darkblue: "#00008b", - darkcyan: "#008b8b", - darkgoldenrod: "#b8860b", - darkgray: "#a9a9a9", - darkgreen: "#006400", - darkgrey: "#a9a9a9", - darkkhaki: "#bdb76b", - darkmagenta: "#8b008b", - darkolivegreen: "#556b2f", - darkorange: "#ff8c00", - darkorchid: "#9932cc", - darkred: "#8b0000", - darksalmon: "#e9967a", - darkseagreen: "#8fbc8f", - darkslateblue: "#483d8b", - darkslategray: "#2f4f4f", - darkslategrey: "#2f4f4f", - darkturquoise: "#00ced1", - darkviolet: "#9400d3", - deeppink: "#ff1493", - deepskyblue: "#00bfff", - dimgray: "#696969", - dimgrey: "#696969", - dodgerblue: "#1e90ff", - firebrick: "#b22222", - floralwhite: "#fffaf0", - forestgreen: "#228b22", - fuchsia: "#ff00ff", - gainsboro: "#dcdcdc", - ghostwhite: "#f8f8ff", - gold: "#ffd700", - goldenrod: "#daa520", - gray: "#808080", - green: "#008000", - greenyellow: "#adff2f", - grey: "#808080", - honeydew: "#f0fff0", - hotpink: "#ff69b4", - indianred: "#cd5c5c", - indigo: "#4b0082", - ivory: "#fffff0", - khaki: "#f0e68c", - lavender: "#e6e6fa", - lavenderblush: "#fff0f5", - lawngreen: "#7cfc00", - lemonchiffon: "#fffacd", - lightblue: "#add8e6", - lightcoral: "#f08080", - lightcyan: "#e0ffff", - lightgoldenrodyellow: "#fafad2", - lightgray: "#d3d3d3", - lightgreen: "#90ee90", - lightgrey: "#d3d3d3", - lightpink: "#ffb6c1", - lightsalmon: "#ffa07a", - lightseagreen: "#20b2aa", - lightskyblue: "#87cefa", - lightslategray: "#778899", - lightslategrey: "#778899", - lightsteelblue: "#b0c4de", - lightyellow: "#ffffe0", - lime: "#00ff00", - limegreen: "#32cd32", - linen: "#faf0e6", - magenta: "#ff00ff", - maroon: "#800000", - mediumaquamarine: "#66cdaa", - mediumblue: "#0000cd", - mediumorchid: "#ba55d3", - mediumpurple: "#9370db", - mediumseagreen: "#3cb371", - mediumslateblue: "#7b68ee", - mediumspringgreen: "#00fa9a", - mediumturquoise: "#48d1cc", - mediumvioletred: "#c71585", - midnightblue: "#191970", - mintcream: "#f5fffa", - mistyrose: "#ffe4e1", - moccasin: "#ffe4b5", - navajowhite: "#ffdead", - navy: "#000080", - oldlace: "#fdf5e6", - olive: "#808000", - olivedrab: "#6b8e23", - orange: "#ffa500", - orangered: "#ff4500", - orchid: "#da70d6", - palegoldenrod: "#eee8aa", - palegreen: "#98fb98", - paleturquoise: "#afeeee", - palevioletred: "#db7093", - papayawhip: "#ffefd5", - peachpuff: "#ffdab9", - peru: "#cd853f", - pink: "#ffc0cb", - plum: "#dda0dd", - powderblue: "#b0e0e6", - purple: "#800080", - red: "#ff0000", - rosybrown: "#bc8f8f", - royalblue: "#4169e1", - saddlebrown: "#8b4513", - salmon: "#fa8072", - sandybrown: "#f4a460", - seagreen: "#2e8b57", - seashell: "#fff5ee", - sienna: "#a0522d", - silver: "#c0c0c0", - skyblue: "#87ceeb", - slateblue: "#6a5acd", - slategray: "#708090", - slategrey: "#708090", - snow: "#fffafa", - springgreen: "#00ff7f", - steelblue: "#4682b4", - tan: "#d2b48c", - teal: "#008080", - thistle: "#d8bfd8", - tomato: "#ff6347", - turquoise: "#40e0d0", - violet: "#ee82ee", - wheat: "#f5deb3", - white: "#ffffff", - whitesmoke: "#f5f5f5", - yellow: "#ffff00", - yellowgreen: "#9acd32" + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgreen: 0x006400, + darkgrey: 0xa9a9a9, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkslategrey: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dimgrey: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + grey: 0x808080, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgray: 0xd3d3d3, + lightgreen: 0x90ee90, + lightgrey: 0xd3d3d3, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightslategrey: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370db, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xdb7093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + rebeccapurple: 0x663399, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + slategrey: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32 }); d3_rgb_names.forEach(function(key, value) { - d3_rgb_names.set(key, d3_rgb_parse(value, d3_rgb, d3_hsl_rgb)); + d3_rgb_names.set(key, d3_rgbNumber(value)); }); d3.interpolateRgb = d3_interpolateRgb; @@ -4631,19 +5428,141 @@ function d3_interpolateRgb(a, b) { }; } -d3.transform = function(string) { - var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); - return (d3.transform = function(string) { - g.setAttribute("transform", string); - var t = g.transform.baseVal.consolidate(); - return new d3_transform(t ? t.matrix : d3_transformIdentity); - })(string); -}; +d3.interpolateObject = d3_interpolateObject; -// Compute x-scale and normalize the first row. -// Compute shear and make second row orthogonal to first. -// Compute y-scale and normalize the second row. -// Finally, compute the rotation. +function d3_interpolateObject(a, b) { + var i = {}, + c = {}, + k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolate(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; +} + +d3.interpolateArray = d3_interpolateArray; + +function d3_interpolateArray(a, b) { + var x = [], + c = [], + na = a.length, + nb = b.length, + n0 = Math.min(a.length, b.length), + i; + for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); + for (; i < na; ++i) c[i] = a[i]; + for (; i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; +} +d3.interpolateNumber = d3_interpolateNumber; + +function d3_interpolateNumber(a, b) { + a = +a, b = +b; + return function(t) { return a * (1 - t) + b * t; }; +} + +d3.interpolateString = d3_interpolateString; + +function d3_interpolateString(a, b) { + 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 + + // Coerce inputs to strings. + a = a + "", b = b + ""; + + // 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; + } + 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)}); + } + bi = d3_interpolate_numberB.lastIndex; + } + + // 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 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_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, + d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); + +d3.interpolate = d3_interpolate; + +function d3_interpolate(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))); + return f; +} + +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 + : Array.isArray(b) ? d3_interpolateArray + : t === "object" && isNaN(b) ? d3_interpolateObject + : d3_interpolateNumber)(a, b); + } +]; + +d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + if (string != null) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + } + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); +}; + +// Compute x-scale and normalize the first row. +// Compute shear and make second row orthogonal to first. +// Compute y-scale and normalize the second row. +// Finally, compute the rotation. function d3_transform(m) { var r0 = [m.a, m.b], r1 = [m.c, m.d], @@ -4690,12 +5609,6 @@ function d3_transformCombine(a, b, k) { } var d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0}; -d3.interpolateNumber = d3_interpolateNumber; - -function d3_interpolateNumber(a, b) { - b -= a = +a; - return function(t) { return a + b * t; }; -} d3.interpolateTransform = d3_interpolateTransform; @@ -4751,170 +5664,19 @@ function d3_interpolateTransform(a, b) { }; } -d3.interpolateObject = d3_interpolateObject; - -function d3_interpolateObject(a, b) { - var i = {}, - c = {}, - k; - for (k in a) { - if (k in b) { - i[k] = d3_interpolateByName(k)(a[k], b[k]); - } else { - c[k] = a[k]; - } - } - for (k in b) { - if (!(k in a)) { - c[k] = b[k]; - } - } - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; -} - -d3.interpolateArray = d3_interpolateArray; - -function d3_interpolateArray(a, b) { - var x = [], - c = [], - na = a.length, - nb = b.length, - n0 = Math.min(a.length, b.length), - i; - for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); - for (; i < na; ++i) c[i] = a[i]; - for (; i < nb; ++i) c[i] = b[i]; - return function(t) { - for (i = 0; i < n0; ++i) c[i] = x[i](t); - return c; - }; -} - -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 - s = [], // string constants and placeholders - q = [], // number interpolators - n, // q.length - o; - - // 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)); - } - } - - // 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); - } - n--; - } - - // Special optimization for only a single match. - if (s.length === 1) { - return s[0] == null ? q[0].x : function() { return b; }; - } - - // 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(""); - }; -} - -var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; - -d3.interpolate = d3_interpolate; - -function d3_interpolate(a, b) { - var i = d3.interpolators.length, f; - while (--i >= 0 && !(f = d3.interpolators[i](a, b))); - return f; -} - -function d3_interpolateByName(name) { - return name == "transform" - ? d3_interpolateTransform - : d3_interpolate; -} - -d3.interpolators = [ - function(a, b) { - var t = typeof b; - return (t === "string" || t !== typeof a ? (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) - : d3_interpolateNumber)(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) { @@ -4927,7 +5689,7 @@ d3_transitionPrototype.attr = function(nameNS, value) { return this; } - var interpolate = d3_interpolateByName(nameNS), + var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); // For attr(string, null), remove the attribute with the specified name. @@ -4938,21 +5700,21 @@ d3_transitionPrototype.attr = function(nameNS, value) { this.removeAttributeNS(name.space, name.local); } - return d3_transition_tween(this, "attr." + nameNS, value, function(b) { - - // For attr(string, string), set the attribute with the specified name. - function attrString() { + // For attr(string, string), set the attribute with the specified name. + function attrTween(b) { + return b == null ? attrNull : (b += "", function() { var a = this.getAttribute(name), i; return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); }); - } - function attrStringNS() { + }); + } + function attrTweenNS(b) { + return b == null ? attrNullNS : (b += "", function() { var a = this.getAttributeNS(name.space, name.local), i; return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); }); - } + }); + } - return b == null ? (name.local ? attrNullNS : attrNull) - : (b += "", name.local ? attrStringNS : attrString); - }); + return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); }; d3_transitionPrototype.attrTween = function(nameNS, tween) { @@ -4962,7 +5724,6 @@ d3_transitionPrototype.attrTween = function(nameNS, tween) { var f = tween.call(this, d, i, this.getAttribute(name)); return f && function(t) { this.setAttribute(name, f(t)); }; } - function attrTweenNS(d, i) { var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); return f && function(t) { this.setAttributeNS(name.space, name.local, f(t)); }; @@ -4990,35 +5751,34 @@ d3_transitionPrototype.style = function(name, value, priority) { priority = ""; } - var interpolate = d3_interpolateByName(name); - // For style(name, null) or style(name, null, priority), remove the style // property with the specified name. The priority is ignored. function styleNull() { this.style.removeProperty(name); } + // For style(name, string) or style(name, string, priority), set the style + // property with the specified name, using the specified priority. // Otherwise, a name, value and priority are specified, and handled as below. - return d3_transition_tween(this, "style." + name, value, function(b) { - - // For style(name, string) or style(name, string, priority), set the style - // property with the specified name, using the specified priority. - function styleString() { - var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; - return a !== b && (i = interpolate(a, b), function(t) { this.style.setProperty(name, i(t), priority); }); - } + function styleString(b) { + return b == null ? styleNull : (b += "", function() { + 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); }); + }); + } - return b == null ? styleNull - : (b += "", styleString); - }); + return d3_transition_tween(this, "style." + name, value, styleString); }; d3_transitionPrototype.styleTween = function(name, tween, priority) { if (arguments.length < 3) priority = ""; - return this.tween("style." + name, function(d, i) { - var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); + + function styleTween(d, i) { + 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); }; - }); + } + + return this.tween("style." + name, styleTween); }; d3_transitionPrototype.text = function(value) { @@ -5031,79 +5791,197 @@ function d3_transition_text(b) { } d3_transitionPrototype.remove = function() { + var ns = this.namespace; return this.each("end.transition", function() { var p; - if (!this.__transition__ && (p = this.parentNode)) p.removeChild(this); + if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); }); }; -d3_transitionPrototype.ease = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].ease; - if (typeof value !== "function") value = d3.ease.apply(d3, arguments); - return d3_selection_each(this, function(node) { node.__transition__[id].ease = value; }); -}; +var d3_ease_default = function() { return d3_identity; }; -d3_transitionPrototype.delay = function(value) { - var id = this.id; - return d3_selection_each(this, typeof value === "function" - ? function(node, i, j) { node.__transition__[id].delay = value.call(node, node.__data__, i, j) | 0; } - : (value |= 0, function(node) { node.__transition__[id].delay = value; })); -}; +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; } +}); -d3_transitionPrototype.duration = function(value) { - var id = this.id; - 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) | 0); } - : (value = Math.max(1, value | 0), function(node) { node.__transition__[id].duration = value; })); -}; +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_transitionPrototype.each = function(type, listener) { - var id = this.id; - 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; - } else { - d3_selection_each(this, function(node) { - node.__transition__[id].event.on(type, listener); - }); - } - return this; +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)))); }; -d3_transitionPrototype.transition = function() { - var id0 = this.id, - id1 = ++d3_transitionId, - subgroups = [], - subgroup, - group, - node, - transition; - - for (var j = 0, m = this.length; j < m; j++) { +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, 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[ns][id].ease = value; }); +}; + +d3_transitionPrototype.delay = function(value) { + 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[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, 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[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, ns = this.namespace; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, + inheritId = d3_transitionInheritId; + 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[ns][id]; + (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); + }); + } + return this; +}; + +d3_transitionPrototype.transition = function() { + var id0 = this.id, + id1 = ++d3_transitionId, + ns = this.namespace, + subgroups = [], + subgroup, + group, + node, + transition; + + 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]) { - 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) { @@ -5111,46 +5989,62 @@ function d3_transitionNode(node, i, id, inherit) { transition = lock[id] = { tween: new d3_Map, - event: d3.dispatch("start", "end"), // TODO construct lazily? 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, - event = transition.event, - delay = transition.delay, - duration = transition.duration, + var delay = transition.delay, + duration, + ease, + timer = d3_timer_active, tweened = []; - return delay <= elapsed - ? start(elapsed) - : d3.timer(start, delay, time), 1; + timer.t = delay + time; + if (delay <= elapsed) return start(elapsed - delay); + timer.c = start; 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; - 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); } }); - if (!tick(elapsed)) d3.timer(tick, 0, time); - return 1; + // 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; + }, 0, time); } function tick(elapsed) { - if (lock.active !== id) return stop(); + if (lock.active !== id) return 1; - var t = (elapsed - delay) / duration, + var t = elapsed / duration, e = ease(t), n = tweened.length; @@ -5159,39 +6053,58 @@ function d3_transitionNode(node, i, id, inherit) { } if (t >= 1) { - stop(); - event.end.call(node, d, i); - return 1; + 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); - - return transition; } } -d3.xhr = function(url, mimeType, callback) { +d3.xhr = d3_xhrType(d3_identity); + +function d3_xhrType(response) { + return function(url, mimeType, callback) { + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null; + return d3_xhr(url, mimeType, response, callback); + }; +} + +function d3_xhr(url, mimeType, response, callback) { var xhr = {}, - dispatch = d3.dispatch("progress", "load", "error"), + dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, - response = d3_identity, - request = new (d3_window.XDomainRequest && /^(http(s)?:)?\/\//.test(url) ? XDomainRequest : XMLHttpRequest); + request = new XMLHttpRequest, + responseType = null; + + // If IE does not support CORS, use XDomainRequest. + if (this.XDomainRequest + && !("withCredentials" in request) + && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest; "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { request.readyState > 3 && respond(); }; function respond() { - var s = request.status; - !s && request.responseText || s >= 200 && s < 300 || s === 304 - ? dispatch.load.call(xhr, response.call(xhr, request)) - : dispatch.error.call(xhr, request); + var status = request.status, result; + if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { + try { + result = response.call(xhr, request); + } catch (e) { + dispatch.error.call(xhr, e); + return; + } + dispatch.load.call(xhr, result); + } else { + dispatch.error.call(xhr, request); + } } request.onprogress = function(event) { @@ -5216,6 +6129,14 @@ d3.xhr = function(url, mimeType, callback) { return xhr; }; + // Specifies what type the response value should take; + // for instance, arraybuffer, blob, document, or text. + xhr.responseType = function(value) { + if (!arguments.length) return responseType; + responseType = value; + return xhr; + }; + // Specify how to convert the response content to a specific type; // changes the callback value on "load" events. xhr.response = function(value) { @@ -5237,7 +6158,9 @@ d3.xhr = function(url, mimeType, callback) { if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (responseType != null) request.responseType = responseType; if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); }); + dispatch.beforesend.call(xhr, request); request.send(data == null ? null : data); return xhr; }; @@ -5249,7 +6172,6 @@ d3.xhr = function(url, mimeType, callback) { d3.rebind(xhr, dispatch, "on"); - if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, mimeType = null; return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); }; @@ -5259,16 +6181,19 @@ function d3_xhr_fixCallback(callback) { : callback; } -d3.text = function() { - return d3.xhr.apply(d3, arguments).response(d3_text); -}; +function d3_xhrHasResponse(request) { + var type = request.responseType; + return type && type !== "text" + ? request.response // null on error + : request.responseText; // "" on error +} -function d3_text(request) { +d3.text = d3_xhrType(function(request) { return request.responseText; -} +}); d3.json = function(url, callback) { - return d3.xhr(url, "application/json", callback).response(d3_json); + return d3_xhr(url, "application/json", d3_json, callback); }; function d3_json(request) { @@ -5276,7 +6201,7 @@ function d3_json(request) { } d3.html = function(url, callback) { - return d3.xhr(url, "text/html", callback).response(d3_html); + return d3_xhr(url, "text/html", d3_html, callback); }; function d3_html(request) { @@ -5285,76 +6210,95 @@ function d3_html(request) { return range.createContextualFragment(request.responseText); } -d3.xml = function() { - return d3.xhr.apply(d3, arguments).response(d3_xml); -}; - -function d3_xml(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 = []; + data = [], + suggestions = [], + minItems = 2, + caseSensitive = false; - var fetcher = function(val, data, cb) { + var fetcher = function(val, cb) { cb(data.filter(function(d) { - return d.title + return d.value .toString() .toLowerCase() .indexOf(val.toLowerCase()) !== -1; })); }; - var combobox = function(input) { - var idx = -1, container, shown = false; + var combobox = function(input, attachTo) { + var idx = -1, + container = d3.select(document.body) + .selectAll('div.combobox') + .filter(function(d) { return d === input.node(); }), + shown = !container.empty(); input .classed('combobox-input', true) + .on('focus.typeahead', focus) + .on('blur.typeahead', blur) + .on('keydown.typeahead', keydown) + .on('keyup.typeahead', keyup) + .on('input.typeahead', change) .each(function() { var parent = this.parentNode, sibling = this.nextSibling; - d3.select(parent) - .insert('div', function() { return sibling; }) - .attr('class', 'combobox-carat') + + var caret = d3.select(parent).selectAll('.combobox-caret') + .filter(function(d) { return d === input.node(); }) + .data([input.node()]); + + caret.enter().insert('div', function() { return sibling; }) + .attr('class', 'combobox-caret'); + + caret .on('mousedown', function () { // prevent the form element from blurring. it blurs // on mousedown d3.event.stopPropagation(); d3.event.preventDefault(); - mousedown(); + if (!shown) { + input.node().focus(); + fetch('', render); + } else { + hide(); + } }); }); - function updateSize() { - var rect = input.node().getBoundingClientRect(); - container.style({ - 'left': rect.left + 'px', - 'width': rect.width + 'px', - 'top': rect.height + rect.top + 'px' - }); + function focus() { + fetch(value(), render); } function blur() { - // hide the combobox whenever the input element - // loses focus - slowHide(); + window.setTimeout(hide, 150); } function show() { if (!shown) { container = d3.select(document.body) .insert('div', ':first-child') + .datum(input.node()) .attr('class', 'combobox') .style({ position: 'absolute', display: 'block', left: '0px' + }) + .on('mousedown', function () { + // prevent moving focus out of the text field + d3.event.preventDefault(); }); d3.select(document.body) - .on('scroll.combobox', updateSize, true); + .on('scroll.combobox', render, true); shown = true; } @@ -5372,24 +6316,35 @@ d3.combobox = function() { } } - function slowHide() { - window.setTimeout(hide, 150); - } function keydown() { - if (!shown) return; switch (d3.event.keyCode) { - // down arrow - case 40: - next(); + // backspace, delete + case 8: + case 46: + input.on('input.typeahead', function() { + idx = -1; + render(); + var start = input.property('selectionStart'); + input.node().setSelectionRange(start, start); + input.on('input.typeahead', change); + }); + break; + // tab + case 9: + container.selectAll('a.selected').each(event.accept); + break; + // return + case 13: d3.event.preventDefault(); break; // up arrow case 38: - prev(); + nav(-1); d3.event.preventDefault(); break; - // escape, tab - case 13: + // down arrow + case 40: + nav(+1); d3.event.preventDefault(); break; } @@ -5402,171 +6357,122 @@ d3.combobox = function() { case 27: hide(); break; - // escape, tab - case 9: + // return case 13: - if (!shown) return; - accept(); + container.selectAll('a.selected').each(event.accept); + hide(); break; - default: - update(); - d3.event.preventDefault(); - } - d3.event.stopPropagation(); - } - - function accept() { - if (container.select('a.selected').node()) { - select(container.select('a.selected').datum()); } - hide(); } - function next() { - var len = container.selectAll('a').data().length; - idx = Math.min(idx + 1, len - 1); - highlight(); + function change() { + fetch(value(), function() { + if (input.property('selectionEnd') === input.property('value').length) { + autocomplete(); + } + render(); + }); } - function prev() { - idx = Math.max(idx - 1, 0); - highlight(); + function nav(dir) { + if (!suggestions.length) return; + idx = Math.max(Math.min(idx + dir, suggestions.length - 1), 0); + input.property('value', suggestions[idx].value); + render(); + ensureVisible(); } - var prevValue, prevCompletion; - - function autocomplete(e, data) { - + function value() { var value = input.property('value'), - match; + start = input.property('selectionStart'), + end = input.property('selectionEnd'); - for (var i = 0; i < data.length; i++) { - if (data[i].value.toLowerCase().indexOf(value.toLowerCase()) === 0) { - match = data[i].value; - break; - } + if (start && end) { + value = value.substring(0, start); } - // backspace - if (e.keyCode === 8) { - prevValue = value; - prevCompletion = ''; + return value; + } - } else if (value && match && value !== prevValue + prevCompletion) { - prevValue = value; - prevCompletion = match.substr(value.length); - input.property('value', prevValue + prevCompletion); - input.node().setSelectionRange(value.length, value.length + prevCompletion.length); - } + function fetch(v, cb) { + fetcher.call(input, v, function(_) { + suggestions = _; + cb(); + }); } + function autocomplete() { + var v = caseSensitive ? value() : value().toLowerCase(); + idx = -1; + if (!v) return; - function highlight() { - container - .selectAll('a') - .classed('selected', function(d, i) { return i == idx; }); - var height = container.node().offsetHeight, - top = container.select('a.selected').node().offsetTop, - selectedHeight = container.select('a.selected').node().offsetHeight; - if ((top + selectedHeight) < height) { - container.node().scrollTop = 0; - } else { - container.node().scrollTop = top; + for (var i = 0; i < suggestions.length; i++) { + var suggestion = suggestions[i].value, + compare = caseSensitive ? suggestion : suggestion.toLowerCase(); + + if (compare.indexOf(v) === 0) { + idx = i; + input.property('value', suggestion); + input.node().setSelectionRange(v.length, suggestion.length); + return; + } } } - function update(value) { - - if (typeof value === 'undefined') { - value = input.property('value'); + function render() { + if (suggestions.length >= minItems && document.activeElement === input.node()) { + show(); + } else { + hide(); + return; } - var e = d3.event; - - function render(data) { + var options = container + .selectAll('a.combobox-option') + .data(suggestions, function(d) { return d.value; }); - if (data.length && - document.activeElement === input.node()) show(); - else return hide(); + options.enter().append('a') + .attr('class', 'combobox-option') + .text(function(d) { return d.value; }); - autocomplete(e, data); + options + .attr('title', function(d) { return d.title; }) + .classed('selected', function(d, i) { return i == idx; }) + .on('mouseover', select) + .on('click', accept) + .order(); - updateSize(); - - var options = container - .selectAll('a.combobox-option') - .data(data, function(d) { return d.value; }); + options.exit() + .remove(); - options.enter() - .append('a') - .text(function(d) { return d.value; }) - .attr('class', 'combobox-option') - .attr('title', function(d) { return d.title; }) - .on('click', select); + var node = attachTo ? attachTo.node() : input.node(), + rect = node.getBoundingClientRect(); - options.exit().remove(); + container.style({ + 'left': rect.left + 'px', + 'width': rect.width + 'px', + 'top': rect.height + rect.top + 'px' + }); + } - options - .classed('selected', function(d, i) { return i == idx; }) - .order(); - } + function select(d, i) { + idx = i; + render(); + } - fetcher.apply(input, [value, data, render]); + function ensureVisible() { + var node = container.selectAll('a.selected').node(); + if (node) node.scrollIntoView(); } - // select the choice given as d - function select(d) { + function accept(d) { + if (!shown) return; input .property('value', d.value) .trigger('change'); event.accept(d); hide(); } - - function mousedown() { - - if (shown) return hide(); - - input.node().focus(); - update(''); - - if (!container) return; - - var entries = container.selectAll('a'), - height = container.node().scrollHeight / entries[0].length, - w = d3.select(window); - - function getIndex(m) { - return Math.floor((m[1] + container.node().scrollTop) / height); - } - - function withinBounds(m) { - var n = container.node(); - return m[0] >= 0 && m[0] < n.offsetWidth && - m[1] >= 0 && m[1] < n.offsetHeight; - } - - w.on('mousemove.typeahead', function() { - var m = d3.mouse(container.node()); - var within = withinBounds(m); - var n = getIndex(m); - entries.classed('selected', function(d, i) { return within && i === n; }); - }); - - w.on('mouseup.typeahead', function() { - var m = d3.mouse(container.node()); - if (withinBounds(m)) select(d3.select(entries[0][getIndex(m)]).datum()); - entries.classed('selected', false); - w.on('mouseup.typeahead', null); - w.on('mousemove.typeahead', null); - }); - } - - input - .on('blur.typeahead', blur) - .on('keydown.typeahead', keydown) - .on('keyup.typeahead', keyup) - .on('mousedown.typeahead', mousedown); }; combobox.fetcher = function(_) { @@ -5581,10 +6487,40 @@ d3.combobox = function() { return combobox; }; + combobox.minItems = function(_) { + if (!arguments.length) return minItems; + minItems = _; + return combobox; + }; + + combobox.caseSensitive = function(_) { + if (!arguments.length) return caseSensitive; + caseSensitive = _; + return combobox; + }; + return d3.rebind(combobox, event, 'on'); }; -d3.combobox.id = 0; +d3.combobox.off = function(input) { + data = null; + fetcher = null; + + input + .on('focus.typeahead', null) + .on('blur.typeahead', null) + .on('keydown.typeahead', null) + .on('keyup.typeahead', null) + .on('input.typeahead', null) + .each(function() { + d3.select(this.parentNode).selectAll('.combobox-caret') + .filter(function(d) { return d === input.node(); }) + .on('mousedown', null); + }); + + d3.select(document.body) + .on('scroll.combobox', null); +}; d3.geo.tile = function() { var size = [960, 500], scale = 256, @@ -5691,25 +6627,29 @@ d3.keybinding = function(namespace) { if (event[p] != binding.event[p]) return false; } - - return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE); + return true; } - function capture() { + function testBindings(isCapturing) { for (var i = 0; i < bindings.length; i++) { var binding = bindings[i]; - if (matches(binding, d3.event)) { + + if (!!binding.capture === isCapturing && matches(binding, d3.event)) { binding.callback(); } } } + function capture() { + testBindings(true); + } + function bubble() { var tagName = d3.select(d3.event.target).node().tagName; if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') { return; } - capture(); + testBindings(false); } function keybinding(selection) { @@ -5720,6 +6660,7 @@ d3.keybinding = function(namespace) { } keybinding.off = function(selection) { + bindings = []; selection = selection || d3.select(document); selection.on('keydown.capture' + namespace, null); selection.on('keydown.bubble' + namespace, null); @@ -5815,14 +6756,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 Plus + 'ffplus': 171, + // Firefox Minus + 'ffminus': 173, // Semicolon - ';': 186, semicolon:186, + ';': 186, semicolon: 186, // = or equals '=': 187, 'equals': 187, // Comma, or , @@ -5880,13 +6827,28 @@ d3.selection.prototype.one = function (type, listener, capture) { target.on(typeOnce, one, capture); return this; }; -d3.selection.prototype.size = function (size) { +d3.selection.prototype.dimensions = function (dimensions) { + var refresh = (function(node) { + var cr = node.getBoundingClientRect(); + prop = [cr.width, cr.height]; + this.property('__dimensions__', prop); + return prop; + }).bind(this); + + var node = this.node(); + if (!arguments.length) { - var node = this.node(); - return [node.offsetWidth, - node.offsetHeight]; + if (!node) return [0,0]; + return this.property('__dimensions__') || refresh(node); } - return this.attr({width: size[0], height: size[1]}); + if (dimensions === null) { + if (!node) return [0,0]; + return refresh(node); + } + + return this + .property('__dimensions__', [dimensions[0], dimensions[1]]) + .attr({width: dimensions[0], height: dimensions[1]}); }; d3.selection.prototype.trigger = function (type) { this.each(function() { @@ -5895,124 +6857,8 @@ d3.selection.prototype.trigger = function (type) { this.dispatchEvent(evt); }); }; -d3.typeahead = function() { - var event = d3.dispatch('accept'), - autohighlight = false, - data; - - var typeahead = function(selection) { - var container, - hidden, - idx = autohighlight ? 0 : -1; - - function setup() { - var rect = selection.node().getBoundingClientRect(); - container = d3.select(document.body) - .append('div').attr('class', 'typeahead') - .style({ - position: 'absolute', - left: rect.left + 'px', - top: rect.bottom + 'px' - }); - selection - .on('keyup.typeahead', key); - hidden = false; - } - - function hide() { - container.remove(); - idx = autohighlight ? 0 : -1; - hidden = true; - } - - function slowHide() { - if (autohighlight) { - if (container.select('a.selected').node()) { - select(container.select('a.selected').datum()); - event.accept(); - } - } - window.setTimeout(hide, 150); - } - - selection - .on('focus.typeahead', setup) - .on('blur.typeahead', slowHide); - - function key() { - var len = container.selectAll('a').data().length; - if (d3.event.keyCode === 40) { - idx = Math.min(idx + 1, len - 1); - return highlight(); - } else if (d3.event.keyCode === 38) { - idx = Math.max(idx - 1, 0); - return highlight(); - } else if (d3.event.keyCode === 13) { - if (container.select('a.selected').node()) { - select(container.select('a.selected').datum()); - } - event.accept(); - hide(); - } else { - update(); - } - } - - function highlight() { - container - .selectAll('a') - .classed('selected', function(d, i) { return i == idx; }); - } - - function update() { - if (hidden) setup(); - - data(selection, function(data) { - container.style('display', function() { - return data.length ? 'block' : 'none'; - }); - - var options = container - .selectAll('a') - .data(data, function(d) { return d.value; }); - - options.enter() - .append('a') - .text(function(d) { return d.value; }) - .attr('title', function(d) { return d.title; }) - .on('click', select); - - options.exit().remove(); - - options - .classed('selected', function(d, i) { return i == idx; }); - }); - } - - function select(d) { - selection - .property('value', d.value) - .trigger('change'); - } - - }; - - typeahead.data = function(_) { - if (!arguments.length) return data; - data = _; - return typeahead; - }; - - typeahead.autohighlight = function(_) { - if (!arguments.length) return autohighlight; - autohighlight = _; - return typeahead; - }; - - return d3.rebind(typeahead, event, 'on'); -}; -// Tooltips and svg mask used to highlight certain features -d3.curtain = function() { +// Tooltips and svg mask used to highlight certain features +d3.curtain = function() { var event = d3.dispatch(), surface, @@ -6070,10 +6916,10 @@ d3.curtain = function() { var html = parts[0] ? '' + parts[0] + '' : ''; if (parts[1]) html += '' + parts[1] + ''; - var size = tooltip.classed('in', true) + var dimensions = tooltip.classed('in', true) .select('.tooltip-inner') .html(html) - .size(); + .dimensions(); var pos; @@ -6082,23 +6928,23 @@ d3.curtain = function() { if (box.top + box.height < Math.min(100, box.width + box.left)) { side = 'bottom'; - pos = [box.left + box.width / 2 - size[0]/ 2, box.top + box.height]; + pos = [box.left + box.width / 2 - dimensions[0]/ 2, box.top + box.height]; } else if (box.left + box.width + 300 < window.innerWidth) { side = 'right'; - pos = [box.left + box.width, box.top + box.height / 2 - size[1] / 2]; + pos = [box.left + box.width, box.top + box.height / 2 - dimensions[1] / 2]; } else if (box.left > 300) { side = 'left'; - pos = [box.left - 200, box.top + box.height / 2 - size[1] / 2]; + pos = [box.left - 200, box.top + box.height / 2 - dimensions[1] / 2]; } else { side = 'bottom'; pos = [box.left, box.top + box.height]; } pos = [ - Math.min(Math.max(10, pos[0]), w - size[0] - 10), - Math.min(Math.max(10, pos[1]), h - size[1] - 10) + Math.min(Math.max(10, pos[0]), w - dimensions[0] - 10), + Math.min(Math.max(10, pos[1]), h - dimensions[1] - 10) ]; @@ -6142,6 +6988,463 @@ d3.curtain = function() { return d3.rebind(curtain, event, 'on'); }; +// Like selection.property('value', ...), but avoids no-op value sets, +// which can result in layout/repaint thrashing in some situations. +d3.selection.prototype.value = function(value) { + function d3_selection_value(value) { + function valueNull() { + delete this.value; + } + + function valueConstant() { + if (this.value !== value) this.value = value; + } + + function valueFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this.value; + else if (this.value !== x) this.value = x; + } + + return value == null + ? valueNull : (typeof value === "function" + ? valueFunction : valueConstant); + } + + 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 */ @@ -6283,2941 +7586,2136 @@ 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. - * Available under MIT license +/** + * @license + * lodash (Custom Build) + * Build: `lodash --development --output js/lib/lodash.js include="includes,toPairs,assign,bind,chunk,clone,compact,debounce,difference,each,every,extend,filter,find,first,forEach,forOwn,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,reject,some,throttle,union,uniq,values,without,flatten,value,chain,cloneDeep,merge,pick,reduce" exports="global,node"` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ -;(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; - } - - /** Used for array and object method references */ - var arrayRef = [], - // avoid a Closure Compiler bug by creatively creating an object - objectRef = new function(){}; - - /** Used to generate unique IDs */ - var idCounter = 0; - - /** 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; +;(function() { - /** Used to restore the original `_` reference in `noConflict` */ - var oldDash = window._; + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '4.12.0'; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as the internal argument placeholder. */ + var PLACEHOLDER = '__lodash_placeholder__'; + + /** Used to compose bitmasks for wrapper metadata. */ + var BIND_FLAG = 1, + BIND_KEY_FLAG = 2, + CURRY_BOUND_FLAG = 4, + CURRY_FLAG = 8, + CURRY_RIGHT_FLAG = 16, + PARTIAL_FLAG = 32, + PARTIAL_RIGHT_FLAG = 64, + ARY_FLAG = 128, + REARG_FLAG = 256, + FLIP_FLAG = 512; + + /** Used to compose bitmasks for comparison styles. */ + var UNORDERED_COMPARE_FLAG = 1, + PARTIAL_COMPARE_FLAG = 2; + + /** Used to detect hot functions by number of calls within a span of milliseconds. */ + var HOT_COUNT = 150, + HOT_SPAN = 16; + + /** Used to indicate the type of lazy iteratees. */ + var LAZY_FILTER_FLAG = 1, + LAZY_MAP_FLAG = 2, + LAZY_WHILE_FLAG = 3; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + + /** Used as references for the maximum length and index of an array. */ + var MAX_ARRAY_LENGTH = 4294967295; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + weakMapTag = '[object WeakMap]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]/g; - /** Used to detect template delimiter values that require a with-statement */ - var reComplexDelimiter = /[-?+=!~*%&^<>|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/; + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; - /** Used to match HTML entities */ - var reEscapedHtml = /&(?:amp|lt|gt|quot|#x27);/g; + /** Used to match leading and trailing whitespace. */ + var reTrim = /^\s+|\s+$/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 to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; - /** Used to match regexp flags from their coerced string values */ + /** 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 ensure capturing order of template delimiters */ - var reNoMatch = /($^)/; + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary = /^0b[01]+$/i; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect octal string values. */ + var reIsOctal = /^0o[0-7]+$/i; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to compose unicode character classes. */ + var rsAstralRange = '\\ud800-\\udfff', + rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23', + rsComboSymbolsRange = '\\u20d0-\\u20f0', + rsVarRange = '\\ufe0e\\ufe0f'; + + /** Used to compose unicode capture groups. */ + var rsAstral = '[' + rsAstralRange + ']', + rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']', + rsFitz = '\\ud83c[\\udffb-\\udfff]', + rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', + rsNonAstral = '[^' + rsAstralRange + ']', + rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', + rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', + rsZWJ = '\\u200d'; + + /** Used to compose unicode regexes. */ + var reOptMod = rsModifier + '?', + rsOptVar = '[' + rsVarRange + ']?', + rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', + rsSeq = rsOptVar + reOptMod + rsOptJoin, + rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; + + /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ + var reComplexSymbol = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = + cloneableTags[boolTag] = cloneableTags[dateTag] = + cloneableTags[float32Tag] = cloneableTags[float64Tag] = + cloneableTags[int8Tag] = cloneableTags[int16Tag] = + cloneableTags[int32Tag] = cloneableTags[mapTag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[setTag] = + cloneableTags[stringTag] = cloneableTags[symbolTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[weakMapTag] = false; + + /** Used to determine if values are of the language type `Object`. */ + var objectTypes = { + 'function': true, + 'object': true + }; - /** Used to match HTML characters */ - var reUnescapedHtml = /[&<>"']/g; + /** Built-in method references without a dependency on `root`. */ + var freeParseInt = parseInt; - /** Used to match unescaped characters in compiled string literals */ - var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + /** Detect free variable `exports`. */ + var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) + ? exports + : undefined; - /** Used to fix the JScript [[DontEnum]] bug */ - var shadowed = [ - 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', - 'toLocaleString', 'toString', 'valueOf' - ]; + /** Detect free variable `module`. */ + var freeModule = (objectTypes[typeof module] && module && !module.nodeType) + ? module + : undefined; - /** 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; + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = (freeModule && freeModule.exports === freeExports) + ? freeExports + : undefined; - /** `Object#toString` result shortcuts */ - var argsClass = '[object Arguments]', - arrayClass = '[object Array]', - boolClass = '[object Boolean]', - dateClass = '[object Date]', - funcClass = '[object Function]', - numberClass = '[object Number]', - objectClass = '[object Object]', - regexpClass = '[object RegExp]', - stringClass = '[object String]'; + /** Detect free variable `global` from Node.js. */ + var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global); - /** Detect various environments */ - var isIeOpera = !!window.attachEvent, - isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera); + /** Detect free variable `self`. */ + var freeSelf = checkGlobal(objectTypes[typeof self] && self); - /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ - var isBindFast = nativeBind && !isV8; + /** Detect free variable `window`. */ + var freeWindow = checkGlobal(objectTypes[typeof window] && window); - /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ - var isKeysFast = nativeKeys && (isIeOpera || isV8); + /** Detect `this` as the global object. */ + var thisGlobal = checkGlobal(objectTypes[typeof this] && this); /** - * Detect the JScript [[DontEnum]] bug: + * Used as a reference to the global object. * - * In IE < 9 an objects own properties, shadowing non-enumerable ones, are - * made non-enumerable as well. + * The `this` value is used if it's the global object to avoid Greasemonkey's + * restricted `window` object, otherwise the `window` object is used. */ - var hasDontEnumBug; + var root = freeGlobal || + ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || + freeSelf || thisGlobal || Function('return this')(); - /** 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: + * Adds the key-value `pair` to `map`. * - * 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. + * @private + * @param {Object} map The map to modify. + * @param {Array} pair The key-value pair to add. + * @returns {Object} Returns `map`. */ - 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); + function addMapEntry(map, pair) { + // Don't return `Map#set` because it doesn't return the map instance in IE 11. + map.set(pair[0], pair[1]); + return map; + } /** - * Detect lack of support for accessing string characters by index: + * Adds `value` to `set`. * - * IE < 8 can't access characters by index and IE 8 can only access - * characters by index on string literals. + * @private + * @param {Object} set The set to modify. + * @param {*} value The value to add. + * @returns {Object} Returns `set`. */ - var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + function addSetEntry(set, value) { + set.add(value); + return set; + } /** - * 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". + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. */ - try { - var noNodeClass = ({ 'toString': 0 } + '', toString.call(document) == objectClass); - } catch(e) { } + function apply(func, thisArg, args) { + var length = args.length; + switch (length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); + } /** - * 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. + * A specialized version of `baseAggregator` for arrays. * - * 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 + * @private + * @param {Array} array The array to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. */ - try { - var useSourceURL = (Function('//@')(), !isIeOpera); - } catch(e) { } - - /** Used to identify object classifications that `_.clone` supports */ - var cloneableClasses = {}; - cloneableClasses[funcClass] = false; - cloneableClasses[argsClass] = cloneableClasses[arrayClass] = - cloneableClasses[boolClass] = cloneableClasses[dateClass] = - cloneableClasses[numberClass] = cloneableClasses[objectClass] = - cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; - - /** Used 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 to determine if values are of the language type Object */ - var objectTypes = { - 'boolean': false, - 'function': true, - 'object': true, - 'number': false, - 'string': false, - 'undefined': false - }; - - /** Used to escape characters for inclusion in compiled string literals */ - var stringEscapes = { - '\\': '\\', - "'": "'", - '\n': 'n', - '\r': 'r', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; + function arrayAggregator(array, setter, iteratee, accumulator) { + var index = -1, + length = array.length; - /*--------------------------------------------------------------------------*/ + while (++index < length) { + var value = array[index]; + setter(accumulator, value, iteratee(value), array); + } + return accumulator; + } /** - * Creates a `lodash` object, that wraps the given `value`, to enable - * method chaining. + * A specialized version of `_.forEach` for arrays without support for + * iteratee shorthands. * - * 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. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. */ - 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); + function arrayEach(array, iteratee) { + var index = -1, + length = array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } } - this.__wrapped__ = value; + return array; } /** - * 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. + * A specialized version of `_.every` for arrays without support for + * iteratee shorthands. * - * @static - * @memberOf _ - * @type Object + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. */ - lodash.templateSettings = { - - /** - * Used to detect `data` property values to be HTML-escaped. - * - * @static - * @memberOf _.templateSettings - * @type RegExp - */ - 'escape': /<%-([\s\S]+?)%>/g, + function arrayEvery(array, predicate) { + var index = -1, + length = array.length; - /** - * 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': '' - }; - - /*--------------------------------------------------------------------------*/ + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } /** - * The template used to create iterator functions. + * A specialized version of `_.filter` for arrays without support for + * iteratee shorthands. * * @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' - ); + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; - /** 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}' - }; + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; + } /** - * Reusable iterator options shared by `each`, `forIn`, and `forOwn`. + * A specialized version of `_.includes` for arrays without support for + * specifying an index to search from. + * + * @private + * @param {Array} array The array to search. + * @param {*} target The value to search for. + * @returns {boolean} Returns `true` if `target` is found, else `false`. */ - 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' - }; - - /** Reusable iterator options for `forIn` and `forOwn` */ - var forOwnIteratorOptions = { - 'arrayLoop': null - }; - - /*--------------------------------------------------------------------------*/ + function arrayIncludes(array, value) { + return !!array.length && baseIndexOf(array, value, 0) > -1; + } /** - * Creates a function optimized to search large arrays for a given `value`, - * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`. + * This function is like `arrayIncludes` except that it accepts a comparator. * * @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 {*} target The value to search for. + * @param {Function} comparator The comparator invoked per element. + * @returns {boolean} Returns `true` if `target` is found, else `false`. */ - 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 arrayIncludesWith(array, value, comparator) { + var index = -1, + length = array.length; - 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 (comparator(value, array[index])) { + return true; } - return indexOf(array, value, fromIndex) > -1; } + return false; } /** - * Used by `_.max` and `_.min` as the default `callback` when a given - * `collection` is a string value. + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. * * @private - * @param {String} value The character to inspect. - * @returns {Number} Returns the code unit of given character. + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. */ - function charAtCallback(value) { - return value.charCodeAt(0); + function arrayMap(array, iteratee) { + var index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; } /** - * Used by `sortBy` to compare transformed `collection` values, stable sorting - * them in ascending order. + * Appends the elements of `values` to `array`. * * @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 {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. */ - function compareAscending(a, b) { - var ai = a.index, - bi = b.index; - - a = a.criteria; - b = b.criteria; + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; - // 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; - } + while (++index < length) { + array[offset + index] = values[index]; } - return ai < bi ? -1 : 1; + return array; } /** - * 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. + * A specialized version of `_.reduce` for arrays without support for + * iteratee shorthands. * * @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 {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the first element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. */ - function createBound(func, thisArg, partialArgs) { - var isFunc = isFunction(func), - isPartial = !partialArgs, - key = thisArg; + function arrayReduce(array, iteratee, accumulator, initAccum) { + var index = -1, + length = array.length; - // juggle arguments - if (isPartial) { - partialArgs = thisArg; + if (initAccum && length) { + accumulator = array[++index]; } - if (!isFunc) { - thisArg = func; + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); } + return accumulator; + } - function bound() { - // `Function#bind` spec - // http://es5.github.com/#x15.3.4.5 - var args = arguments, - thisBinding = isPartial ? this : thisArg; + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array.length; - if (!isFunc) { - func = thisArg[key]; - } - if (partialArgs.length) { - args = args.length - ? partialArgs.concat(slice(args)) - : partialArgs; - } - if (this instanceof bound) { - // ensure `new bound` is an instance of `bound` and `func` - noop.prototype = func.prototype; - thisBinding = new noop; - noop.prototype = null; - - // mimic the constructor's `return` behavior - // http://es5.github.com/#x13.2.2 - var result = func.apply(thisBinding, args); - return isObject(result) ? result : thisBinding; + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; } - return func.apply(thisBinding, args); } - return bound; + return false; } /** - * 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. + * The base implementation of methods like `_.find` and `_.findKey`, without + * support for iteratee shorthands, which iterates over `collection` using + * `eachFunc`. * * @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. - */ - 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); - }; + * @param {Array|Object} collection The collection to search. + * @param {Function} predicate The function invoked per iteration. + * @param {Function} eachFunc The function to iterate over `collection`. + * @param {boolean} [retKey] Specify returning the key of the found element + * instead of the element itself. + * @returns {*} Returns the found element or its key, else `undefined`. + */ + function baseFind(collection, predicate, eachFunc, retKey) { + var result; + eachFunc(collection, function(value, key, collection) { + if (predicate(value, key, collection)) { + result = retKey ? key : value; + return false; } - return function(value, index, object) { - return func.call(thisArg, value, index, object); - }; - } - return func; + }); + return result; } /** - * Creates compiled iteration functions. + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for iteratee shorthands. * * @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 - }; - - // 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]; + * @param {Array} array The array to search. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return 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 -1; } /** - * 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`. + * The base implementation of `_.indexOf` without `fromIndex` bounds checks. * * @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`. + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. */ - var each = createIterator(eachIteratorOptions); + function baseIndexOf(array, value, fromIndex) { + if (value !== value) { + return indexOfNaN(array, fromIndex); + } + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } /** - * Used by `template` to escape characters for inclusion in compiled - * string literals. + * The base implementation of `_.reduce` and `_.reduceRight`, without support + * for iteratee shorthands, which iterates over `collection` using `eachFunc`. * * @private - * @param {String} match The matched character to escape. - * @returns {String} Returns the escaped character. + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} accumulator The initial value. + * @param {boolean} initAccum Specify using the first or last element of + * `collection` as the initial value. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the accumulated value. */ - function escapeStringChar(match) { - return '\\' + stringEscapes[match]; + function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { + eachFunc(collection, function(value, index, collection) { + accumulator = initAccum + ? (initAccum = false, value) + : iteratee(accumulator, value, index, collection); + }); + return accumulator; } /** - * Used by `escape` to convert characters to HTML entities. + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. * * @private - * @param {String} match The matched character to escape. - * @returns {String} Returns the escaped character. + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. */ - function escapeHtmlChar(match) { - return htmlEscapes[match]; + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; } /** - * Checks if `value` is a DOM node in IE < 9. + * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array + * of key-value pairs for `object` corresponding to the property names of `props`. * * @private - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a DOM node, else `false`. + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the key-value pairs. */ - function isNode(value) { - // IE < 9 presents DOM nodes as `Object` objects except they have `toString` - // methods that are `typeof` "string" and still can coerce nodes to strings - return typeof value.toString != 'function' && typeof (value + '') == 'string'; + function baseToPairs(object, props) { + return arrayMap(props, function(key) { + return [key, object[key]]; + }); } /** - * A no-operation function. + * The base implementation of `_.unary` without support for storing wrapper metadata. * * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. */ - function noop() { - // no operation performed + function baseUnary(func) { + return function(value) { + return func(value); + }; } /** - * Slices the `collection` from the `start` index up to, but not including, - * the `end` index. - * - * Note: This function is used, instead of `Array#slice`, to support node lists - * in IE < 9 and to ensure dense arrays are returned. + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. * * @private - * @param {Array|Object|String} collection The collection to slice. - * @param {Number} start The start index. - * @param {Number} end The end index. - * @returns {Array} Returns the new array. + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. */ - function slice(array, start, end) { - start || (start = 0); - if (typeof end == 'undefined') { - end = array ? array.length : 0; - } - var index = -1, - length = end - start || 0, - result = Array(length < 0 ? 0 : length); - - while (++index < length) { - result[index] = array[start + index]; - } - return result; + function baseValues(object, props) { + return arrayMap(props, function(key) { + return object[key]; + }); } /** - * Used by `unescape` to convert HTML entities to characters. + * Checks if a cache value for `key` exists. * * @private - * @param {String} match The matched character to unescape. - * @returns {String} Returns the unescaped character. + * @param {Object} cache The cache to query. + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ - function unescapeHtmlChar(match) { - return htmlUnescapes[match]; + function cacheHas(cache, key) { + return cache.has(key); } - /*--------------------------------------------------------------------------*/ - /** - * Assigns own enumerable properties of source object(s) to the `destination` - * object. Subsequent sources will overwrite propery assignments of previous - * sources. - * - * @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 + * Checks if `value` is a global object. * - * _.assign({ 'name': 'moe' }, { 'age': 40 }); - * // => { 'name': 'moe', 'age': 40 } + * @private + * @param {*} value The value to check. + * @returns {null|Object} Returns `value` if it's a global object, else `null`. */ - var assign = createIterator(assignIteratorOptions); + function checkGlobal(value) { + return (value && value.Object === Object) ? value : null; + } /** - * 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 an `arguments` object, else `false`. - * @example - * - * (function() { return _.isArguments(arguments); })(1, 2, 3); - * // => true + * Gets the number of `placeholder` occurrences in `array`. * - * _.isArguments([1, 2, 3]); - * // => false + * @private + * @param {Array} array The array to inspect. + * @param {*} placeholder The placeholder to search for. + * @returns {number} Returns the placeholder count. */ - 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; - }; + function countHolders(array, placeholder) { + var length = array.length, + result = 0; + + while (length--) { + if (array[length] === placeholder) { + result++; + } + } + return result; } /** - * 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`. - * - * @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 - * - * function Dog(name) { - * this.name = name; - * } - * - * Dog.prototype.bark = function() { - * alert('Woof, woof!'); - * }; + * Gets the index at which the first occurrence of `NaN` is found in `array`. * - * _.forIn(new Dog('Dagny'), function(value, key) { - * alert(key); - * }); - * // => alerts 'name' and 'bark' (order is not guaranteed) + * @private + * @param {Array} array The array to search. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched `NaN`, else `-1`. */ - var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { - 'useHas': false - }); + function indexOfNaN(array, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 0 : -1); - /** - * 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 - * - * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * alert(key); - * }); - * // => alerts '0', '1', and 'length' (order is not guaranteed) - */ - var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); + while ((fromRight ? index-- : ++index < length)) { + var other = array[index]; + if (other !== other) { + return index; + } + } + return -1; + } /** - * 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. + * Checks if `value` is a host object in IE < 9. * * @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 check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. */ - function shimIsPlainObject(value) { - // avoid non-objects and false positives for `arguments` objects + function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. 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); + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} } return result; } /** - * A fallback implementation of `Object.keys` that produces an array of the - * given object's own enumerable property names. + * Converts `iterator` to an array. * * @private - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. + * @param {Object} iterator The iterator to convert. + * @returns {Array} Returns the converted array. */ - function shimKeys(object) { - var result = []; - forOwn(object, function(value, key) { - result.push(key); - }); + function iteratorToArray(iterator) { + var data, + result = []; + + while (!(data = iterator.next()).done) { + result.push(data.value); + } return result; } /** - * Used to convert characters to HTML entities: + * Converts `map` to its key-value pairs. * - * 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} map The map to convert. + * @returns {Array} Returns the key-value pairs. */ - var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - - /** Used to convert HTML entities to characters */ - var htmlUnescapes = invert(htmlEscapes); + function mapToArray(map) { + var index = -1, + result = Array(map.size); - /*--------------------------------------------------------------------------*/ + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } /** - * 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 + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. * - * var deep = _.clone(stooges, true); - * deep[0] === stooges[0]; - * // => false + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. */ - function clone(value, deep, guard, stackA, stackB) { - if (value == null) { - return value; - } - if (guard) { - deep = false; - } - // inspect [[Class]] - var isObj = isObject(value); - if (isObj) { - var className = toString.call(value); - if (!cloneableClasses[className] || (noNodeClass && 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: - return ctor(value.source, reFlags.exec(value)); - } - // check for circular references and return corresponding clone - stackA || (stackA = []); - stackB || (stackB = []); + function replaceHolders(array, placeholder) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; - var length = stackA.length; - while (length--) { - if (stackA[length] == value) { - return stackB[length]; + while (++index < length) { + var value = array[index]; + if (value === placeholder || value === PLACEHOLDER) { + array[index] = PLACEHOLDER; + result[resIndex++] = index; } } - // init cloned object - var result = isArr ? ctor(value.length) : {}; + return result; + } - // add the source value to the stack of traversed objects - // and associate it with its clone - stackA.push(value); - stackB.push(result); + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); - // recursively populate clone (susceptible to call stack limits) - (isArr ? forEach : forOwn)(value, function(objValue, key) { - result[key] = clone(objValue, deep, null, stackA, stackB); + set.forEach(function(value) { + result[++index] = 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; - } - } 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 + * Converts `set` to its value-value pairs. * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * var deep = _.cloneDeep(stooges); - * deep[0] === stooges[0]; - * // => false + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the value-value pairs. */ - function cloneDeep(value) { - return clone(value, true); + function setToPairs(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = [value, value]; + }); + return result; } /** - * 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 + * Converts `string` to an array. * - * var iceCream = { 'flavor': 'chocolate' }; - * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); - * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. */ - var defaults = createIterator(assignIteratorOptions, { - 'objectLoop': 'if (result[index] == null) ' + assignIteratorOptions.objectLoop - }); + function stringToArray(string) { + return string.match(reComplexSymbol); + } + + /*--------------------------------------------------------------------------*/ + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + objectProto = Object.prototype; + + /** Used to resolve the decompiled source of functions. */ + var funcToString = Function.prototype.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); /** - * 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 - * - * _.functions(_); - * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. */ - function functions(object) { - var result = []; - forIn(object, function(value, key) { - if (isFunction(value)) { - result.push(key); - } - }); - return result.sort(); - } + var objectToString = objectProto.toString; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Buffer = moduleExports ? root.Buffer : undefined, + Reflect = root.Reflect, + Symbol = root.Symbol, + Uint8Array = root.Uint8Array, + enumerate = Reflect ? Reflect.enumerate : undefined, + getOwnPropertySymbols = Object.getOwnPropertySymbols, + iteratorSymbol = typeof (iteratorSymbol = Symbol && Symbol.iterator) == 'symbol' ? iteratorSymbol : undefined, + objectCreate = Object.create, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeGetPrototype = Object.getPrototypeOf, + nativeKeys = Object.keys, + nativeMax = Math.max, + nativeMin = Math.min, + nativeReverse = arrayProto.reverse; + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(root, 'DataView'), + Map = getNative(root, 'Map'), + Promise = getNative(root, 'Promise'), + Set = getNative(root, 'Set'), + WeakMap = getNative(root, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to store function metadata. */ + var metaMap = WeakMap && new WeakMap; + + /** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */ + var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf'); + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /*------------------------------------------------------------------------*/ /** - * Checks if the specified object `property` exists and is a direct property, - * instead of an inherited property. + * Creates a `lodash` object which wraps `value` to enable implicit method + * chain sequences. Methods that operate on and return arrays, collections, + * and functions can be chained together. Methods that retrieve a single value + * or may return a primitive value will automatically end the chain sequence + * and return the unwrapped value. Otherwise, the value must be unwrapped + * with `_#value`. + * + * Explicit chain sequences, which must be unwrapped with `_#value`, may be + * enabled using `_.chain`. + * + * The execution of chained methods is lazy, that is, it's deferred until + * `_#value` is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. + * Shortcut fusion is an optimization to merge iteratee calls; this avoids + * the creation of intermediate arrays and can greatly reduce the number of + * iteratee executions. Sections of a chain sequence qualify for shortcut + * fusion if the section is applied to an array of at least `200` elements + * and any iteratees accept only one argument. The heuristic for whether a + * section qualifies for shortcut fusion is subject to change. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, + * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, + * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, + * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, + * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, + * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, + * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, + * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, + * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, + * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, + * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, + * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, + * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, + * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, + * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, + * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, + * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, + * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, + * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, + * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, + * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, + * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, + * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, + * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, + * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, + * `zipObject`, `zipObjectDeep`, and `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, + * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `deburr`, `divide`, `each`, + * `eachRight`, `endsWith`, `eq`, `escape`, `escapeRegExp`, `every`, `find`, + * `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `first`, + * `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, + * `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`, + * `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`, + * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`, + * `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`, + * `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`, + * `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`, + * `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`, + * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`, + * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`, + * `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, `min`, `minBy`, `multiply`, + * `noConflict`, `noop`, `now`, `nth`, `pad`, `padEnd`, `padStart`, `parseInt`, + * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`, + * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, + * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`, + * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toFinite`, + * `toInteger`, `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`, + * `toString`, `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`, + * `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words` * - * @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`. + * @name _ + * @constructor + * @category Seq + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. * @example * - * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); - * // => true - */ - function has(object, property) { - return object ? hasOwnProperty.call(object, property) : false; - } - - /** - * Creates an object composed of the inverted keys and values of the given `object`. + * function square(n) { + * return n * n; + * } * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to invert. - * @returns {Object} Returns the created inverted object. - * @example + * var wrapped = _([1, 2, 3]); * - * _.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; - } - - /** - * Checks if `value` is an array. + * // Returns an unwrapped value. + * wrapped.reduce(_.add); + * // => 6 * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. - * @example + * // Returns a wrapped value. + * var squares = wrapped.map(square); * - * (function() { return _.isArray(arguments); })(); + * _.isArray(squares); * // => false * - * _.isArray([1, 2, 3]); + * _.isArray(squares.value()); * // => true */ - 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 lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); + } /** - * 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 + * The function whose prototype chain sequence wrappers inherit from. * - * _.isBoolean(null); - * // => false + * @private */ - function isBoolean(value) { - return value === true || value === false || toString.call(value) == boolClass; + function baseLodash() { + // No operation performed. } /** - * 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 constructor for creating `lodash` wrapper objects. * - * _.isDate(new Date); - * // => true + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable explicit method chain sequences. */ - function isDate(value) { - return value instanceof Date || toString.call(value) == dateClass; + function LodashWrapper(value, chainAll) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__chain__ = !!chainAll; + this.__index__ = 0; + this.__values__ = undefined; } + // Ensure wrappers are instances of `baseLodash`. + lodash.prototype = baseLodash.prototype; + lodash.prototype.constructor = lodash; + + LodashWrapper.prototype = baseCreate(baseLodash.prototype); + LodashWrapper.prototype.constructor = LodashWrapper; + + /*------------------------------------------------------------------------*/ + /** - * 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 + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. * - * _.isElement(document.body); - * // => true + * @private + * @constructor + * @param {*} value The value to wrap. */ - function isElement(value) { - return value ? value.nodeType === 1 : false; + function LazyWrapper(value) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__dir__ = 1; + this.__filtered__ = false; + this.__iteratees__ = []; + this.__takeCount__ = MAX_ARRAY_LENGTH; + this.__views__ = []; } /** - * 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 + * Creates a clone of the lazy wrapper object. * - * _.isEmpty({}); - * // => true - * - * _.isEmpty(''); - * // => true + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. */ - function isEmpty(value) { - var result = true; - if (!value) { - return result; - } - var className = toString.call(value), - length = value.length; - - if ((className == arrayClass || className == stringClass || - className == argsClass || (noArgsClass && isArguments(value))) || - (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { - return !length; - } - forOwn(value, function() { - return (result = false); - }); + function lazyClone() { + var result = new LazyWrapper(this.__wrapped__); + result.__actions__ = copyArray(this.__actions__); + result.__dir__ = this.__dir__; + result.__filtered__ = this.__filtered__; + result.__iteratees__ = copyArray(this.__iteratees__); + result.__takeCount__ = this.__takeCount__; + result.__views__ = copyArray(this.__views__); 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 - * - * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * - * moe == clone; - * // => false + * Reverses the direction of lazy iteration. * - * _.isEqual(moe, clone); - * // => true + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. */ - function isEqual(a, b, stackA, stackB) { - // 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` - if (a == null || b == null) { - return a === b; + function lazyReverse() { + if (this.__filtered__) { + var result = new LazyWrapper(this); + result.__dir__ = -1; + result.__filtered__ = true; + } else { + result = this.clone(); + result.__dir__ *= -1; } - // compare [[Class]] names - var className = toString.call(a), - otherName = toString.call(b); + return result; + } - if (className == argsClass) { - className = objectClass; - } - if (otherName == argsClass) { - otherName = objectClass; - } - if (className != otherName) { - return false; - } - switch (className) { - case boolClass: - case dateClass: - // coerce dates and booleans to numbers, dates to milliseconds and booleans - // to `1` or `0`, treating invalid dates coerced to `NaN` as not equal - return +a == +b; - - case numberClass: - // treat `NaN` vs. `NaN` as equal - return a != +a - ? b != +b - // but treat `+0` vs. `-0` as not equal - : (a == 0 ? (1 / a == 1 / b) : a == +b); - - case regexpClass: - case stringClass: - // coerce regexes to strings (http://es5.github.com/#x15.10.6.4) - // treat string primitives and their corresponding object instances as equal - return a == 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); - } - // exit for functions and DOM nodes - if (className != objectClass || (noNodeClass && (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; - - // non `Object` object instances with different constructors are not equal - if (ctorA != ctorB && !( - isFunction(ctorA) && ctorA instanceof ctorA && - isFunction(ctorB) && ctorB instanceof ctorB - )) { - return false; - } + /** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */ + function lazyValue() { + var array = this.__wrapped__.value(), + dir = this.__dir__, + isArr = isArray(array), + isRight = dir < 0, + arrLength = isArr ? array.length : 0, + view = getView(0, arrLength, this.__views__), + start = view.start, + end = view.end, + length = end - start, + index = isRight ? end : (start - 1), + iteratees = this.__iteratees__, + iterLength = iteratees.length, + resIndex = 0, + takeCount = nativeMin(length, this.__takeCount__); + + if (!isArr || arrLength < LARGE_ARRAY_SIZE || + (arrLength == length && takeCount == length)) { + return baseWrapperValue(array, this.__actions__); } - // 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 = []); + var result = []; - var length = stackA.length; - while (length--) { - if (stackA[length] == a) { - return stackB[length] == b; + outer: + while (length-- && resIndex < takeCount) { + index += dir; + + var iterIndex = -1, + value = array[index]; + + while (++iterIndex < iterLength) { + var data = iteratees[iterIndex], + iteratee = data.iteratee, + type = data.type, + computed = iteratee(value); + + if (type == LAZY_MAP_FLAG) { + value = computed; + } else if (!computed) { + if (type == LAZY_FILTER_FLAG) { + continue outer; + } else { + break outer; + } + } } + result[resIndex++] = value; } - var index = -1, - result = true, - size = 0; + return result; + } - // add `a` and `b` to the stack of traversed objects - stackA.push(a); - stackB.push(b); + // Ensure `LazyWrapper` is an instance of `baseLodash`. + LazyWrapper.prototype = baseCreate(baseLodash.prototype); + LazyWrapper.prototype.constructor = LazyWrapper; - // 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; + /*------------------------------------------------------------------------*/ - if (result) { - // deep compare the contents, ignoring non-numeric properties - while (size--) { - if (!(result = isEqual(a[size], b[size], 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)) { - // count the number of properties. - size++; - // deep compare each property value. - return (result = hasOwnProperty.call(b, key) && isEqual(value, b[key], stackA, stackB)); - } - }); + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; - if (result) { - // 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` - return (result = --size > -1); - } - }); + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); } - 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 + * Removes all key-value entries from the hash. * - * _.isFinite(''); - * // => false - * - * _.isFinite(Infinity); - * // => false + * @private + * @name clear + * @memberOf Hash */ - function isFinite(value) { - return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value)); + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; } /** - * 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 function, else `false`. - * @example + * Removes `key` and its value from the hash. * - * _.isFunction(_); - * // => true + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ - function isFunction(value) { - return typeof value == 'function'; - } - // fallback for older versions of Chrome and Safari - if (isFunction(/x/)) { - isFunction = function(value) { - return value instanceof Function || toString.call(value) == funcClass; - }; + function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; } /** - * 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 an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true + * Gets the hash value for `key`. * - * _.isObject(1); - * // => false + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. */ - 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; + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; } /** - * Checks if `value` is `NaN`. - * - * 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. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true + * Checks if a hash value for `key` exists. * - * _.isNaN(undefined); - * // => false + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ - function isNaN(value) { - // `NaN` as a primitive is the only value that is not equal to itself - // (perform the [[Class]] check first to avoid errors with some host objects in IE) - return isNumber(value) && value != +value + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); } /** - * Checks if `value` is `null`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true + * Sets the hash `key` to `value`. * - * _.isNull(undefined); - * // => false + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. */ - function isNull(value) { - return value === null; + function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; } + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /*------------------------------------------------------------------------*/ + /** - * Checks if `value` is a number. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. - * @example + * Creates an list cache object. * - * _.isNumber(8.4 * 5); - * // => true + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. */ - function isNumber(value) { - return typeof value == 'number' || toString.call(value) == numberClass; + function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } } /** - * Checks if a given `value` is an object created by the `Object` constructor. + * Removes all key-value entries from the list cache. * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Stooge(name, age) { - * this.name = name; - * this.age = age; - * } - * - * _.isPlainObject(new Stooge('moe', 40)); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'name': 'moe', 'age': 40 }); - * // => true + * @private + * @name clear + * @memberOf ListCache */ - var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { - if (!(value && typeof value == 'object')) { - return false; - } - var valueOf = value.valueOf, - objProto = typeof valueOf == 'function' && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); - - return objProto - ? value == objProto || (getPrototypeOf(value) == objProto && !isArguments(value)) - : shimIsPlainObject(value); - }; + function listCacheClear() { + this.__data__ = []; + } /** - * Checks if `value` is a regular expression. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. - * @example + * Removes `key` and its value from the list cache. * - * _.isRegExp(/moe/); - * // => true + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ - function isRegExp(value) { - return value instanceof RegExp || toString.call(value) == regexpClass; + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; } /** - * Checks if `value` is a string. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. - * @example + * Gets the list cache value for `key`. * - * _.isString('moe'); - * // => true + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. */ - function isString(value) { - return typeof value == 'string' || toString.call(value) == stringClass; + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; } /** - * Checks if `value` is `undefined`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. - * @example + * Checks if a list cache value for `key` exists. * - * _.isUndefined(void 0); - * // => true + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ - function isUndefined(value) { - return typeof value == 'undefined'; + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; } /** - * Creates an array composed of the own enumerable property names of `object`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. - * @example + * Sets the list cache `key` to `value`. * - * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); - * // => ['one', 'two', 'three'] (order is not guaranteed) + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. */ - 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) : []); - }; + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /*------------------------------------------------------------------------*/ /** - * Merges enumerable properties of the source object(s) into the `destination` - * object. Subsequent sources will overwrite propery assignments of previous - * sources. - * - * @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. - * @returns {Object} Returns the destination object. - * @example - * - * var stooges = [ - * { 'name': 'moe' }, - * { 'name': 'larry' } - * ]; - * - * var ages = [ - * { 'age': 40 }, - * { 'age': 50 } - * ]; + * Creates a map cache object to store key-value pairs. * - * _.merge(stooges, ages); - * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. */ - function merge(object, source, indicator) { - var args = arguments, - index = 0, - length = 2, - stackA = args[3], - stackB = args[4]; - - if (indicator !== indicatorObject) { - stackA = []; - stackB = []; + function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; - // work with `_.reduce` by only using its callback `accumulator` and `value` arguments - if (typeof indicator != 'number') { - length = args.length; - } - } + this.clear(); 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; - } - }); + var entry = entries[index]; + this.set(entry[0], entry[1]); } - 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). - * - * @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`. - * @returns {Object} Returns an object without the omitted properties. - * @example + * Removes all key-value entries from the map. * - * _.omit({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid'); - * // => { 'name': 'moe', 'age': 40 } - * - * _.omit({ 'name': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { - * return key.charAt(0) == '_'; - * }); - * // => { 'name': 'moe' } + * @private + * @name clear + * @memberOf MapCache */ - function omit(object, callback, thisArg) { - var isFunc = typeof callback == 'function', - result = {}; - - if (isFunc) { - callback = createCallback(callback, thisArg); - } else { - var props = concat.apply(arrayRef, arguments); - } - forIn(object, function(value, key, object) { - if (isFunc - ? !callback(value, key, object) - : indexOf(props, key, 1) < 0 - ) { - result[key] = value; - } - }); - return result; + function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; } /** - * Creates a two dimensional array of the given object's key-value pairs, - * i.e. `[[key1, value1], [key2, value2]]`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns new array of key-value pairs. - * @example + * Removes `key` and its value from the map. * - * _.pairs({ 'moe': 30, 'larry': 40, 'curly': 50 }); - * // => [['moe', 30], ['larry', 40], ['curly', 50]] (order is not guaranteed) + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ - function pairs(object) { - var result = []; - forOwn(object, function(value, key) { - result.push([key, value]); - }); - return result; + function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); } /** - * 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). + * Gets the map value for `key`. * - * @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`. - * @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': 'moe', '_hint': 'knucklehead', '_seed': '96c4eb' }, function(value, key) { - * return key.charAt(0) != '_'; - * }); - * // => { 'name': 'moe' } + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. */ - function pick(object, callback, thisArg) { - var result = {}; - if (typeof callback != 'function') { - var index = 0, - props = concat.apply(arrayRef, arguments), - length = props.length; - - while (++index < length) { - var key = props[index]; - if (key in object) { - result[key] = object[key]; - } - } - } else { - callback = createCallback(callback, thisArg); - forIn(object, function(value, key, object) { - if (callback(value, key, object)) { - result[key] = value; - } - }); - } - return result; + function mapCacheGet(key) { + return getMapData(this, key).get(key); } /** - * Creates an array composed of the own enumerable property values of `object`. + * Checks if a map value for `key` exists. * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property values. - * @example + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. * - * _.values({ 'one': 1, 'two': 2, 'three': 3 }); - * // => [1, 2, 3] + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. */ - function values(object) { - var result = []; - forOwn(object, function(value) { - result.push(value); - }); - return result; + function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; } - /*--------------------------------------------------------------------------*/ + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /*------------------------------------------------------------------------*/ /** - * 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. - * - * @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`. - * @example * - * _.contains([1, 2, 3], 1); - * // => true - * - * _.contains([1, 2, 3], 1, 2); - * // => false - * - * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); - * // => true + * Creates an array cache object to store unique values. * - * _.contains('curly', 'ur'); - * // => true + * @private + * @constructor + * @param {Array} [values] The values to cache. */ - function contains(collection, target, fromIndex) { + function SetCache(values) { var index = -1, - length = collection ? collection.length : 0, - result = false; + length = values ? values.length : 0; - 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; - } else { - each(collection, function(value) { - if (++index >= fromIndex) { - return !(result = value === target); - } - }); + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); } - return result; } /** - * 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 } + * Adds `value` to the array cache. * - * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); - * // => { '4': 1, '6': 2 } - * - * _.countBy(['one', 'two', 'three'], 'length'); - * // => { '3': 2, '5': 1 } + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. */ - 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; + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; } /** - * 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). - * - * @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, - * else `false`. - * @example + * Checks if `value` is in the array cache. * - * _.every([true, 1, null, 'yes'], Boolean); - * // => false + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. */ - function every(collection, callback, thisArg) { - var result = true; - callback = createCallback(callback, thisArg); + function setCacheHas(value) { + return this.__data__.has(value); + } - if (isArray(collection)) { - var index = -1, - length = collection.length; + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; - while (++index < length) { - if (!(result = !!callback(collection[index], index, collection))) { - break; - } - } - } else { - each(collection, function(value, index, collection) { - return (result = !!callback(value, index, collection)); - }); - } - return result; - } + /*------------------------------------------------------------------------*/ /** - * Examines each element in a `collection`, returning an array of all elements - * the `callback` returns truthy for. The `callback` is bound to `thisArg` and - * invoked with three arguments; (value, index|key, collection). - * - * @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`. - * @returns {Array} Returns a new array of elements that passed the callback check. - * @example + * Creates a stack cache object to store key-value pairs. * - * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => [2, 4, 6] + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. */ - function filter(collection, callback, thisArg) { - var result = []; - callback = createCallback(callback, thisArg); - - if (isArray(collection)) { - var index = -1, - length = collection.length; - - while (++index < length) { - var value = collection[index]; - if (callback(value, index, collection)) { - result.push(value); - } - } - } else { - each(collection, function(value, index, collection) { - if (callback(value, index, collection)) { - result.push(value); - } - }); - } - return result; + function Stack(entries) { + this.__data__ = new ListCache(entries); } /** - * 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). - * - * @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 + * Removes all key-value entries from the stack. * - * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => 2 + * @private + * @name clear + * @memberOf Stack */ - 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; + function stackClear() { + this.__data__ = new ListCache; } /** - * 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`. - * - * @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 {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Array|Object|String} Returns `collection`. - * @example + * Removes `key` and its value from the stack. * - * _([1, 2, 3]).forEach(alert).join(','); - * // => alerts each number and returns '1,2,3' - * - * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); - * // => alerts each number value (order is not guaranteed) + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ - function forEach(collection, callback, thisArg) { - if (callback && typeof thisArg == 'undefined' && isArray(collection)) { - var index = -1, - length = collection.length; - - while (++index < length) { - if (callback(collection[index], index, collection) === false) { - break; - } - } - } else { - each(collection, callback, thisArg); - } - return collection; + function stackDelete(key) { + return this.__data__['delete'](key); } /** - * 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'). - * - * @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`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); - * // => { '4': [4.2], '6': [6.1, 6.4] } + * Gets the stack value for `key`. * - * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); - * // => { '4': [4.2], '6': [6.1, 6.4] } - * - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. */ - 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; + function stackGet(key) { + return this.__data__.get(key); } /** - * 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 - * - * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] + * Checks if a stack value for `key` exists. * - * _.invoke([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ - 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; + function stackHas(key) { + return this.__data__.has(key); } /** - * 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). - * - * @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`. - * @returns {Array} Returns a new array of the results of each `callback` execution. - * @example - * - * _.map([1, 2, 3], function(num) { return num * 3; }); - * // => [3, 6, 9] + * Sets the stack `key` to `value`. * - * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); - * // => [3, 6, 9] (order is not guaranteed) + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. */ - function map(collection, callback, thisArg) { - var index = -1, - length = collection ? collection.length : 0, - result = Array(typeof length == 'number' ? length : 0); - - callback = createCallback(callback, thisArg); - if (isArray(collection)) { - while (++index < length) { - result[index] = callback(collection[index], index, collection); - } - } else { - each(collection, function(value, key, collection) { - result[++index] = callback(value, key, collection); - }); + function stackSet(key, value) { + var cache = this.__data__; + if (cache instanceof ListCache && cache.__data__.length == LARGE_ARRAY_SIZE) { + cache = this.__data__ = new MapCache(cache.__data__); } - return result; + cache.set(key, value); + return this; } - /** - * 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); + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; - 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 + * This function is like `assignValue` except that it doesn't assign + * `undefined` values. * - * _.min([10, 5, 100, 2, 1000]); - * // => 2 + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. */ - 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]; - } - } + function assignMergeValue(object, key, value) { + if ((value !== undefined && !eq(object[key], value)) || + (typeof key == 'number' && value === undefined && !(key in object))) { + object[key] = value; } - return result; } /** - * Retrieves the value of a specified property from all elements in - * the `collection`. - * - * @static - * @memberOf _ - * @category Collections - * @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 } - * ]; + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. * - * _.pluck(stooges, 'name'); - * // => ['moe', 'larry', 'curly'] + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. */ - function pluck(collection, property) { - return map(collection, property + ''); + function assignValue(object, key, value) { + var objValue = object[key]; + if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || + (value === undefined && !(key in object))) { + object[key] = value; + } } /** - * 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). - * - * @static - * @memberOf _ - * @alias foldl, inject - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [accumulator] Initial value of the accumulator. - * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Mixed} Returns the accumulated value. - * @example + * Gets the index at which the `key` is found in `array` of key-value pairs. * - * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); - * // => 6 + * @private + * @param {Array} array The array to search. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. */ - function reduce(collection, callback, accumulator, thisArg) { - var noaccum = arguments.length < 3; - callback = createCallback(callback, thisArg, indicatorObject); - - if (isArray(collection)) { - var index = -1, - length = collection.length; - - if (noaccum) { - accumulator = collection[++index]; - } - while (++index < length) { - accumulator = callback(accumulator, collection[index], index, collection); + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; } - } else { - each(collection, function(value, index, collection) { - accumulator = noaccum - ? (noaccum = false, value) - : callback(accumulator, value, index, collection) - }); } - return accumulator; + return -1; } /** - * The right-associative version of `_.reduce`. - * - * @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 + * Aggregates elements of `collection` on `accumulator` with keys transformed + * by `iteratee` and values set by `setter`. * - * 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); + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function baseAggregator(collection, setter, iteratee, accumulator) { + baseEach(collection, function(value, key, collection) { + setter(accumulator, value, iteratee(value), collection); }); return accumulator; } /** - * The opposite of `_.filter`, this method returns the values of a - * `collection` that `callback` does **not** return truthy for. - * - * @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. - * @example + * The base implementation of `_.assign` without support for multiple sources + * or `customizer` functions. * - * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => [1, 3, 5] + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. */ - function reject(collection, callback, thisArg) { - callback = createCallback(callback, thisArg); - return filter(collection, function(value, index, collection) { - return !callback(value, index, collection); - }); + function baseAssign(object, source) { + return object && copyObject(source, keys(source), object); } /** - * 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 + * The base implementation of `_.at` without support for individual paths. * - * _.shuffle([1, 2, 3, 4, 5, 6]); - * // => [4, 1, 6, 3, 5, 2] + * @private + * @param {Object} object The object to iterate over. + * @param {string[]} paths The property paths of elements to pick. + * @returns {Array} Returns the picked elements. */ - function shuffle(collection) { + function baseAt(object, paths) { var index = -1, - result = Array(collection ? collection.length : 0); + isNil = object == null, + length = paths.length, + result = Array(length); - forEach(collection, function(value) { - var rand = floor(nativeRandom() * (++index + 1)); - result[index] = result[rand]; - result[rand] = value; - }); + while (++index < length) { + result[index] = isNil ? undefined : get(object, paths[index]); + } return result; } /** - * Gets the size of the `collection` by returning `collection.length` for arrays - * and array-like objects or the number of own enumerable properties for objects. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to inspect. - * @returns {Number} Returns `collection.length` or number of own enumerable properties. - * @example - * - * _.size([1, 2]); - * // => 2 - * - * _.size({ 'one': 1, 'two': 2, 'three': 3 }); - * // => 3 - * - * _.size('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). - * - * @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, - * else `false`. - * @example + * The base implementation of `_.clone` and `_.cloneDeep` which tracks + * traversed objects. * - * _.some([null, 0, 'yes', false], Boolean); - * // => true + * @private + * @param {*} value The value to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @param {boolean} [isFull] Specify a clone including symbols. + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. */ - function some(collection, callback, thisArg) { + function baseClone(value, isDeep, isFull, customizer, key, object, stack) { var result; - callback = createCallback(callback, thisArg); - - if (isArray(collection)) { - var index = -1, - length = collection.length; + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return copyArray(value, result); + } + } else { + var tag = getTag(value), + isFunc = tag == funcTag || tag == genTag; - while (++index < length) { - if ((result = callback(collection[index], index, collection))) { - break; + if (isBuffer(value)) { + return cloneBuffer(value, isDeep); + } + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + if (isHostObject(value)) { + return object ? value : {}; + } + result = initCloneObject(isFunc ? {} : value); + if (!isDeep) { + return copySymbols(value, baseAssign(result, value)); } + } else { + if (!cloneableTags[tag]) { + return object ? value : {}; + } + result = initCloneByTag(value, tag, baseClone, isDeep); } - } else { - each(collection, function(value, index, collection) { - return !(result = callback(value, index, collection)); - }); } - return !!result; + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack); + var stacked = stack.get(value); + if (stacked) { + return stacked; + } + stack.set(value, result); + + if (!isArr) { + var props = isFull ? getAllKeys(value) : keys(value); + } + // Recursively populate clone (susceptible to call stack limits). + arrayEach(props || value, function(subValue, key) { + if (props) { + key = subValue; + subValue = value[key]; + } + assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack)); + }); + 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'). + * The base implementation of `_.create` without support for assigning + * properties to the created object. * - * @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] + * @private + * @param {Object} prototype The object to inherit from. + * @returns {Object} Returns the new object. + */ + function baseCreate(proto) { + return isObject(proto) ? objectCreate(proto) : {}; + } + + /** + * The base implementation of methods like `_.difference` without support + * for excluding multiple arrays or iteratee shorthands. * - * _.sortBy(['larry', 'brendan', 'moe'], 'length'); - * // => ['moe', 'larry', 'brendan'] + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. */ - function sortBy(collection, callback, thisArg) { - var result = []; - callback = createCallback(callback, thisArg); + function baseDifference(array, values, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + isCommon = true, + length = array.length, + result = [], + valuesLength = values.length; - forEach(collection, function(value, index, collection) { - result.push({ - 'criteria': callback(value, index, collection), - 'index': index, - 'value': value - }); - }); + if (!length) { + return result; + } + if (iteratee) { + values = arrayMap(values, baseUnary(iteratee)); + } + if (comparator) { + includes = arrayIncludesWith; + isCommon = false; + } + else if (values.length >= LARGE_ARRAY_SIZE) { + includes = cacheHas; + isCommon = false; + values = new SetCache(values); + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; - var length = result.length; - result.sort(compareAscending); - while (length--) { - result[length] = result[length].value; + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === computed) { + continue outer; + } + } + result.push(value); + } + else if (!includes(values, computed, comparator)) { + result.push(value); + } } return result; } /** - * Converts the `collection` to an array. + * The base implementation of `_.forEach` without support for iteratee shorthands. * - * @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] + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. */ - function toArray(collection) { - var length = collection ? collection.length : 0; - if (typeof length == 'number') { - return noCharByIndex && isString(collection) - ? collection.split('') - : slice(collection); - } - return values(collection); - } + var baseEach = createBaseEach(baseForOwn); /** - * 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 } - * ]; + * The base implementation of `_.every` without support for iteratee shorthands. * - * _.where(stooges, { 'age': 40 }); - * // => [{ 'name': 'moe', 'age': 40 }] + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false` */ - 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; + function baseEvery(collection, predicate) { + var result = true; + baseEach(collection, function(value, index, collection) { + result = !!predicate(value, index, collection); + return result; }); + return result; } - /*--------------------------------------------------------------------------*/ - /** - * Creates an array with all falsey values of `array` 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. - * @example + * The base implementation of `_.filter` without support for iteratee shorthands. * - * _.compact([0, 1, false, 2, '', 3]); - * // => [1, 2, 3] + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. */ - function compact(array) { - var index = -1, - length = array ? array.length : 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (value) { + function baseFilter(collection, predicate) { + var result = []; + baseEach(collection, function(value, index, collection) { + if (predicate(value, index, collection)) { result.push(value); } - } + }); return result; } /** - * Creates an array of `array` elements not present in the other 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. - * @example + * The base implementation of `_.flatten` with support for restricting flattening. * - * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); - * // => [1, 3, 4] + * @private + * @param {Array} array The array to flatten. + * @param {number} depth The maximum recursion depth. + * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. + * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. */ - function difference(array) { + function baseFlatten(array, depth, predicate, isStrict, result) { var index = -1, - length = array ? array.length : 0, - flattened = concat.apply(arrayRef, arguments), - contains = cachedContains(flattened, length), - result = []; + length = array.length; + + predicate || (predicate = isFlattenable); + result || (result = []); while (++index < length) { var value = array[index]; - if (!contains(value)) { - result.push(value); + if (depth > 0 && predicate(value)) { + if (depth > 1) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, depth - 1, predicate, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; } } return result; } /** - * Gets the first element of the `array`. Pass `n` to return the first `n` - * elements of the `array`. + * The base implementation of `baseForOwn` which iterates over `object` + * properties returned by `keysFunc` and invokes `iteratee` for each property. + * Iteratee functions may exit iteration early by explicitly returning `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`. - * @example + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * The base implementation of `_.forOwn` without support for iteratee shorthands. * - * _.first([5, 4, 3, 2, 1]); - * // => 5 + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. */ - 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 baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); } /** - * Flattens a nested array (the nesting can be to any depth). If `shallow` is - * truthy, `array` will only be flattened a single level. + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from `props`. * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to compact. - * @param {Boolean} shallow A flag to indicate only flattening a single level. - * @returns {Array} Returns a new flattened array. - * @example - * - * _.flatten([1, [2], [3, [[4]]]]); - * // => [1, 2, 3, 4]; + * @private + * @param {Object} object The object to inspect. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the function names. + */ + function baseFunctions(object, props) { + return arrayFilter(props, function(key) { + return isFunction(object[key]); + }); + } + + /** + * The base implementation of `_.get` without support for default values. * - * _.flatten([1, [2], [3, [[4]]]], true); - * // => [1, 2, 3, [[4]]]; + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. */ - function flatten(array, shallow) { - var index = -1, - length = array ? array.length : 0, - result = []; + function baseGet(object, path) { + path = isKey(path, object) ? [path] : castPath(path); - while (++index < length) { - var value = array[index]; + var index = 0, + length = path.length; - // recursively flatten arrays (susceptible to call stack limits) - if (isArray(value)) { - push.apply(result, shallow ? value : flatten(value)); - } else { - result.push(value); - } + while (object != null && index < length) { + object = object[toKey(path[index++])]; } - return result; + return (index && index == length) ? object : undefined; } /** - * 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. - * - * @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`. - * @example - * - * _.indexOf([1, 2, 3, 1, 2, 3], 2); - * // => 1 - * - * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); - * // => 4 + * The base implementation of `getAllKeys` and `getAllKeysIn` which uses + * `keysFunc` and `symbolsFunc` to get the enumerable property names and + * symbols of `object`. * - * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); - * // => 2 + * @private + * @param {Object} object The object to query. + * @param {Function} keysFunc The function to get the keys of `object`. + * @param {Function} symbolsFunc The function to get the symbols of `object`. + * @returns {Array} Returns the array of property names and symbols. */ - 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; - } else if (fromIndex) { - index = sortedIndex(array, value); - return array[index] === value ? index : -1; - } - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); } /** - * 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 + * The base implementation of `_.has` without support for deep paths. * - * _.initial([3, 2, 1]); - * // => [3, 2] + * @private + * @param {Object} object The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. */ - 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)); + function baseHas(object, key) { + // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`, + // that are composed entirely of index properties, return `false` for + // `hasOwnProperty` checks of them. + return hasOwnProperty.call(object, key) || + (typeof object == 'object' && key in object && getPrototype(object) === null); } /** - * Computes the intersection of all the passed-in arrays using strict equality - * for comparisons, i.e. `===`. + * The base implementation of `_.hasIn` without support for deep paths. * - * @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. - * @example + * @private + * @param {Object} object The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHasIn(object, key) { + return key in Object(object); + } + + /** + * The base implementation of methods like `_.intersection`, without support + * for iteratee shorthands, that accepts an array of arrays to inspect. * - * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); - * // => [1, 2] + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of shared values. */ - function intersection(array) { - var args = arguments, - argsLength = args.length, - cache = { '0': {} }, - index = -1, - length = array ? array.length : 0, - isLarge = length >= 100, - result = [], - seen = result; + function baseIntersection(arrays, iteratee, comparator) { + var includes = comparator ? arrayIncludesWith : arrayIncludes, + length = arrays[0].length, + othLength = arrays.length, + othIndex = othLength, + caches = Array(othLength), + maxLength = Infinity, + result = []; - 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] = []); + while (othIndex--) { + var array = arrays[othIndex]; + if (othIndex && iteratee) { + array = arrayMap(array, baseUnary(iteratee)); } - if (inited || indexOf(seen, value) < 0) { - if (isLarge) { - seen.push(value); - } - var argsIndex = argsLength; - while (--argsIndex) { - if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0, 100)))(value)) { + maxLength = nativeMin(array.length, maxLength); + caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) + ? new SetCache(othIndex && array) + : undefined; + } + array = arrays[0]; + + var index = -1, + seen = caches[0]; + + outer: + while (++index < length && result.length < maxLength) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (!(seen + ? cacheHas(seen, computed) + : includes(result, computed, comparator) + )) { + othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if (!(cache + ? cacheHas(cache, computed) + : includes(arrays[othIndex], computed, comparator)) + ) { continue outer; } } + if (seen) { + seen.push(computed); + } result.push(value); } } @@ -9225,38990 +9723,33219 @@ var JXON = new (function () { } /** - * 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 + * The base implementation of `_.invoke` without support for individual + * method arguments. * - * _.last([3, 2, 1]); - * // => 1 + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. */ - function last(array, n, guard) { - if (array) { - var length = array.length; - return (n == null || guard) ? array[length - 1] : slice(array, nativeMax(0, length - n)); + function baseInvoke(object, path, args) { + if (!isKey(path, object)) { + path = castPath(path); + object = parent(object, path); + path = last(path); } + var func = object == null ? object : object[toKey(path)]; + return func == null ? undefined : apply(func, object, args); } /** - * 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 + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); - * // => 4 - * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); - * // => 1 + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @param {boolean} [bitmask] The bitmask of comparison flags. + * The bitmask may be composed of the following flags: + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ - 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; + function baseIsEqual(value, other, customizer, bitmask, stack) { + if (value === other) { + return true; } - while (index--) { - if (array[index] === value) { - return index; - } + if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { + return value !== value && other !== other; } - return -1; + return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack); } /** - * 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`. + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. * - * @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 + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Function} [customizer] The function to customize comparisons. + * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` + * for more details. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = arrayTag, + othTag = arrayTag; + + if (!objIsArr) { + objTag = getTag(object); + objTag = objTag == argsTag ? objectTag : objTag; + } + if (!othIsArr) { + othTag = getTag(other); + othTag = othTag == argsTag ? objectTag : othTag; + } + var objIsObj = objTag == objectTag && !isHostObject(object), + othIsObj = othTag == objectTag && !isHostObject(other), + isSameTag = objTag == othTag; + + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, equalFunc, customizer, bitmask, stack) + : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack); + } + if (!(bitmask & PARTIAL_COMPARE_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, equalFunc, customizer, bitmask, stack); + } + + /** + * The base implementation of `_.isMatch` without support for iteratee shorthands. * - * _.object(['moe', 'larry', 'curly'], [30, 40, 50]); - * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. */ - function object(keys, values) { - var index = -1, - length = keys ? keys.length : 0, - result = {}; + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } while (++index < length) { - var key = keys[index]; - if (values) { - result[key] = values[index]; + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } } else { - result[key[0]] = key[1]; + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack) + : result + )) { + return false; + } } } - return result; + return true; } /** - * 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. - * - * @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. - * @example - * - * _.range(10); - * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - * - * _.range(1, 11); - * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - * - * _.range(0, 30, 5); - * // => [0, 5, 10, 15, 20, 25] + * The base implementation of `_.iteratee`. * - * _.range(0, -10, -1); - * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] - * - * _.range(0); - * // => [] + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. */ - function range(start, end, step) { - start = +start || 0; - step = +step || 1; - - if (end == null) { - end = start; - start = 0; + function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; } - // 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); - - while (++index < length) { - result[index] = start; - start += step; + if (value == null) { + return identity; } - return result; + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); } /** - * 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. - * - * @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 + * The base implementation of `_.keys` which doesn't skip the constructor + * property of prototypes or treat sparse arrays as dense. * - * _.rest([3, 2, 1]); - * // => [2, 1] + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. */ - function rest(array, n, guard) { - return slice(array, (n == null || guard) ? 1 : nativeMax(0, n)); + function baseKeys(object) { + return nativeKeys(Object(object)); } /** - * 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. - * - * @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 - * into `array`. - * @example - * - * _.sortedIndex([20, 30, 50], 40); - * // => 2 - * - * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); - * // => 2 + * The base implementation of `_.keysIn` which doesn't skip the constructor + * property of prototypes or treat sparse arrays as dense. * - * var dict = { - * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 } - * }; - * - * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { - * return dict.wordToNumber[word]; - * }); - * // => 2 - * - * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { - * return this.wordToNumber[word]; - * }, dict); - * // => 2 + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. */ - function sortedIndex(array, value, callback, thisArg) { - var low = 0, - high = array ? array.length : low; - - // explicitly reference `identity` for better inlining in Firefox - callback = callback ? createCallback(callback, thisArg) : identity; - value = callback(value); + function baseKeysIn(object) { + object = object == null ? object : Object(object); - while (low < high) { - var mid = (low + high) >>> 1; - callback(array[mid]) < value - ? low = mid + 1 - : high = mid; + var result = []; + for (var key in object) { + result.push(key); } - return low; + return result; } - /** - * Computes the union of the passed-in 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. - * @example - * - * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); - * // => [1, 2, 3, 101, 10] - */ - function union() { - return uniq(concat.apply(arrayRef, arguments)); + // Fallback for IE < 9 with es6-shim. + if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) { + baseKeysIn = function(object) { + return iteratorToArray(enumerate(object)); + }; } /** - * 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). - * - * @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`. - * @returns {Array} Returns a duplicate-value-free array. - * @example - * - * _.uniq([1, 2, 1, 3, 1]); - * // => [1, 2, 3] + * The base implementation of `_.map` without support for iteratee shorthands. * - * _.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([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); - * // => [1, 2, 3] + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. */ - function uniq(array, isSorted, callback, thisArg) { + function baseMap(collection, iteratee) { var index = -1, - length = array ? array.length : 0, - result = [], - seen = result; - - // juggle arguments - if (typeof isSorted == 'function') { - thisArg = callback; - callback = 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; + result = isArrayLike(collection) ? Array(collection.length) : []; - 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); - } - } + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); return result; } /** - * Creates an array with all occurrences of the passed values removed 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. - * @example + * The base implementation of `_.matches` which doesn't clone `source`. * - * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); - * // => [2, 3, 4] + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. */ - 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); - } + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); } - return result; + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; } /** - * 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 + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. * - * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); - * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. */ - function zip(array) { - var index = -1, - length = array ? max(pluck(arguments, 'length')) : 0, - result = Array(length); - - while (++index < length) { - result[index] = pluck(arguments, index); + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); } - return result; + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG); + }; } - /*--------------------------------------------------------------------------*/ - /** - * 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 + * The base implementation of `_.merge` without support for multiple sources. * - * var renderNotes = _.after(notes.length, render); - * _.forEach(notes, function(note) { - * note.asyncSave({ 'success': renderNotes }); - * }); - * // `renderNotes` is run once, after all notes have saved + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {number} srcIndex The index of `source`. + * @param {Function} [customizer] The function to customize merged values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. */ - function after(n, func) { - if (n < 1) { - return func(); + function baseMerge(object, source, srcIndex, customizer, stack) { + if (object === source) { + return; } - return function() { - if (--n < 1) { - return func.apply(this, arguments); + if (!(isArray(source) || isTypedArray(source))) { + var props = keysIn(source); + } + arrayEach(props || source, function(srcValue, key) { + if (props) { + key = srcValue; + srcValue = source[key]; } - }; + if (isObject(srcValue)) { + stack || (stack = new Stack); + baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); + } + else { + var newValue = customizer + ? customizer(object[key], srcValue, (key + ''), object, source, stack) + : undefined; + + if (newValue === undefined) { + newValue = srcValue; + } + assignMergeValue(object, key, newValue); + } + }); } /** - * 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. + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. * - * @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. - * @returns {Function} Returns the new bound function. - * @example + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {number} srcIndex The index of `source`. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize assigned values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { + var objValue = object[key], + srcValue = source[key], + stacked = stack.get(srcValue); + + if (stacked) { + assignMergeValue(object, key, stacked); + return; + } + var newValue = customizer + ? customizer(objValue, srcValue, (key + ''), object, source, stack) + : undefined; + + var isCommon = newValue === undefined; + + if (isCommon) { + newValue = srcValue; + if (isArray(srcValue) || isTypedArray(srcValue)) { + if (isArray(objValue)) { + newValue = objValue; + } + else if (isArrayLikeObject(objValue)) { + newValue = copyArray(objValue); + } + else { + isCommon = false; + newValue = baseClone(srcValue, true); + } + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + if (isArguments(objValue)) { + newValue = toPlainObject(objValue); + } + else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { + isCommon = false; + newValue = baseClone(srcValue, true); + } + else { + newValue = objValue; + } + } + else { + isCommon = false; + } + } + stack.set(srcValue, newValue); + + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + mergeFunc(newValue, srcValue, srcIndex, customizer, stack); + } + stack['delete'](srcValue); + assignMergeValue(object, key, newValue); + } + + /** + * The base implementation of `_.pick` without support for individual + * property identifiers. * - * var func = function(greeting) { - * return greeting + ' ' + this.name; - * }; + * @private + * @param {Object} object The source object. + * @param {string[]} props The property identifiers to pick. + * @returns {Object} Returns the new object. + */ + function basePick(object, props) { + object = Object(object); + return arrayReduce(props, function(result, key) { + if (key in object) { + result[key] = object[key]; + } + return result; + }, {}); + } + + /** + * The base implementation of `_.property` without support for deep paths. * - * func = _.bind(func, { 'name': 'moe' }, 'hi'); - * func(); - * // => 'hi moe' + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. */ - 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)); + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; } /** - * 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. + * A specialized version of `baseProperty` which supports deep paths. * - * @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 + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + + /** + * The base implementation of `setData` without support for hot loop detection. * - * var buttonView = { - * 'label': 'lodash', - * 'onClick': function() { alert('clicked: ' + this.label); } - * }; + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var baseSetData = !metaMap ? identity : function(func, data) { + metaMap.set(func, data); + return func; + }; + + /** + * The base implementation of `_.slice` without an iteratee call guard. * - * _.bindAll(buttonView); - * jQuery('#lodash_button').on('click', buttonView.onClick); - * // => When the button is clicked, `this.label` will have the correct value + * @private + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. */ - function bindAll(object) { - var funcs = arguments, - index = funcs.length > 1 ? 0 : (funcs = functions(object), -1), - length = funcs.length; + function baseSlice(array, start, end) { + var index = -1, + length = array.length; + + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = end > length ? length : end; + if (end < 0) { + end += length; + } + length = start > end ? 0 : ((end - start) >>> 0); + start >>>= 0; + var result = Array(length); while (++index < length) { - var key = funcs[index]; - object[key] = bind(object[key], object); + result[index] = array[index + start]; } - return object; + return result; } /** - * 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. - * - * @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. - * @example - * - * var object = { - * 'name': 'moe', - * 'greet': function(greeting) { - * return greeting + ' ' + this.name; - * } - * }; - * - * var func = _.bindKey(object, 'greet', 'hi'); - * func(); - * // => 'hi moe' - * - * object.greet = function(greeting) { - * return greeting + ', ' + this.name + '!'; - * }; + * The base implementation of `_.some` without support for iteratee shorthands. * - * func(); - * // => 'hi, moe!' + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. */ - function bindKey(object, key) { - return createBound(object, key, slice(arguments, 2)); + function baseSome(collection, predicate) { + var result; + + baseEach(collection, function(value, index, collection) { + result = predicate(value, index, collection); + return !result; + }); + return !!result; } /** - * 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 + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. * - * var greet = function(name) { return 'hi: ' + name; }; - * var exclaim = function(statement) { return statement + '!'; }; - * var welcome = _.compose(exclaim, greet); - * welcome('moe'); - * // => 'hi: moe!' + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. */ - function compose() { - var funcs = arguments; - return function() { - var args = arguments, - length = funcs.length; - - while (length--) { - args = [funcs[length].apply(this, args)]; - } - return args[0]; - }; + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : 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. - * - * @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. - * @returns {Function} Returns the new debounced function. - * @example + * The base implementation of `_.uniqBy` without support for iteratee shorthands. * - * var lazyLayout = _.debounce(calculateLayout, 300); - * jQuery(window).on('resize', lazyLayout); + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. */ - function debounce(func, wait, immediate) { - var args, - result, - thisArg, - timeoutId; + function baseUniq(array, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + length = array.length, + isCommon = true, + result = [], + seen = result; - function delayed() { - timeoutId = null; - if (!immediate) { - result = func.apply(thisArg, args); + if (comparator) { + isCommon = false; + includes = arrayIncludesWith; + } + else if (length >= LARGE_ARRAY_SIZE) { + var set = iteratee ? null : createSet(array); + if (set) { + return setToArray(set); } + isCommon = false; + includes = cacheHas; + seen = new SetCache; } - return function() { - var isImmediate = immediate && !timeoutId; - args = arguments; - thisArg = this; - - clearTimeout(timeoutId); - timeoutId = setTimeout(delayed, wait); + else { + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; - if (isImmediate) { - result = func.apply(thisArg, args); + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); } - return result; - }; + else if (!includes(seen, computed, comparator)) { + if (seen !== result) { + seen.push(computed); + } + result.push(value); + } + } + 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 + * The base implementation of `wrapperValue` which returns the result of + * performing a sequence of actions on the unwrapped `value`, where each + * successive action is supplied the return value of the previous. * - * var log = _.bind(console.log, console); - * _.delay(log, 1000, 'logged later'); - * // => 'logged later' (Appears after one second.) + * @private + * @param {*} value The unwrapped value. + * @param {Array} actions Actions to perform to resolve the unwrapped value. + * @returns {*} Returns the resolved value. */ - function delay(func, wait) { - var args = slice(arguments, 2); - return setTimeout(function() { func.apply(undefined, args); }, wait); + function baseWrapperValue(value, actions) { + var result = value; + if (result instanceof LazyWrapper) { + result = result.value(); + } + return arrayReduce(actions, function(result, action) { + return action.func.apply(action.thisArg, arrayPush([result], action.args)); + }, result); } /** - * 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 + * Casts `value` to an empty array if it's not an array like object. * - * _.defer(function() { alert('deferred'); }); - * // returns from the function before `alert` is called + * @private + * @param {*} value The value to inspect. + * @returns {Array|Object} Returns the cast array-like object. */ - function defer(func) { - var args = slice(arguments, 1); - return setTimeout(function() { func.apply(undefined, args); }, 1); + function castArrayLikeObject(value) { + return isArrayLikeObject(value) ? value : []; } /** - * 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 + * Casts `value` to a path array if it's not one. * - * var fibonacci = _.memoize(function(n) { - * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); - * }); + * @private + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast property path array. */ - 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)); - }; + function castPath(value) { + return isArray(value) ? value : stringToPath(value); } /** - * 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 + * Creates a clone of `buffer`. * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // Application is only created once. + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. */ - 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; - }; + function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice(); + } + var result = new buffer.constructor(buffer.length); + buffer.copy(result); + 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 + * Creates a clone of `arrayBuffer`. * - * var greet = function(greeting, name) { return greeting + ': ' + name; }; - * var hi = _.partial(greet, 'hi'); - * hi('moe'); - * // => 'hi: moe' + * @private + * @param {ArrayBuffer} arrayBuffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. */ - function partial(func) { - return createBound(func, slice(arguments, 1)); + function cloneArrayBuffer(arrayBuffer) { + var result = new arrayBuffer.constructor(arrayBuffer.byteLength); + new Uint8Array(result).set(new Uint8Array(arrayBuffer)); + return result; } /** - * 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. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to throttle. - * @param {Number} wait The number of milliseconds to throttle executions to. - * @returns {Function} Returns the new throttled function. - * @example + * Creates a clone of `dataView`. * - * var throttled = _.throttle(updatePosition, 100); - * jQuery(window).on('scroll', throttled); + * @private + * @param {Object} dataView The data view to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned data view. */ - function throttle(func, wait) { - var args, - result, - thisArg, - timeoutId, - lastCalled = 0; - - function trailingCall() { - lastCalled = new Date; - timeoutId = null; - result = func.apply(thisArg, args); - } - 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; - }; + function cloneDataView(dataView, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; + return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); } /** - * 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 + * Creates a clone of `map`. * - * var hello = function(name) { return 'hello ' + name; }; - * hello = _.wrap(hello, function(func) { - * return 'before, ' + func('moe') + ', after'; - * }); - * hello(); - * // => 'before, hello moe, after' + * @private + * @param {Object} map The map to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned map. */ - function wrap(value, wrapper) { - return function() { - var args = [value]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; + function cloneMap(map, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map); + return arrayReduce(array, addMapEntry, new map.constructor); } - /*--------------------------------------------------------------------------*/ - /** - * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their - * corresponding HTML entities. - * - * @static - * @memberOf _ - * @category Utilities - * @param {String} string The string to escape. - * @returns {String} Returns the escaped string. - * @example + * Creates a clone of `regexp`. * - * _.escape('Moe, Larry & Curly'); - * // => 'Moe, Larry & Curly' + * @private + * @param {Object} regexp The regexp to clone. + * @returns {Object} Returns the cloned regexp. */ - function escape(string) { - return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); + function cloneRegExp(regexp) { + var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); + result.lastIndex = regexp.lastIndex; + return result; } /** - * This function returns the first argument passed to it. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Mixed} value Any value. - * @returns {Mixed} Returns `value`. - * @example + * Creates a clone of `set`. * - * var moe = { 'name': 'moe' }; - * moe === _.identity(moe); - * // => true + * @private + * @param {Object} set The set to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned set. */ - function identity(value) { - return value; + function cloneSet(set, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set); + return arrayReduce(array, addSetEntry, new set.constructor); } /** - * Adds functions properties of `object` to the `lodash` function and chainable - * wrapper. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Object} object The object of function properties to add to `lodash`. - * @example - * - * _.mixin({ - * 'capitalize': function(string) { - * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); - * } - * }); - * - * _.capitalize('larry'); - * // => 'Larry' + * Creates a clone of the `symbol` object. * - * _('curly').capitalize(); - * // => 'Curly' + * @private + * @param {Object} symbol The symbol object to clone. + * @returns {Object} Returns the cloned symbol object. */ - 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); - }; - }); + function cloneSymbol(symbol) { + return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; } /** - * 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 + * Creates a clone of `typedArray`. * - * var lodash = _.noConflict(); + * @private + * @param {Object} typedArray The typed array to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned typed array. */ - function noConflict() { - window._ = oldDash; - return this; + function cloneTypedArray(typedArray, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; + return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); } /** - * 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 + * Creates an array that is the composition of partially applied arguments, + * placeholders, and provided arguments into a single array of arguments. * - * _.random(5); - * // => also a number between 1 and 5 + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to prepend to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. */ - function random(min, max) { - if (min == null && max == null) { - max = 1; + function composeArgs(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersLength = holders.length, + leftIndex = -1, + leftLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(leftLength + rangeLength), + isUncurried = !isCurried; + + while (++leftIndex < leftLength) { + result[leftIndex] = partials[leftIndex]; + } + while (++argsIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[holders[argsIndex]] = args[argsIndex]; + } } - min = +min || 0; - if (max == null) { - max = min; - min = 0; + while (rangeLength--) { + result[leftIndex++] = args[argsIndex++]; } - return min + floor(nativeRandom() * ((+max || 0) - min + 1)); + return result; } /** - * 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' + * This function is like `composeArgs` except that the arguments composition + * is tailored for `_.partialRight`. * - * _.result(object, 'stuff'); - * // => 'nonsense' + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to append to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. */ - 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; + function composeArgsRight(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersIndex = -1, + holdersLength = holders.length, + rightIndex = -1, + rightLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(rangeLength + rightLength), + isUncurried = !isCurried; + + while (++argsIndex < rangeLength) { + result[argsIndex] = args[argsIndex]; + } + var offset = argsIndex; + while (++rightIndex < rightLength) { + result[offset + rightIndex] = partials[rightIndex]; + } + while (++holdersIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[offset + holders[holdersIndex]] = args[argsIndex++]; + } + } + return result; } /** - * 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 + * Copies the values of `source` to `array`. * - * 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
  • ' - * - * // using the "escape" delimiter to escape HTML in data property values - * _.template('<%- value %>', { 'value': '