+ return new Hsl(h, s, l, o.opacity);
+}
+
+function hsl(h, s, l, opacity) {
+ return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
+}
+
+function Hsl(h, s, l, opacity) {
+ this.h = +h;
+ this.s = +s;
+ this.l = +l;
+ this.opacity = +opacity;
+}
+
+define(Hsl, hsl, extend(Color, {
+ brighter: function(k) {
+ k = k == null ? brighter : Math.pow(brighter, k);
+ return new Hsl(this.h, this.s, this.l * k, this.opacity);
+ },
+ darker: function(k) {
+ k = k == null ? darker : Math.pow(darker, k);
+ return new Hsl(this.h, this.s, this.l * k, this.opacity);
+ },
+ rgb: function() {
+ var h = this.h % 360 + (this.h < 0) * 360,
+ s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
+ l = this.l,
+ m2 = l + (l < 0.5 ? l : 1 - l) * s,
+ m1 = 2 * l - m2;
+ return new Rgb(
+ hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
+ hsl2rgb(h, m1, m2),
+ hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
+ this.opacity
+ );
+ },
+ displayable: function() {
+ return (0 <= this.s && this.s <= 1 || isNaN(this.s))
+ && (0 <= this.l && this.l <= 1)
+ && (0 <= this.opacity && this.opacity <= 1);
+ }
+}));
+
+/* From FvD 13.37, CSS Color Module Level 3 */
+function hsl2rgb(h, m1, m2) {
+ return (h < 60 ? m1 + (m2 - m1) * h / 60
+ : h < 180 ? m2
+ : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
+ : m1) * 255;
+}
+
+var deg2rad = Math.PI / 180;
+var rad2deg = 180 / Math.PI;
+
+var Kn = 18;
+var Xn = 0.950470;
+var Yn = 1;
+var Zn = 1.088830;
+var t0 = 4 / 29;
+var t1 = 6 / 29;
+var t2 = 3 * t1 * t1;
+var t3 = t1 * t1 * t1;
+
+function labConvert(o) {
+ if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
+ if (o instanceof Hcl) {
+ var h = o.h * deg2rad;
+ return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
+ }
+ if (!(o instanceof Rgb)) o = rgbConvert(o);
+ var b = rgb2xyz(o.r),
+ a = rgb2xyz(o.g),
+ l = rgb2xyz(o.b),
+ x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn),
+ y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn),
+ z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn);
+ return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
+}
+
+function lab(l, a, b, opacity) {
+ return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);
+}
+
+function Lab(l, a, b, opacity) {
+ this.l = +l;
+ this.a = +a;
+ this.b = +b;
+ this.opacity = +opacity;
+}
+
+define(Lab, lab, extend(Color, {
+ brighter: function(k) {
+ return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
+ },
+ darker: function(k) {
+ return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
+ },
+ rgb: function() {
+ var y = (this.l + 16) / 116,
+ x = isNaN(this.a) ? y : y + this.a / 500,
+ z = isNaN(this.b) ? y : y - this.b / 200;
+ y = Yn * lab2xyz(y);
+ x = Xn * lab2xyz(x);
+ z = Zn * lab2xyz(z);
+ return new Rgb(
+ xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB
+ xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
+ xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z),
+ this.opacity
+ );
+ }
+}));
+
+function xyz2lab(t) {
+ return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
+}
+
+function lab2xyz(t) {
+ return t > t1 ? t * t * t : t2 * (t - t0);
+}
+
+function xyz2rgb(x) {
+ return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
+}
+
+function rgb2xyz(x) {
+ return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
+}
+
+function hclConvert(o) {
+ if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
+ if (!(o instanceof Lab)) o = labConvert(o);
+ var h = Math.atan2(o.b, o.a) * rad2deg;
+ return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);
+}
+
+function hcl(h, c, l, opacity) {
+ return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
+}
+
+function Hcl(h, c, l, opacity) {
+ this.h = +h;
+ this.c = +c;
+ this.l = +l;
+ this.opacity = +opacity;
+}
+
+define(Hcl, hcl, extend(Color, {
+ brighter: function(k) {
+ return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity);
+ },
+ darker: function(k) {
+ return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity);
+ },
+ rgb: function() {
+ return labConvert(this).rgb();
+ }
+}));
+
+var A = -0.14861;
+var B = +1.78277;
+var C = -0.29227;
+var D = -0.90649;
+var E = +1.97294;
+var ED = E * D;
+var EB = E * B;
+var BC_DA = B * C - D * A;
+
+function cubehelixConvert(o) {
+ if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
+ if (!(o instanceof Rgb)) o = rgbConvert(o);
+ var r = o.r / 255,
+ g = o.g / 255,
+ b = o.b / 255,
+ l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
+ bl = b - l,
+ k = (E * (g - l) - C * bl) / D,
+ s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1
+ h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;
+ return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
+}
+
+function cubehelix(h, s, l, opacity) {
+ return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
+}
+
+function Cubehelix(h, s, l, opacity) {
+ this.h = +h;
+ this.s = +s;
+ this.l = +l;
+ this.opacity = +opacity;
+}
+
+define(Cubehelix, cubehelix, extend(Color, {
+ brighter: function(k) {
+ k = k == null ? brighter : Math.pow(brighter, k);
+ return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
+ },
+ darker: function(k) {
+ k = k == null ? darker : Math.pow(darker, k);
+ return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
+ },
+ rgb: function() {
+ var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
+ l = +this.l,
+ a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
+ cosh = Math.cos(h),
+ sinh = Math.sin(h);
+ return new Rgb(
+ 255 * (l + a * (A * cosh + B * sinh)),
+ 255 * (l + a * (C * cosh + D * sinh)),
+ 255 * (l + a * (E * cosh)),
+ this.opacity
+ );
+ }
+}));
+
+function basis(t1, v0, v1, v2, v3) {
+ var t2 = t1 * t1, t3 = t2 * t1;
+ return ((1 - 3 * t1 + 3 * t2 - t3) * v0
+ + (4 - 6 * t2 + 3 * t3) * v1
+ + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2
+ + t3 * v3) / 6;
+}
+
+var basis$1 = function(values) {
+ var n = values.length - 1;
+ return function(t) {
+ var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),
+ v1 = values[i],
+ v2 = values[i + 1],
+ v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,
+ v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;
+ return basis((t - i / n) * n, v0, v1, v2, v3);
+ };
+};
+
+var basisClosed = function(values) {
+ var n = values.length;
+ return function(t) {
+ var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),
+ v0 = values[(i + n - 1) % n],
+ v1 = values[i % n],
+ v2 = values[(i + 1) % n],
+ v3 = values[(i + 2) % n];
+ return basis((t - i / n) * n, v0, v1, v2, v3);
+ };
+};
+
+var constant$3 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+function linear(a, d) {
+ return function(t) {
+ return a + t * d;
+ };
+}
+
+function exponential(a, b, y) {
+ return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
+ return Math.pow(a + t * b, y);
+ };
+}
+
+function hue(a, b) {
+ var d = b - a;
+ return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a);
+}
+
+function gamma(y) {
+ return (y = +y) === 1 ? nogamma : function(a, b) {
+ return b - a ? exponential(a, b, y) : constant$3(isNaN(a) ? b : a);
+ };
+}
+
+function nogamma(a, b) {
+ var d = b - a;
+ return d ? linear(a, d) : constant$3(isNaN(a) ? b : a);
+}
+
+var interpolateRgb = ((function rgbGamma(y) {
+ var color$$1 = gamma(y);
+
+ function rgb$$1(start, end) {
+ var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r),
+ g = color$$1(start.g, end.g),
+ b = color$$1(start.b, end.b),
+ opacity = nogamma(start.opacity, end.opacity);
+ return function(t) {
+ start.r = r(t);
+ start.g = g(t);
+ start.b = b(t);
+ start.opacity = opacity(t);
+ return start + "";
+ };
+ }
+
+ rgb$$1.gamma = rgbGamma;
+
+ return rgb$$1;
+}))(1);
+
+function rgbSpline(spline) {
+ return function(colors) {
+ var n = colors.length,
+ r = new Array(n),
+ g = new Array(n),
+ b = new Array(n),
+ i, color$$1;
+ for (i = 0; i < n; ++i) {
+ color$$1 = rgb(colors[i]);
+ r[i] = color$$1.r || 0;
+ g[i] = color$$1.g || 0;
+ b[i] = color$$1.b || 0;
+ }
+ r = spline(r);
+ g = spline(g);
+ b = spline(b);
+ color$$1.opacity = 1;
+ return function(t) {
+ color$$1.r = r(t);
+ color$$1.g = g(t);
+ color$$1.b = b(t);
+ return color$$1 + "";
+ };
+ };
+}
+
+var rgbBasis = rgbSpline(basis$1);
+var rgbBasisClosed = rgbSpline(basisClosed);
+
+var array$1 = function(a, b) {
+ var nb = b ? b.length : 0,
+ na = a ? Math.min(nb, a.length) : 0,
+ x = new Array(nb),
+ c = new Array(nb),
+ i;
+
+ for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]);
+ for (; i < nb; ++i) c[i] = b[i];
+
+ return function(t) {
+ for (i = 0; i < na; ++i) c[i] = x[i](t);
+ return c;
+ };
+};
+
+var date = function(a, b) {
+ var d = new Date;
+ return a = +a, b -= a, function(t) {
+ return d.setTime(a + b * t), d;
+ };
+};
+
+var reinterpolate = function(a, b) {
+ return a = +a, b -= a, function(t) {
+ return a + b * t;
+ };
+};
+
+var object = function(a, b) {
+ var i = {},
+ c = {},
+ k;
+
+ if (a === null || typeof a !== "object") a = {};
+ if (b === null || typeof b !== "object") b = {};
+
+ for (k in b) {
+ if (k in a) {
+ i[k] = interpolateValue(a[k], b[k]);
+ } else {
+ c[k] = b[k];
+ }
+ }
+
+ return function(t) {
+ for (k in i) c[k] = i[k](t);
+ return c;
+ };
+};
+
+var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;
+var reB = new RegExp(reA.source, "g");
+
+function zero(b) {
+ return function() {
+ return b;
+ };
+}
+
+function one(b) {
+ return function(t) {
+ return b(t) + "";
+ };
+}
+
+var interpolateString = function(a, b) {
+ var bi = reA.lastIndex = reB.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 = reA.exec(a))
+ && (bm = reB.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: reinterpolate(am, bm)});
+ }
+ bi = reB.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]
+ ? one(q[0].x)
+ : zero(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 interpolateValue = function(a, b) {
+ var t = typeof b, c;
+ return b == null || t === "boolean" ? constant$3(b)
+ : (t === "number" ? reinterpolate
+ : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString)
+ : b instanceof color ? interpolateRgb
+ : b instanceof Date ? date
+ : Array.isArray(b) ? array$1
+ : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
+ : reinterpolate)(a, b);
+};
+
+var interpolateRound = function(a, b) {
+ return a = +a, b -= a, function(t) {
+ return Math.round(a + b * t);
+ };
+};
+
+var degrees = 180 / Math.PI;
+
+var identity$2 = {
+ translateX: 0,
+ translateY: 0,
+ rotate: 0,
+ skewX: 0,
+ scaleX: 1,
+ scaleY: 1
+};
+
+var decompose = function(a, b, c, d, e, f) {
+ var scaleX, scaleY, skewX;
+ if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
+ if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
+ if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
+ if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
+ return {
+ translateX: e,
+ translateY: f,
+ rotate: Math.atan2(b, a) * degrees,
+ skewX: Math.atan(skewX) * degrees,
+ scaleX: scaleX,
+ scaleY: scaleY
+ };
+};
+
+var cssNode;
+var cssRoot;
+var cssView;
+var svgNode;
+
+function parseCss(value) {
+ if (value === "none") return identity$2;
+ if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView;
+ cssNode.style.transform = value;
+ value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform");
+ cssRoot.removeChild(cssNode);
+ value = value.slice(7, -1).split(",");
+ return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);
+}
+
+function parseSvg(value) {
+ if (value == null) return identity$2;
+ if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
+ svgNode.setAttribute("transform", value);
+ if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2;
+ value = value.matrix;
+ return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
+}
+
+function interpolateTransform(parse, pxComma, pxParen, degParen) {
+
+ function pop(s) {
+ return s.length ? s.pop() + " " : "";
+ }
+
+ function translate(xa, ya, xb, yb, s, q) {
+ if (xa !== xb || ya !== yb) {
+ var i = s.push("translate(", null, pxComma, null, pxParen);
+ q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)});
+ } else if (xb || yb) {
+ s.push("translate(" + xb + pxComma + yb + pxParen);
+ }
+ }
+
+ function rotate(a, b, s, q) {
+ if (a !== b) {
+ if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path
+ q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: reinterpolate(a, b)});
+ } else if (b) {
+ s.push(pop(s) + "rotate(" + b + degParen);
+ }
+ }
+
+ function skewX(a, b, s, q) {
+ if (a !== b) {
+ q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: reinterpolate(a, b)});
+ } else if (b) {
+ s.push(pop(s) + "skewX(" + b + degParen);
+ }
+ }
+
+ function scale(xa, ya, xb, yb, s, q) {
+ if (xa !== xb || ya !== yb) {
+ var i = s.push(pop(s) + "scale(", null, ",", null, ")");
+ q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)});
+ } else if (xb !== 1 || yb !== 1) {
+ s.push(pop(s) + "scale(" + xb + "," + yb + ")");
+ }
+ }
+
+ return function(a, b) {
+ var s = [], // string constants and placeholders
+ q = []; // number interpolators
+ a = parse(a), b = parse(b);
+ translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
+ rotate(a.rotate, b.rotate, s, q);
+ skewX(a.skewX, b.skewX, s, q);
+ scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
+ a = b = null; // gc
+ return function(t) {
+ var i = -1, n = q.length, o;
+ while (++i < n) s[(o = q[i]).i] = o.x(t);
+ return s.join("");
+ };
+ };
+}
+
+var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
+var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
+
+var rho = Math.SQRT2;
+var rho2 = 2;
+var rho4 = 4;
+var epsilon2 = 1e-12;
+
+function cosh(x) {
+ return ((x = Math.exp(x)) + 1 / x) / 2;
+}
+
+function sinh(x) {
+ return ((x = Math.exp(x)) - 1 / x) / 2;
+}
+
+function tanh(x) {
+ return ((x = Math.exp(2 * x)) - 1) / (x + 1);
+}
+
+// p0 = [ux0, uy0, w0]
+// p1 = [ux1, uy1, w1]
+var interpolateZoom = function(p0, p1) {
+ var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
+ ux1 = p1[0], uy1 = p1[1], w1 = p1[2],
+ dx = ux1 - ux0,
+ dy = uy1 - uy0,
+ d2 = dx * dx + dy * dy,
+ i,
+ S;
+
+ // Special case for u0 ≅ u1.
+ if (d2 < epsilon2) {
+ S = Math.log(w1 / w0) / rho;
+ i = function(t) {
+ return [
+ ux0 + t * dx,
+ uy0 + t * dy,
+ w0 * Math.exp(rho * t * S)
+ ];
+ };
+ }
+
+ // General case.
+ else {
+ var d1 = Math.sqrt(d2),
+ b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
+ b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
+ r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
+ r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
+ S = (r1 - r0) / rho;
+ i = function(t) {
+ var s = t * S,
+ coshr0 = cosh(r0),
+ u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
+ return [
+ ux0 + u * dx,
+ uy0 + u * dy,
+ w0 * coshr0 / cosh(rho * s + r0)
+ ];
+ };
+ }
+
+ i.duration = S * 1000;
+
+ return i;
+};
+
+function hsl$1(hue$$1) {
+ return function(start, end) {
+ var h = hue$$1((start = hsl(start)).h, (end = hsl(end)).h),
+ s = nogamma(start.s, end.s),
+ l = nogamma(start.l, end.l),
+ opacity = nogamma(start.opacity, end.opacity);
+ return function(t) {
+ start.h = h(t);
+ start.s = s(t);
+ start.l = l(t);
+ start.opacity = opacity(t);
+ return start + "";
+ };
+ }
+}
+
+var hsl$2 = hsl$1(hue);
+var hslLong = hsl$1(nogamma);
+
+function lab$1(start, end) {
+ var l = nogamma((start = lab(start)).l, (end = lab(end)).l),
+ a = nogamma(start.a, end.a),
+ b = nogamma(start.b, end.b),
+ opacity = nogamma(start.opacity, end.opacity);
+ return function(t) {
+ start.l = l(t);
+ start.a = a(t);
+ start.b = b(t);
+ start.opacity = opacity(t);
+ return start + "";
+ };
+}
+
+function hcl$1(hue$$1) {
+ return function(start, end) {
+ var h = hue$$1((start = hcl(start)).h, (end = hcl(end)).h),
+ c = nogamma(start.c, end.c),
+ l = nogamma(start.l, end.l),
+ opacity = nogamma(start.opacity, end.opacity);
+ return function(t) {
+ start.h = h(t);
+ start.c = c(t);
+ start.l = l(t);
+ start.opacity = opacity(t);
+ return start + "";
+ };
+ }
+}
+
+var hcl$2 = hcl$1(hue);
+var hclLong = hcl$1(nogamma);
+
+function cubehelix$1(hue$$1) {
+ return (function cubehelixGamma(y) {
+ y = +y;
+
+ function cubehelix$$1(start, end) {
+ var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h),
+ s = nogamma(start.s, end.s),
+ l = nogamma(start.l, end.l),
+ opacity = nogamma(start.opacity, end.opacity);
+ return function(t) {
+ start.h = h(t);
+ start.s = s(t);
+ start.l = l(Math.pow(t, y));
+ start.opacity = opacity(t);
+ return start + "";
+ };
+ }
+
+ cubehelix$$1.gamma = cubehelixGamma;
+
+ return cubehelix$$1;
+ })(1);
+}
+
+var cubehelix$2 = cubehelix$1(hue);
+var cubehelixLong = cubehelix$1(nogamma);
+
+var quantize = function(interpolator, n) {
+ var samples = new Array(n);
+ for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));
+ return samples;
+};
+
+var frame = 0;
+var timeout = 0;
+var interval = 0;
+var pokeDelay = 1000;
+var taskHead;
+var taskTail;
+var clockLast = 0;
+var clockNow = 0;
+var clockSkew = 0;
+var clock = typeof performance === "object" && performance.now ? performance : Date;
+var setFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : function(f) { setTimeout(f, 17); };
+
+function now() {
+ return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
+}
+
+function clearNow() {
+ clockNow = 0;
+}
+
+function Timer() {
+ this._call =
+ this._time =
+ this._next = null;
+}
+
+Timer.prototype = timer.prototype = {
+ constructor: Timer,
+ restart: function(callback, delay, time) {
+ if (typeof callback !== "function") throw new TypeError("callback is not a function");
+ time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
+ if (!this._next && taskTail !== this) {
+ if (taskTail) taskTail._next = this;
+ else taskHead = this;
+ taskTail = this;
+ }
+ this._call = callback;
+ this._time = time;
+ sleep();
+ },
+ stop: function() {
+ if (this._call) {
+ this._call = null;
+ this._time = Infinity;
+ sleep();
+ }
+ }
+};
+
+function timer(callback, delay, time) {
+ var t = new Timer;
+ t.restart(callback, delay, time);
+ return t;
+}
+
+function timerFlush() {
+ now(); // Get the current time, if not already set.
+ ++frame; // Pretend we’ve set an alarm, if we haven’t already.
+ var t = taskHead, e;
+ while (t) {
+ if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
+ t = t._next;
+ }
+ --frame;
+}
+
+function wake() {
+ clockNow = (clockLast = clock.now()) + clockSkew;
+ frame = timeout = 0;
+ try {
+ timerFlush();
+ } finally {
+ frame = 0;
+ nap();
+ clockNow = 0;
+ }
+}
+
+function poke() {
+ var now = clock.now(), delay = now - clockLast;
+ if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
+}
+
+function nap() {
+ var t0, t1 = taskHead, t2, time = Infinity;
+ while (t1) {
+ if (t1._call) {
+ if (time > t1._time) time = t1._time;
+ t0 = t1, t1 = t1._next;
+ } else {
+ t2 = t1._next, t1._next = null;
+ t1 = t0 ? t0._next = t2 : taskHead = t2;
+ }
+ }
+ taskTail = t0;
+ sleep(time);
+}
+
+function sleep(time) {
+ if (frame) return; // Soonest alarm already set, or will be.
+ if (timeout) timeout = clearTimeout(timeout);
+ var delay = time - clockNow;
+ if (delay > 24) {
+ if (time < Infinity) timeout = setTimeout(wake, delay);
+ if (interval) interval = clearInterval(interval);
+ } else {
+ if (!interval) clockLast = clockNow, interval = setInterval(poke, pokeDelay);
+ frame = 1, setFrame(wake);
+ }
+}
+
+var timeout$1 = function(callback, delay, time) {
+ var t = new Timer;
+ delay = delay == null ? 0 : +delay;
+ t.restart(function(elapsed) {
+ t.stop();
+ callback(elapsed + delay);
+ }, delay, time);
+ return t;
+};
+
+var interval$1 = function(callback, delay, time) {
+ var t = new Timer, total = delay;
+ if (delay == null) return t.restart(callback, delay, time), t;
+ delay = +delay, time = time == null ? now() : +time;
+ t.restart(function tick(elapsed) {
+ elapsed += total;
+ t.restart(tick, total += delay, time);
+ callback(elapsed);
+ }, delay, time);
+ return t;
+};
+
+var emptyOn = dispatch("start", "end", "interrupt");
+var emptyTween = [];
+
+var CREATED = 0;
+var SCHEDULED = 1;
+var STARTING = 2;
+var STARTED = 3;
+var RUNNING = 4;
+var ENDING = 5;
+var ENDED = 6;
+
+var schedule = function(node, name, id, index, group, timing) {
+ var schedules = node.__transition;
+ if (!schedules) node.__transition = {};
+ else if (id in schedules) return;
+ create(node, id, {
+ name: name,
+ index: index, // For context during callback.
+ group: group, // For context during callback.
+ on: emptyOn,
+ tween: emptyTween,
+ time: timing.time,
+ delay: timing.delay,
+ duration: timing.duration,
+ ease: timing.ease,
+ timer: null,
+ state: CREATED
+ });
+};
+
+function init(node, id) {
+ var schedule = node.__transition;
+ if (!schedule || !(schedule = schedule[id]) || schedule.state > CREATED) throw new Error("too late");
+ return schedule;
+}
+
+function set$1(node, id) {
+ var schedule = node.__transition;
+ if (!schedule || !(schedule = schedule[id]) || schedule.state > STARTING) throw new Error("too late");
+ return schedule;
+}
+
+function get$1(node, id) {
+ var schedule = node.__transition;
+ if (!schedule || !(schedule = schedule[id])) throw new Error("too late");
+ return schedule;
+}
+
+function create(node, id, self) {
+ var schedules = node.__transition,
+ tween;
+
+ // Initialize the self timer when the transition is created.
+ // Note the actual delay is not known until the first callback!
+ schedules[id] = self;
+ self.timer = timer(schedule, 0, self.time);
+
+ function schedule(elapsed) {
+ self.state = SCHEDULED;
+ self.timer.restart(start, self.delay, self.time);
+
+ // If the elapsed delay is less than our first sleep, start immediately.
+ if (self.delay <= elapsed) start(elapsed - self.delay);
+ }
+
+ function start(elapsed) {
+ var i, j, n, o;
+
+ // If the state is not SCHEDULED, then we previously errored on start.
+ if (self.state !== SCHEDULED) return stop();
+
+ for (i in schedules) {
+ o = schedules[i];
+ if (o.name !== self.name) continue;
+
+ // While this element already has a starting transition during this frame,
+ // defer starting an interrupting transition until that transition has a
+ // chance to tick (and possibly end); see d3/d3-transition#54!
+ if (o.state === STARTED) return timeout$1(start);
+
+ // Interrupt the active transition, if any.
+ // Dispatch the interrupt event.
+ if (o.state === RUNNING) {
+ o.state = ENDED;
+ o.timer.stop();
+ o.on.call("interrupt", node, node.__data__, o.index, o.group);
+ delete schedules[i];
+ }
+
+ // Cancel any pre-empted transitions. No interrupt event is dispatched
+ // because the cancelled transitions never started. Note that this also
+ // removes this transition from the pending list!
+ else if (+i < id) {
+ o.state = ENDED;
+ o.timer.stop();
+ delete schedules[i];
+ }
+ }
+
+ // Defer the first tick to end of the current frame; see d3/d3#1576.
+ // Note the transition may be canceled after start and before the first tick!
+ // Note this must be scheduled before the start event; see d3/d3-transition#16!
+ // Assuming this is successful, subsequent callbacks go straight to tick.
+ timeout$1(function() {
+ if (self.state === STARTED) {
+ self.state = RUNNING;
+ self.timer.restart(tick, self.delay, self.time);
+ tick(elapsed);
+ }
+ });
+
+ // Dispatch the start event.
+ // Note this must be done before the tween are initialized.
+ self.state = STARTING;
+ self.on.call("start", node, node.__data__, self.index, self.group);
+ if (self.state !== STARTING) return; // interrupted
+ self.state = STARTED;
+
+ // Initialize the tween, deleting null tween.
+ tween = new Array(n = self.tween.length);
+ for (i = 0, j = -1; i < n; ++i) {
+ if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
+ tween[++j] = o;
+ }
+ }
+ tween.length = j + 1;
+ }
+
+ function tick(elapsed) {
+ var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
+ i = -1,
+ n = tween.length;
+
+ while (++i < n) {
+ tween[i].call(null, t);
+ }
+
+ // Dispatch the end event.
+ if (self.state === ENDING) {
+ self.on.call("end", node, node.__data__, self.index, self.group);
+ stop();
+ }
+ }
+
+ function stop() {
+ self.state = ENDED;
+ self.timer.stop();
+ delete schedules[id];
+ for (var i in schedules) return; // eslint-disable-line no-unused-vars
+ delete node.__transition;
+ }
+}
+
+var interrupt = function(node, name) {
+ var schedules = node.__transition,
+ schedule$$1,
+ active,
+ empty = true,
+ i;
+
+ if (!schedules) return;
+
+ name = name == null ? null : name + "";
+
+ for (i in schedules) {
+ if ((schedule$$1 = schedules[i]).name !== name) { empty = false; continue; }
+ active = schedule$$1.state > STARTING && schedule$$1.state < ENDING;
+ schedule$$1.state = ENDED;
+ schedule$$1.timer.stop();
+ if (active) schedule$$1.on.call("interrupt", node, node.__data__, schedule$$1.index, schedule$$1.group);
+ delete schedules[i];
+ }
+
+ if (empty) delete node.__transition;
+};
+
+var selection_interrupt = function(name) {
+ return this.each(function() {
+ interrupt(this, name);
+ });
+};
+
+function tweenRemove(id, name) {
+ var tween0, tween1;
+ return function() {
+ var schedule$$1 = set$1(this, id),
+ tween = schedule$$1.tween;
+
+ // If this node shared tween with the previous node,
+ // just assign the updated shared tween and we’re done!
+ // Otherwise, copy-on-write.
+ if (tween !== tween0) {
+ tween1 = tween0 = tween;
+ for (var i = 0, n = tween1.length; i < n; ++i) {
+ if (tween1[i].name === name) {
+ tween1 = tween1.slice();
+ tween1.splice(i, 1);
+ break;
+ }
+ }
+ }
+
+ schedule$$1.tween = tween1;
+ };
+}
+
+function tweenFunction(id, name, value) {
+ var tween0, tween1;
+ if (typeof value !== "function") throw new Error;
+ return function() {
+ var schedule$$1 = set$1(this, id),
+ tween = schedule$$1.tween;
+
+ // If this node shared tween with the previous node,
+ // just assign the updated shared tween and we’re done!
+ // Otherwise, copy-on-write.
+ if (tween !== tween0) {
+ tween1 = (tween0 = tween).slice();
+ for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {
+ if (tween1[i].name === name) {
+ tween1[i] = t;
+ break;
+ }
+ }
+ if (i === n) tween1.push(t);
+ }
+
+ schedule$$1.tween = tween1;
+ };
+}
+
+var transition_tween = function(name, value) {
+ var id = this._id;
+
+ name += "";
+
+ if (arguments.length < 2) {
+ var tween = get$1(this.node(), id).tween;
+ for (var i = 0, n = tween.length, t; i < n; ++i) {
+ if ((t = tween[i]).name === name) {
+ return t.value;
+ }
+ }
+ return null;
+ }
+
+ return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
+};
+
+function tweenValue(transition, name, value) {
+ var id = transition._id;
+
+ transition.each(function() {
+ var schedule$$1 = set$1(this, id);
+ (schedule$$1.value || (schedule$$1.value = {}))[name] = value.apply(this, arguments);
+ });
+
+ return function(node) {
+ return get$1(node, id).value[name];
+ };
+}
+
+var interpolate = function(a, b) {
+ var c;
+ return (typeof b === "number" ? reinterpolate
+ : b instanceof color ? interpolateRgb
+ : (c = color(b)) ? (b = c, interpolateRgb)
+ : interpolateString)(a, b);
+};
+
+function attrRemove$1(name) {
+ return function() {
+ this.removeAttribute(name);
+ };
+}
+
+function attrRemoveNS$1(fullname) {
+ return function() {
+ this.removeAttributeNS(fullname.space, fullname.local);
+ };
+}
+
+function attrConstant$1(name, interpolate$$1, value1) {
+ var value00,
+ interpolate0;
+ return function() {
+ var value0 = this.getAttribute(name);
+ return value0 === value1 ? null
+ : value0 === value00 ? interpolate0
+ : interpolate0 = interpolate$$1(value00 = value0, value1);
+ };
+}
+
+function attrConstantNS$1(fullname, interpolate$$1, value1) {
+ var value00,
+ interpolate0;
+ return function() {
+ var value0 = this.getAttributeNS(fullname.space, fullname.local);
+ return value0 === value1 ? null
+ : value0 === value00 ? interpolate0
+ : interpolate0 = interpolate$$1(value00 = value0, value1);
+ };
+}
+
+function attrFunction$1(name, interpolate$$1, value) {
+ var value00,
+ value10,
+ interpolate0;
+ return function() {
+ var value0, value1 = value(this);
+ if (value1 == null) return void this.removeAttribute(name);
+ value0 = this.getAttribute(name);
+ return value0 === value1 ? null
+ : value0 === value00 && value1 === value10 ? interpolate0
+ : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
+ };
+}
+
+function attrFunctionNS$1(fullname, interpolate$$1, value) {
+ var value00,
+ value10,
+ interpolate0;
+ return function() {
+ var value0, value1 = value(this);
+ if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
+ value0 = this.getAttributeNS(fullname.space, fullname.local);
+ return value0 === value1 ? null
+ : value0 === value00 && value1 === value10 ? interpolate0
+ : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
+ };
+}
+
+var transition_attr = function(name, value) {
+ var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate;
+ return this.attrTween(name, typeof value === "function"
+ ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value))
+ : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname)
+ : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value + ""));
+};
+
+function attrTweenNS(fullname, value) {
+ function tween() {
+ var node = this, i = value.apply(node, arguments);
+ return i && function(t) {
+ node.setAttributeNS(fullname.space, fullname.local, i(t));
+ };
+ }
+ tween._value = value;
+ return tween;
+}
+
+function attrTween(name, value) {
+ function tween() {
+ var node = this, i = value.apply(node, arguments);
+ return i && function(t) {
+ node.setAttribute(name, i(t));
+ };
+ }
+ tween._value = value;
+ return tween;
+}
+
+var transition_attrTween = function(name, value) {
+ var key = "attr." + name;
+ if (arguments.length < 2) return (key = this.tween(key)) && key._value;
+ if (value == null) return this.tween(key, null);
+ if (typeof value !== "function") throw new Error;
+ var fullname = namespace(name);
+ return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
+};
+
+function delayFunction(id, value) {
+ return function() {
+ init(this, id).delay = +value.apply(this, arguments);
+ };
+}
+
+function delayConstant(id, value) {
+ return value = +value, function() {
+ init(this, id).delay = value;
+ };
+}
+
+var transition_delay = function(value) {
+ var id = this._id;
+
+ return arguments.length
+ ? this.each((typeof value === "function"
+ ? delayFunction
+ : delayConstant)(id, value))
+ : get$1(this.node(), id).delay;
+};
+
+function durationFunction(id, value) {
+ return function() {
+ set$1(this, id).duration = +value.apply(this, arguments);
+ };
+}
+
+function durationConstant(id, value) {
+ return value = +value, function() {
+ set$1(this, id).duration = value;
+ };
+}
+
+var transition_duration = function(value) {
+ var id = this._id;
+
+ return arguments.length
+ ? this.each((typeof value === "function"
+ ? durationFunction
+ : durationConstant)(id, value))
+ : get$1(this.node(), id).duration;
+};
+
+function easeConstant(id, value) {
+ if (typeof value !== "function") throw new Error;
+ return function() {
+ set$1(this, id).ease = value;
+ };
+}
+
+var transition_ease = function(value) {
+ var id = this._id;
+
+ return arguments.length
+ ? this.each(easeConstant(id, value))
+ : get$1(this.node(), id).ease;
+};
+
+var transition_filter = function(match) {
+ if (typeof match !== "function") match = matcher$1(match);
+
+ for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
+ if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
+ subgroup.push(node);
+ }
+ }
+ }
+
+ return new Transition(subgroups, this._parents, this._name, this._id);
+};
+
+var transition_merge = function(transition$$1) {
+ if (transition$$1._id !== this._id) throw new Error;
+
+ for (var groups0 = this._groups, groups1 = transition$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
+ for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
+ if (node = group0[i] || group1[i]) {
+ merge[i] = node;
+ }
+ }
+ }
+
+ for (; j < m0; ++j) {
+ merges[j] = groups0[j];
+ }
+
+ return new Transition(merges, this._parents, this._name, this._id);
+};
+
+function start(name) {
+ return (name + "").trim().split(/^|\s+/).every(function(t) {
+ var i = t.indexOf(".");
+ if (i >= 0) t = t.slice(0, i);
+ return !t || t === "start";
+ });
+}
+
+function onFunction(id, name, listener) {
+ var on0, on1, sit = start(name) ? init : set$1;
+ return function() {
+ var schedule$$1 = sit(this, id),
+ on = schedule$$1.on;
+
+ // If this node shared a dispatch with the previous node,
+ // just assign the updated shared dispatch and we’re done!
+ // Otherwise, copy-on-write.
+ if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
+
+ schedule$$1.on = on1;
+ };
+}
+
+var transition_on = function(name, listener) {
+ var id = this._id;
+
+ return arguments.length < 2
+ ? get$1(this.node(), id).on.on(name)
+ : this.each(onFunction(id, name, listener));
+};
+
+function removeFunction(id) {
+ return function() {
+ var parent = this.parentNode;
+ for (var i in this.__transition) if (+i !== id) return;
+ if (parent) parent.removeChild(this);
+ };
+}
+
+var transition_remove = function() {
+ return this.on("end.remove", removeFunction(this._id));
+};
+
+var transition_select = function(select$$1) {
+ var name = this._name,
+ id = this._id;
+
+ if (typeof select$$1 !== "function") select$$1 = selector(select$$1);
+
+ for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
+ if ((node = group[i]) && (subnode = select$$1.call(node, node.__data__, i, group))) {
+ if ("__data__" in node) subnode.__data__ = node.__data__;
+ subgroup[i] = subnode;
+ schedule(subgroup[i], name, id, i, subgroup, get$1(node, id));
+ }
+ }
+ }
+
+ return new Transition(subgroups, this._parents, name, id);
+};
+
+var transition_selectAll = function(select$$1) {
+ var name = this._name,
+ id = this._id;
+
+ if (typeof select$$1 !== "function") select$$1 = selectorAll(select$$1);
+
+ for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
+ if (node = group[i]) {
+ for (var children = select$$1.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) {
+ if (child = children[k]) {
+ schedule(child, name, id, k, children, inherit);
+ }
+ }
+ subgroups.push(children);
+ parents.push(node);
+ }
+ }
+ }
+
+ return new Transition(subgroups, parents, name, id);
+};
+
+var Selection$1 = selection.prototype.constructor;
+
+var transition_selection = function() {
+ return new Selection$1(this._groups, this._parents);
+};
+
+function styleRemove$1(name, interpolate$$1) {
+ var value00,
+ value10,
+ interpolate0;
+ return function() {
+ var value0 = styleValue(this, name),
+ value1 = (this.style.removeProperty(name), styleValue(this, name));
+ return value0 === value1 ? null
+ : value0 === value00 && value1 === value10 ? interpolate0
+ : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
+ };
+}
+
+function styleRemoveEnd(name) {
+ return function() {
+ this.style.removeProperty(name);
+ };
+}
+
+function styleConstant$1(name, interpolate$$1, value1) {
+ var value00,
+ interpolate0;
+ return function() {
+ var value0 = styleValue(this, name);
+ return value0 === value1 ? null
+ : value0 === value00 ? interpolate0
+ : interpolate0 = interpolate$$1(value00 = value0, value1);
+ };
+}
+
+function styleFunction$1(name, interpolate$$1, value) {
+ var value00,
+ value10,
+ interpolate0;
+ return function() {
+ var value0 = styleValue(this, name),
+ value1 = value(this);
+ if (value1 == null) value1 = (this.style.removeProperty(name), styleValue(this, name));
+ return value0 === value1 ? null
+ : value0 === value00 && value1 === value10 ? interpolate0
+ : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
+ };
+}
+
+var transition_style = function(name, value, priority) {
+ var i = (name += "") === "transform" ? interpolateTransformCss : interpolate;
+ return value == null ? this
+ .styleTween(name, styleRemove$1(name, i))
+ .on("end.style." + name, styleRemoveEnd(name))
+ : this.styleTween(name, typeof value === "function"
+ ? styleFunction$1(name, i, tweenValue(this, "style." + name, value))
+ : styleConstant$1(name, i, value + ""), priority);
+};
+
+function styleTween(name, value, priority) {
+ function tween() {
+ var node = this, i = value.apply(node, arguments);
+ return i && function(t) {
+ node.style.setProperty(name, i(t), priority);
+ };
+ }
+ tween._value = value;
+ return tween;
+}
+
+var transition_styleTween = function(name, value, priority) {
+ var key = "style." + (name += "");
+ if (arguments.length < 2) return (key = this.tween(key)) && key._value;
+ if (value == null) return this.tween(key, null);
+ if (typeof value !== "function") throw new Error;
+ return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
+};
+
+function textConstant$1(value) {
+ return function() {
+ this.textContent = value;
+ };
+}
+
+function textFunction$1(value) {
+ return function() {
+ var value1 = value(this);
+ this.textContent = value1 == null ? "" : value1;
+ };
+}
+
+var transition_text = function(value) {
+ return this.tween("text", typeof value === "function"
+ ? textFunction$1(tweenValue(this, "text", value))
+ : textConstant$1(value == null ? "" : value + ""));
+};
+
+var transition_transition = function() {
+ var name = this._name,
+ id0 = this._id,
+ id1 = newId();
+
+ for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
+ if (node = group[i]) {
+ var inherit = get$1(node, id0);
+ schedule(node, name, id1, i, group, {
+ time: inherit.time + inherit.delay + inherit.duration,
+ delay: 0,
+ duration: inherit.duration,
+ ease: inherit.ease
+ });
+ }
+ }
+ }
+
+ return new Transition(groups, this._parents, name, id1);
+};
+
+var id = 0;
+
+function Transition(groups, parents, name, id) {
+ this._groups = groups;
+ this._parents = parents;
+ this._name = name;
+ this._id = id;
+}
+
+function transition(name) {
+ return selection().transition(name);
+}
+
+function newId() {
+ return ++id;
+}
+
+var selection_prototype = selection.prototype;
+
+Transition.prototype = transition.prototype = {
+ constructor: Transition,
+ select: transition_select,
+ selectAll: transition_selectAll,
+ filter: transition_filter,
+ merge: transition_merge,
+ selection: transition_selection,
+ transition: transition_transition,
+ call: selection_prototype.call,
+ nodes: selection_prototype.nodes,
+ node: selection_prototype.node,
+ size: selection_prototype.size,
+ empty: selection_prototype.empty,
+ each: selection_prototype.each,
+ on: transition_on,
+ attr: transition_attr,
+ attrTween: transition_attrTween,
+ style: transition_style,
+ styleTween: transition_styleTween,
+ text: transition_text,
+ remove: transition_remove,
+ tween: transition_tween,
+ delay: transition_delay,
+ duration: transition_duration,
+ ease: transition_ease
+};
+
+function linear$1(t) {
+ return +t;
+}
+
+function quadIn(t) {
+ return t * t;
+}
+
+function quadOut(t) {
+ return t * (2 - t);
+}
+
+function quadInOut(t) {
+ return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;
+}
+
+function cubicIn(t) {
+ return t * t * t;
+}
+
+function cubicOut(t) {
+ return --t * t * t + 1;
+}
+
+function cubicInOut(t) {
+ return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
+}
+
+var exponent = 3;
+
+var polyIn = (function custom(e) {
+ e = +e;
+
+ function polyIn(t) {
+ return Math.pow(t, e);
+ }
+
+ polyIn.exponent = custom;
+
+ return polyIn;
+})(exponent);
+
+var polyOut = (function custom(e) {
+ e = +e;
+
+ function polyOut(t) {
+ return 1 - Math.pow(1 - t, e);
+ }
+
+ polyOut.exponent = custom;
+
+ return polyOut;
+})(exponent);
+
+var polyInOut = (function custom(e) {
+ e = +e;
+
+ function polyInOut(t) {
+ return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;
+ }
+
+ polyInOut.exponent = custom;
+
+ return polyInOut;
+})(exponent);
+
+var pi = Math.PI;
+var halfPi = pi / 2;
+
+function sinIn(t) {
+ return 1 - Math.cos(t * halfPi);
+}
+
+function sinOut(t) {
+ return Math.sin(t * halfPi);
+}
+
+function sinInOut(t) {
+ return (1 - Math.cos(pi * t)) / 2;
+}
+
+function expIn(t) {
+ return Math.pow(2, 10 * t - 10);
+}
+
+function expOut(t) {
+ return 1 - Math.pow(2, -10 * t);
+}
+
+function expInOut(t) {
+ return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;
+}
+
+function circleIn(t) {
+ return 1 - Math.sqrt(1 - t * t);
+}
+
+function circleOut(t) {
+ return Math.sqrt(1 - --t * t);
+}
+
+function circleInOut(t) {
+ return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;
+}
+
+var b1 = 4 / 11;
+var b2 = 6 / 11;
+var b3 = 8 / 11;
+var b4 = 3 / 4;
+var b5 = 9 / 11;
+var b6 = 10 / 11;
+var b7 = 15 / 16;
+var b8 = 21 / 22;
+var b9 = 63 / 64;
+var b0 = 1 / b1 / b1;
+
+function bounceIn(t) {
+ return 1 - bounceOut(1 - t);
+}
+
+function bounceOut(t) {
+ return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;
+}
+
+function bounceInOut(t) {
+ return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;
+}
+
+var overshoot = 1.70158;
+
+var backIn = (function custom(s) {
+ s = +s;
+
+ function backIn(t) {
+ return t * t * ((s + 1) * t - s);
+ }
+
+ backIn.overshoot = custom;
+
+ return backIn;
+})(overshoot);
+
+var backOut = (function custom(s) {
+ s = +s;
+
+ function backOut(t) {
+ return --t * t * ((s + 1) * t + s) + 1;
+ }
+
+ backOut.overshoot = custom;
+
+ return backOut;
+})(overshoot);
+
+var backInOut = (function custom(s) {
+ s = +s;
+
+ function backInOut(t) {
+ return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;
+ }
+
+ backInOut.overshoot = custom;
+
+ return backInOut;
+})(overshoot);
+
+var tau = 2 * Math.PI;
+var amplitude = 1;
+var period = 0.3;
+
+var elasticIn = (function custom(a, p) {
+ var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
+
+ function elasticIn(t) {
+ return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);
+ }
+
+ elasticIn.amplitude = function(a) { return custom(a, p * tau); };
+ elasticIn.period = function(p) { return custom(a, p); };
+
+ return elasticIn;
+})(amplitude, period);
+
+var elasticOut = (function custom(a, p) {
+ var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
+
+ function elasticOut(t) {
+ return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);
+ }
+
+ elasticOut.amplitude = function(a) { return custom(a, p * tau); };
+ elasticOut.period = function(p) { return custom(a, p); };
+
+ return elasticOut;
+})(amplitude, period);
+
+var elasticInOut = (function custom(a, p) {
+ var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
+
+ function elasticInOut(t) {
+ return ((t = t * 2 - 1) < 0
+ ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)
+ : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;
+ }
+
+ elasticInOut.amplitude = function(a) { return custom(a, p * tau); };
+ elasticInOut.period = function(p) { return custom(a, p); };
+
+ return elasticInOut;
+})(amplitude, period);
+
+var defaultTiming = {
+ time: null, // Set on use.
+ delay: 0,
+ duration: 250,
+ ease: cubicInOut
+};
+
+function inherit(node, id) {
+ var timing;
+ while (!(timing = node.__transition) || !(timing = timing[id])) {
+ if (!(node = node.parentNode)) {
+ return defaultTiming.time = now(), defaultTiming;
+ }
+ }
+ return timing;
+}
+
+var selection_transition = function(name) {
+ var id,
+ timing;
+
+ if (name instanceof Transition) {
+ id = name._id, name = name._name;
+ } else {
+ id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
+ }
+
+ for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
+ for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
+ if (node = group[i]) {
+ schedule(node, name, id, i, group, timing || inherit(node, id));
+ }
+ }
+ }
+
+ return new Transition(groups, this._parents, name, id);
+};
+
+selection.prototype.interrupt = selection_interrupt;
+selection.prototype.transition = selection_transition;
+
+var root$1 = [null];
+
+var active = function(node, name) {
+ var schedules = node.__transition,
+ schedule$$1,
+ i;
+
+ if (schedules) {
+ name = name == null ? null : name + "";
+ for (i in schedules) {
+ if ((schedule$$1 = schedules[i]).state > SCHEDULED && schedule$$1.name === name) {
+ return new Transition([[node]], root$1, name, +i);
+ }
+ }
+ }
+
+ return null;
+};
+
+var constant$4 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+var BrushEvent = function(target, type, selection) {
+ this.target = target;
+ this.type = type;
+ this.selection = selection;
+};
+
+function nopropagation$1() {
+ event.stopImmediatePropagation();
+}
+
+var noevent$1 = function() {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+};
+
+var MODE_DRAG = {name: "drag"};
+var MODE_SPACE = {name: "space"};
+var MODE_HANDLE = {name: "handle"};
+var MODE_CENTER = {name: "center"};
+
+var X = {
+ name: "x",
+ handles: ["e", "w"].map(type$2),
+ input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; },
+ output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }
+};
+
+var Y = {
+ name: "y",
+ handles: ["n", "s"].map(type$2),
+ input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; },
+ output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }
+};
+
+var XY = {
+ name: "xy",
+ handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type$2),
+ input: function(xy) { return xy; },
+ output: function(xy) { return xy; }
+};
+
+var cursors = {
+ overlay: "crosshair",
+ selection: "move",
+ n: "ns-resize",
+ e: "ew-resize",
+ s: "ns-resize",
+ w: "ew-resize",
+ nw: "nwse-resize",
+ ne: "nesw-resize",
+ se: "nwse-resize",
+ sw: "nesw-resize"
+};
+
+var flipX = {
+ e: "w",
+ w: "e",
+ nw: "ne",
+ ne: "nw",
+ se: "sw",
+ sw: "se"
+};
+
+var flipY = {
+ n: "s",
+ s: "n",
+ nw: "sw",
+ ne: "se",
+ se: "ne",
+ sw: "nw"
+};
+
+var signsX = {
+ overlay: +1,
+ selection: +1,
+ n: null,
+ e: +1,
+ s: null,
+ w: -1,
+ nw: -1,
+ ne: +1,
+ se: +1,
+ sw: -1
+};
+
+var signsY = {
+ overlay: +1,
+ selection: +1,
+ n: -1,
+ e: null,
+ s: +1,
+ w: null,
+ nw: -1,
+ ne: -1,
+ se: +1,
+ sw: +1
+};
+
+function type$2(t) {
+ return {type: t};
+}
+
+// Ignore right-click, since that should open the context menu.
+function defaultFilter() {
+ return !event.button;
+}
+
+function defaultExtent() {
+ var svg = this.ownerSVGElement || this;
+ return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];
+}
+
+// Like d3.local, but with the name “__brush” rather than auto-generated.
+function local$$1(node) {
+ while (!node.__brush) if (!(node = node.parentNode)) return;
+ return node.__brush;
+}
+
+function empty(extent) {
+ return extent[0][0] === extent[1][0]
+ || extent[0][1] === extent[1][1];
+}
+
+function brushSelection(node) {
+ var state = node.__brush;
+ return state ? state.dim.output(state.selection) : null;
+}
+
+function brushX() {
+ return brush$1(X);
+}
+
+function brushY() {
+ return brush$1(Y);
+}
+
+var brush = function() {
+ return brush$1(XY);
+};
+
+function brush$1(dim) {
+ var extent = defaultExtent,
+ filter = defaultFilter,
+ listeners = dispatch(brush, "start", "brush", "end"),
+ handleSize = 6,
+ touchending;
+
+ function brush(group) {
+ var overlay = group
+ .property("__brush", initialize)
+ .selectAll(".overlay")
+ .data([type$2("overlay")]);
+
+ overlay.enter().append("rect")
+ .attr("class", "overlay")
+ .attr("pointer-events", "all")
+ .attr("cursor", cursors.overlay)
+ .merge(overlay)
+ .each(function() {
+ var extent = local$$1(this).extent;
+ select(this)
+ .attr("x", extent[0][0])
+ .attr("y", extent[0][1])
+ .attr("width", extent[1][0] - extent[0][0])
+ .attr("height", extent[1][1] - extent[0][1]);
+ });
+
+ group.selectAll(".selection")
+ .data([type$2("selection")])
+ .enter().append("rect")
+ .attr("class", "selection")
+ .attr("cursor", cursors.selection)
+ .attr("fill", "#777")
+ .attr("fill-opacity", 0.3)
+ .attr("stroke", "#fff")
+ .attr("shape-rendering", "crispEdges");
+
+ var handle = group.selectAll(".handle")
+ .data(dim.handles, function(d) { return d.type; });
+
+ handle.exit().remove();
+
+ handle.enter().append("rect")
+ .attr("class", function(d) { return "handle handle--" + d.type; })
+ .attr("cursor", function(d) { return cursors[d.type]; });
+
+ group
+ .each(redraw)
+ .attr("fill", "none")
+ .attr("pointer-events", "all")
+ .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
+ .on("mousedown.brush touchstart.brush", started);
+ }
+
+ brush.move = function(group, selection) {
+ if (group.selection) {
+ group
+ .on("start.brush", function() { emitter(this, arguments).beforestart().start(); })
+ .on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); })
+ .tween("brush", function() {
+ var that = this,
+ state = that.__brush,
+ emit = emitter(that, arguments),
+ selection0 = state.selection,
+ selection1 = dim.input(typeof selection === "function" ? selection.apply(this, arguments) : selection, state.extent),
+ i = interpolateValue(selection0, selection1);
+
+ function tween(t) {
+ state.selection = t === 1 && empty(selection1) ? null : i(t);
+ redraw.call(that);
+ emit.brush();
+ }
+
+ return selection0 && selection1 ? tween : tween(1);
+ });
+ } else {
+ group
+ .each(function() {
+ var that = this,
+ args = arguments,
+ state = that.__brush,
+ selection1 = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent),
+ emit = emitter(that, args).beforestart();
+
+ interrupt(that);
+ state.selection = selection1 == null || empty(selection1) ? null : selection1;
+ redraw.call(that);
+ emit.start().brush().end();
+ });
+ }
+ };
+
+ function redraw() {
+ var group = select(this),
+ selection = local$$1(this).selection;
+
+ if (selection) {
+ group.selectAll(".selection")
+ .style("display", null)
+ .attr("x", selection[0][0])
+ .attr("y", selection[0][1])
+ .attr("width", selection[1][0] - selection[0][0])
+ .attr("height", selection[1][1] - selection[0][1]);
+
+ group.selectAll(".handle")
+ .style("display", null)
+ .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; })
+ .attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; })
+ .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; })
+ .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; });
+ }
+
+ else {
+ group.selectAll(".selection,.handle")
+ .style("display", "none")
+ .attr("x", null)
+ .attr("y", null)
+ .attr("width", null)
+ .attr("height", null);
+ }
+ }
+
+ function emitter(that, args) {
+ return that.__brush.emitter || new Emitter(that, args);
+ }
+
+ function Emitter(that, args) {
+ this.that = that;
+ this.args = args;
+ this.state = that.__brush;
+ this.active = 0;
+ }
+
+ Emitter.prototype = {
+ beforestart: function() {
+ if (++this.active === 1) this.state.emitter = this, this.starting = true;
+ return this;
+ },
+ start: function() {
+ if (this.starting) this.starting = false, this.emit("start");
+ return this;
+ },
+ brush: function() {
+ this.emit("brush");
+ return this;
+ },
+ end: function() {
+ if (--this.active === 0) delete this.state.emitter, this.emit("end");
+ return this;
+ },
+ emit: function(type) {
+ customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]);
+ }
+ };
+
+ function started() {
+ if (event.touches) { if (event.changedTouches.length < event.touches.length) return noevent$1(); }
+ else if (touchending) return;
+ if (!filter.apply(this, arguments)) return;
+
+ var that = this,
+ type = event.target.__data__.type,
+ mode = (event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (event.altKey ? MODE_CENTER : MODE_HANDLE),
+ signX = dim === Y ? null : signsX[type],
+ signY = dim === X ? null : signsY[type],
+ state = local$$1(that),
+ extent = state.extent,
+ selection = state.selection,
+ W = extent[0][0], w0, w1,
+ N = extent[0][1], n0, n1,
+ E = extent[1][0], e0, e1,
+ S = extent[1][1], s0, s1,
+ dx,
+ dy,
+ moving,
+ shifting = signX && signY && event.shiftKey,
+ lockX,
+ lockY,
+ point0 = mouse(that),
+ point = point0,
+ emit = emitter(that, arguments).beforestart();
+
+ if (type === "overlay") {
+ state.selection = selection = [
+ [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],
+ [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]
+ ];
+ } else {
+ w0 = selection[0][0];
+ n0 = selection[0][1];
+ e0 = selection[1][0];
+ s0 = selection[1][1];
+ }
+
+ w1 = w0;
+ n1 = n0;
+ e1 = e0;
+ s1 = s0;
+
+ var group = select(that)
+ .attr("pointer-events", "none");
+
+ var overlay = group.selectAll(".overlay")
+ .attr("cursor", cursors[type]);
+
+ if (event.touches) {
+ group
+ .on("touchmove.brush", moved, true)
+ .on("touchend.brush touchcancel.brush", ended, true);
+ } else {
+ var view = select(event.view)
+ .on("keydown.brush", keydowned, true)
+ .on("keyup.brush", keyupped, true)
+ .on("mousemove.brush", moved, true)
+ .on("mouseup.brush", ended, true);
+
+ dragDisable(event.view);
+ }
+
+ nopropagation$1();
+ interrupt(that);
+ redraw.call(that);
+ emit.start();
+
+ function moved() {
+ var point1 = mouse(that);
+ if (shifting && !lockX && !lockY) {
+ if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true;
+ else lockX = true;
+ }
+ point = point1;
+ moving = true;
+ noevent$1();
+ move();
+ }
+
+ function move() {
+ var t;
+
+ dx = point[0] - point0[0];
+ dy = point[1] - point0[1];
+
+ switch (mode) {
+ case MODE_SPACE:
+ case MODE_DRAG: {
+ if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;
+ if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;
+ break;
+ }
+ case MODE_HANDLE: {
+ if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0;
+ else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx;
+ if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0;
+ else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy;
+ break;
+ }
+ case MODE_CENTER: {
+ if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX));
+ if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY));
+ break;
+ }
+ }
+
+ if (e1 < w1) {
+ signX *= -1;
+ t = w0, w0 = e0, e0 = t;
+ t = w1, w1 = e1, e1 = t;
+ if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]);
+ }
+
+ if (s1 < n1) {
+ signY *= -1;
+ t = n0, n0 = s0, s0 = t;
+ t = n1, n1 = s1, s1 = t;
+ if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]);
+ }
+
+ if (state.selection) selection = state.selection; // May be set by brush.move!
+ if (lockX) w1 = selection[0][0], e1 = selection[1][0];
+ if (lockY) n1 = selection[0][1], s1 = selection[1][1];
+
+ if (selection[0][0] !== w1
+ || selection[0][1] !== n1
+ || selection[1][0] !== e1
+ || selection[1][1] !== s1) {
+ state.selection = [[w1, n1], [e1, s1]];
+ redraw.call(that);
+ emit.brush();
+ }
+ }
+
+ function ended() {
+ nopropagation$1();
+ if (event.touches) {
+ if (event.touches.length) return;
+ if (touchending) clearTimeout(touchending);
+ touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
+ group.on("touchmove.brush touchend.brush touchcancel.brush", null);
+ } else {
+ yesdrag(event.view, moving);
+ view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null);
+ }
+ group.attr("pointer-events", "all");
+ overlay.attr("cursor", cursors.overlay);
+ if (state.selection) selection = state.selection; // May be set by brush.move (on start)!
+ if (empty(selection)) state.selection = null, redraw.call(that);
+ emit.end();
+ }
+
+ function keydowned() {
+ switch (event.keyCode) {
+ case 16: { // SHIFT
+ shifting = signX && signY;
+ break;
+ }
+ case 18: { // ALT
+ if (mode === MODE_HANDLE) {
+ if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
+ if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
+ mode = MODE_CENTER;
+ move();
+ }
+ break;
+ }
+ case 32: { // SPACE; takes priority over ALT
+ if (mode === MODE_HANDLE || mode === MODE_CENTER) {
+ if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;
+ if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;
+ mode = MODE_SPACE;
+ overlay.attr("cursor", cursors.selection);
+ move();
+ }
+ break;
+ }
+ default: return;
+ }
+ noevent$1();
+ }
+
+ function keyupped() {
+ switch (event.keyCode) {
+ case 16: { // SHIFT
+ if (shifting) {
+ lockX = lockY = shifting = false;
+ move();
+ }
+ break;
+ }
+ case 18: { // ALT
+ if (mode === MODE_CENTER) {
+ if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
+ if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
+ mode = MODE_HANDLE;
+ move();
+ }
+ break;
+ }
+ case 32: { // SPACE
+ if (mode === MODE_SPACE) {
+ if (event.altKey) {
+ if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
+ if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
+ mode = MODE_CENTER;
+ } else {
+ if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
+ if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
+ mode = MODE_HANDLE;
+ }
+ overlay.attr("cursor", cursors[type]);
+ move();
+ }
+ break;
+ }
+ default: return;
+ }
+ noevent$1();
+ }
+ }
+
+ function initialize() {
+ var state = this.__brush || {selection: null};
+ state.extent = extent.apply(this, arguments);
+ state.dim = dim;
+ return state;
+ }
+
+ brush.extent = function(_) {
+ return arguments.length ? (extent = typeof _ === "function" ? _ : constant$4([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent;
+ };
+
+ brush.filter = function(_) {
+ return arguments.length ? (filter = typeof _ === "function" ? _ : constant$4(!!_), brush) : filter;
+ };
+
+ brush.handleSize = function(_) {
+ return arguments.length ? (handleSize = +_, brush) : handleSize;
+ };
+
+ brush.on = function() {
+ var value = listeners.on.apply(listeners, arguments);
+ return value === listeners ? brush : value;
+ };
+
+ return brush;
+}
+
+var cos = Math.cos;
+var sin = Math.sin;
+var pi$1 = Math.PI;
+var halfPi$1 = pi$1 / 2;
+var tau$1 = pi$1 * 2;
+var max$1 = Math.max;
+
+function compareValue(compare) {
+ return function(a, b) {
+ return compare(
+ a.source.value + a.target.value,
+ b.source.value + b.target.value
+ );
+ };
+}
+
+var chord = function() {
+ var padAngle = 0,
+ sortGroups = null,
+ sortSubgroups = null,
+ sortChords = null;
+
+ function chord(matrix) {
+ var n = matrix.length,
+ groupSums = [],
+ groupIndex = sequence(n),
+ subgroupIndex = [],
+ chords = [],
+ groups = chords.groups = new Array(n),
+ subgroups = new Array(n * n),
+ k,
+ x,
+ x0,
+ dx,
+ i,
+ j;
+
+ // Compute the sum.
+ k = 0, i = -1; while (++i < n) {
+ x = 0, j = -1; while (++j < n) {
+ x += matrix[i][j];
+ }
+ groupSums.push(x);
+ subgroupIndex.push(sequence(n));
+ k += x;
+ }
+
+ // Sort groups…
+ if (sortGroups) groupIndex.sort(function(a, b) {
+ return sortGroups(groupSums[a], groupSums[b]);
+ });
+
+ // Sort subgroups…
+ if (sortSubgroups) subgroupIndex.forEach(function(d, i) {
+ d.sort(function(a, b) {
+ return sortSubgroups(matrix[i][a], matrix[i][b]);
+ });
+ });
+
+ // Convert the sum to scaling factor for [0, 2pi].
+ // TODO Allow start and end angle to be specified?
+ // TODO Allow padding to be specified as percentage?
+ k = max$1(0, tau$1 - padAngle * n) / k;
+ dx = k ? padAngle : tau$1 / n;
+
+ // Compute the start and end angle for each group and subgroup.
+ // Note: Opera has a bug reordering object literal properties!
+ x = 0, i = -1; while (++i < n) {
+ x0 = x, j = -1; while (++j < n) {
+ var di = groupIndex[i],
+ dj = subgroupIndex[di][j],
+ v = matrix[di][dj],
+ a0 = x,
+ a1 = x += v * k;
+ subgroups[dj * n + di] = {
+ index: di,
+ subindex: dj,
+ startAngle: a0,
+ endAngle: a1,
+ value: v
+ };
+ }
+ groups[di] = {
+ index: di,
+ startAngle: x0,
+ endAngle: x,
+ value: groupSums[di]
+ };
+ x += dx;
+ }
+
+ // Generate chords for each (non-empty) subgroup-subgroup link.
+ i = -1; while (++i < n) {
+ j = i - 1; while (++j < n) {
+ var source = subgroups[j * n + i],
+ target = subgroups[i * n + j];
+ if (source.value || target.value) {
+ chords.push(source.value < target.value
+ ? {source: target, target: source}
+ : {source: source, target: target});
+ }
+ }
+ }
+
+ return sortChords ? chords.sort(sortChords) : chords;
+ }
+
+ chord.padAngle = function(_) {
+ return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle;
+ };
+
+ chord.sortGroups = function(_) {
+ return arguments.length ? (sortGroups = _, chord) : sortGroups;
+ };
+
+ chord.sortSubgroups = function(_) {
+ return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups;
+ };
+
+ chord.sortChords = function(_) {
+ return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._;
+ };
+
+ return chord;
+};
+
+var slice$2 = Array.prototype.slice;
+
+var constant$5 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+var pi$2 = Math.PI;
+var tau$2 = 2 * pi$2;
+var epsilon$1 = 1e-6;
+var tauEpsilon = tau$2 - epsilon$1;
+
+function Path() {
+ this._x0 = this._y0 = // start of current subpath
+ this._x1 = this._y1 = null; // end of current subpath
+ this._ = "";
+}
+
+function path() {
+ return new Path;
+}
+
+Path.prototype = path.prototype = {
+ constructor: Path,
+ moveTo: function(x, y) {
+ this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y);
+ },
+ closePath: function() {
+ if (this._x1 !== null) {
+ this._x1 = this._x0, this._y1 = this._y0;
+ this._ += "Z";
+ }
+ },
+ lineTo: function(x, y) {
+ this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y);
+ },
+ quadraticCurveTo: function(x1, y1, x, y) {
+ this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
+ },
+ bezierCurveTo: function(x1, y1, x2, y2, x, y) {
+ this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
+ },
+ arcTo: function(x1, y1, x2, y2, r) {
+ x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
+ var x0 = this._x1,
+ y0 = this._y1,
+ x21 = x2 - x1,
+ y21 = y2 - y1,
+ x01 = x0 - x1,
+ y01 = y0 - y1,
+ l01_2 = x01 * x01 + y01 * y01;
+
+ // Is the radius negative? Error.
+ if (r < 0) throw new Error("negative radius: " + r);
+
+ // Is this path empty? Move to (x1,y1).
+ if (this._x1 === null) {
+ this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1);
+ }
+
+ // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
+ else if (!(l01_2 > epsilon$1)) {}
+
+ // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
+ // Equivalently, is (x1,y1) coincident with (x2,y2)?
+ // Or, is the radius zero? Line to (x1,y1).
+ else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$1) || !r) {
+ this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1);
+ }
+
+ // Otherwise, draw an arc!
+ else {
+ var x20 = x2 - x0,
+ y20 = y2 - y0,
+ l21_2 = x21 * x21 + y21 * y21,
+ l20_2 = x20 * x20 + y20 * y20,
+ l21 = Math.sqrt(l21_2),
+ l01 = Math.sqrt(l01_2),
+ l = r * Math.tan((pi$2 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
+ t01 = l / l01,
+ t21 = l / l21;
+
+ // If the start tangent is not coincident with (x0,y0), line to.
+ if (Math.abs(t01 - 1) > epsilon$1) {
+ this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01);
+ }
+
+ this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21);
+ }
+ },
+ arc: function(x, y, r, a0, a1, ccw) {
+ x = +x, y = +y, r = +r;
+ var dx = r * Math.cos(a0),
+ dy = r * Math.sin(a0),
+ x0 = x + dx,
+ y0 = y + dy,
+ cw = 1 ^ ccw,
+ da = ccw ? a0 - a1 : a1 - a0;
+
+ // Is the radius negative? Error.
+ if (r < 0) throw new Error("negative radius: " + r);
+
+ // Is this path empty? Move to (x0,y0).
+ if (this._x1 === null) {
+ this._ += "M" + x0 + "," + y0;
+ }
+
+ // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
+ else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) {
+ this._ += "L" + x0 + "," + y0;
+ }
+
+ // Is this arc empty? We’re done.
+ if (!r) return;
+
+ // Does the angle go the wrong way? Flip the direction.
+ if (da < 0) da = da % tau$2 + tau$2;
+
+ // Is this a complete circle? Draw two arcs to complete the circle.
+ if (da > tauEpsilon) {
+ this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0);
+ }
+
+ // Is this arc non-empty? Draw an arc!
+ else if (da > epsilon$1) {
+ this._ += "A" + r + "," + r + ",0," + (+(da >= pi$2)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1));
+ }
+ },
+ rect: function(x, y, w, h) {
+ this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z";
+ },
+ toString: function() {
+ return this._;
+ }
+};
+
+function defaultSource(d) {
+ return d.source;
+}
+
+function defaultTarget(d) {
+ return d.target;
+}
+
+function defaultRadius(d) {
+ return d.radius;
+}
+
+function defaultStartAngle(d) {
+ return d.startAngle;
+}
+
+function defaultEndAngle(d) {
+ return d.endAngle;
+}
+
+var ribbon = function() {
+ var source = defaultSource,
+ target = defaultTarget,
+ radius = defaultRadius,
+ startAngle = defaultStartAngle,
+ endAngle = defaultEndAngle,
+ context = null;
+
+ function ribbon() {
+ var buffer,
+ argv = slice$2.call(arguments),
+ s = source.apply(this, argv),
+ t = target.apply(this, argv),
+ sr = +radius.apply(this, (argv[0] = s, argv)),
+ sa0 = startAngle.apply(this, argv) - halfPi$1,
+ sa1 = endAngle.apply(this, argv) - halfPi$1,
+ sx0 = sr * cos(sa0),
+ sy0 = sr * sin(sa0),
+ tr = +radius.apply(this, (argv[0] = t, argv)),
+ ta0 = startAngle.apply(this, argv) - halfPi$1,
+ ta1 = endAngle.apply(this, argv) - halfPi$1;
+
+ if (!context) context = buffer = path();
+
+ context.moveTo(sx0, sy0);
+ context.arc(0, 0, sr, sa0, sa1);
+ if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr?
+ context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0));
+ context.arc(0, 0, tr, ta0, ta1);
+ }
+ context.quadraticCurveTo(0, 0, sx0, sy0);
+ context.closePath();
+
+ if (buffer) return context = null, buffer + "" || null;
+ }
+
+ ribbon.radius = function(_) {
+ return arguments.length ? (radius = typeof _ === "function" ? _ : constant$5(+_), ribbon) : radius;
+ };
+
+ ribbon.startAngle = function(_) {
+ return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : startAngle;
+ };
+
+ ribbon.endAngle = function(_) {
+ return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : endAngle;
+ };
+
+ ribbon.source = function(_) {
+ return arguments.length ? (source = _, ribbon) : source;
+ };
+
+ ribbon.target = function(_) {
+ return arguments.length ? (target = _, ribbon) : target;
+ };
+
+ ribbon.context = function(_) {
+ return arguments.length ? ((context = _ == null ? null : _), ribbon) : context;
+ };
+
+ return ribbon;
+};
+
+var prefix = "$";
+
+function Map() {}
+
+Map.prototype = map$1.prototype = {
+ constructor: Map,
+ has: function(key) {
+ return (prefix + key) in this;
+ },
+ get: function(key) {
+ return this[prefix + key];
+ },
+ set: function(key, value) {
+ this[prefix + key] = value;
+ return this;
+ },
+ remove: function(key) {
+ var property = prefix + key;
+ return property in this && delete this[property];
+ },
+ clear: function() {
+ for (var property in this) if (property[0] === prefix) delete this[property];
+ },
+ keys: function() {
+ var keys = [];
+ for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
+ return keys;
+ },
+ values: function() {
+ var values = [];
+ for (var property in this) if (property[0] === prefix) values.push(this[property]);
+ return values;
+ },
+ entries: function() {
+ var entries = [];
+ for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
+ return entries;
+ },
+ size: function() {
+ var size = 0;
+ for (var property in this) if (property[0] === prefix) ++size;
+ return size;
+ },
+ empty: function() {
+ for (var property in this) if (property[0] === prefix) return false;
+ return true;
+ },
+ each: function(f) {
+ for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
+ }
+};
+
+function map$1(object, f) {
+ var map = new Map;
+
+ // Copy constructor.
+ if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });
+
+ // Index array by numeric index or specified key function.
+ else if (Array.isArray(object)) {
+ var i = -1,
+ n = object.length,
+ o;
+
+ if (f == null) while (++i < n) map.set(i, object[i]);
+ else while (++i < n) map.set(f(o = object[i], i, object), o);
+ }
+
+ // Convert object to map.
+ else if (object) for (var key in object) map.set(key, object[key]);
+
+ return map;
+}
+
+var nest = function() {
+ var keys = [],
+ sortKeys = [],
+ sortValues,
+ rollup,
+ nest;
+
+ function apply(array, depth, createResult, setResult) {
+ if (depth >= keys.length) return rollup != null
+ ? rollup(array) : (sortValues != null
+ ? array.sort(sortValues)
+ : array);
+
+ var i = -1,
+ n = array.length,
+ key = keys[depth++],
+ keyValue,
+ value,
+ valuesByKey = map$1(),
+ values,
+ result = createResult();
+
+ while (++i < n) {
+ if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) {
+ values.push(value);
+ } else {
+ valuesByKey.set(keyValue, [value]);
+ }
+ }
+
+ valuesByKey.each(function(values, key) {
+ setResult(result, key, apply(values, depth, createResult, setResult));
+ });
+
+ return result;
+ }
+
+ function entries(map, depth) {
+ if (++depth > keys.length) return map;
+ var array, sortKey = sortKeys[depth - 1];
+ if (rollup != null && depth >= keys.length) array = map.entries();
+ else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });
+ return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;
+ }
+
+ return nest = {
+ object: function(array) { return apply(array, 0, createObject, setObject); },
+ map: function(array) { return apply(array, 0, createMap, setMap); },
+ entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },
+ key: function(d) { keys.push(d); return nest; },
+ sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },
+ sortValues: function(order) { sortValues = order; return nest; },
+ rollup: function(f) { rollup = f; return nest; }
+ };
+};
+
+function createObject() {
+ return {};
+}
+
+function setObject(object, key, value) {
+ object[key] = value;
+}
+
+function createMap() {
+ return map$1();
+}
+
+function setMap(map, key, value) {
+ map.set(key, value);
+}
+
+function Set() {}
+
+var proto = map$1.prototype;
+
+Set.prototype = set$2.prototype = {
+ constructor: Set,
+ has: proto.has,
+ add: function(value) {
+ value += "";
+ this[prefix + value] = value;
+ return this;
+ },
+ remove: proto.remove,
+ clear: proto.clear,
+ values: proto.keys,
+ size: proto.size,
+ empty: proto.empty,
+ each: proto.each
+};
+
+function set$2(object, f) {
+ var set = new Set;
+
+ // Copy constructor.
+ if (object instanceof Set) object.each(function(value) { set.add(value); });
+
+ // Otherwise, assume it’s an array.
+ else if (object) {
+ var i = -1, n = object.length;
+ if (f == null) while (++i < n) set.add(object[i]);
+ else while (++i < n) set.add(f(object[i], i, object));
+ }
+
+ return set;
+}
+
+var keys = function(map) {
+ var keys = [];
+ for (var key in map) keys.push(key);
+ return keys;
+};
+
+var values = function(map) {
+ var values = [];
+ for (var key in map) values.push(map[key]);
+ return values;
+};
+
+var entries = function(map) {
+ var entries = [];
+ for (var key in map) entries.push({key: key, value: map[key]});
+ return entries;
+};
+
+function objectConverter(columns) {
+ return new Function("d", "return {" + columns.map(function(name, i) {
+ return JSON.stringify(name) + ": d[" + i + "]";
+ }).join(",") + "}");
+}
+
+function customConverter(columns, f) {
+ var object = objectConverter(columns);
+ return function(row, i) {
+ return f(object(row), i, columns);
+ };
+}
+
+// Compute unique columns in order of discovery.
+function inferColumns(rows) {
+ var columnSet = Object.create(null),
+ columns = [];
+
+ rows.forEach(function(row) {
+ for (var column in row) {
+ if (!(column in columnSet)) {
+ columns.push(columnSet[column] = column);
+ }
+ }
+ });
+
+ return columns;
+}
+
+var dsv = function(delimiter) {
+ var reFormat = new RegExp("[\"" + delimiter + "\n\r]"),
+ delimiterCode = delimiter.charCodeAt(0);
+
+ function parse(text, f) {
+ var convert, columns, rows = parseRows(text, function(row, i) {
+ if (convert) return convert(row, i - 1);
+ columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
+ });
+ rows.columns = columns;
+ return rows;
+ }
+
+ function parseRows(text, f) {
+ var EOL = {}, // sentinel value for end-of-line
+ EOF = {}, // sentinel value for end-of-file
+ rows = [], // output rows
+ N = text.length,
+ I = 0, // current character index
+ n = 0, // the current line number
+ t, // the current token
+ eol; // is the current token followed by EOL?
+
+ function token() {
+ if (I >= N) return EOF; // special case: end of file
+ if (eol) return eol = false, EOL; // special case: end of line
+
+ // special case: quotes
+ var j = I, c;
+ if (text.charCodeAt(j) === 34) {
+ var i = j;
+ while (i++ < N) {
+ if (text.charCodeAt(i) === 34) {
+ if (text.charCodeAt(i + 1) !== 34) break;
+ ++i;
+ }
+ }
+ I = i + 2;
+ c = text.charCodeAt(i + 1);
+ if (c === 13) {
+ eol = true;
+ if (text.charCodeAt(i + 2) === 10) ++I;
+ } else if (c === 10) {
+ eol = true;
+ }
+ return text.slice(j + 1, i).replace(/""/g, "\"");
+ }
+
+ // common case: find next delimiter or newline
+ while (I < N) {
+ var k = 1;
+ c = text.charCodeAt(I++);
+ if (c === 10) eol = true; // \n
+ else if (c === 13) { eol = true; if (text.charCodeAt(I) === 10) ++I, ++k; } // \r|\r\n
+ else if (c !== delimiterCode) continue;
+ return text.slice(j, I - k);
+ }
+
+ // special case: last token before EOF
+ return text.slice(j);
+ }
+
+ while ((t = token()) !== EOF) {
+ var a = [];
+ while (t !== EOL && t !== EOF) {
+ a.push(t);
+ t = token();
+ }
+ if (f && (a = f(a, n++)) == null) continue;
+ rows.push(a);
+ }
+
+ return rows;
+ }
+
+ function format(rows, columns) {
+ if (columns == null) columns = inferColumns(rows);
+ return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {
+ return columns.map(function(column) {
+ return formatValue(row[column]);
+ }).join(delimiter);
+ })).join("\n");
+ }
+
+ function formatRows(rows) {
+ return rows.map(formatRow).join("\n");
+ }
+
+ function formatRow(row) {
+ return row.map(formatValue).join(delimiter);
+ }
+
+ function formatValue(text) {
+ return text == null ? ""
+ : reFormat.test(text += "") ? "\"" + text.replace(/\"/g, "\"\"") + "\""
+ : text;
+ }
+
+ return {
+ parse: parse,
+ parseRows: parseRows,
+ format: format,
+ formatRows: formatRows
+ };
+};
+
+var csv = dsv(",");
+
+var csvParse = csv.parse;
+var csvParseRows = csv.parseRows;
+var csvFormat = csv.format;
+var csvFormatRows = csv.formatRows;
+
+var tsv = dsv("\t");
+
+var tsvParse = tsv.parse;
+var tsvParseRows = tsv.parseRows;
+var tsvFormat = tsv.format;
+var tsvFormatRows = tsv.formatRows;
+
+var center$1 = function(x, y) {
+ var nodes;
+
+ if (x == null) x = 0;
+ if (y == null) y = 0;
+
+ function force() {
+ var i,
+ n = nodes.length,
+ node,
+ sx = 0,
+ sy = 0;
+
+ for (i = 0; i < n; ++i) {
+ node = nodes[i], sx += node.x, sy += node.y;
+ }
+
+ for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) {
+ node = nodes[i], node.x -= sx, node.y -= sy;
+ }
+ }
+
+ force.initialize = function(_) {
+ nodes = _;
+ };
+
+ force.x = function(_) {
+ return arguments.length ? (x = +_, force) : x;
+ };
+
+ force.y = function(_) {
+ return arguments.length ? (y = +_, force) : y;
+ };
+
+ return force;
+};
+
+var constant$6 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+var jiggle = function() {
+ return (Math.random() - 0.5) * 1e-6;
+};
+
+var tree_add = function(d) {
+ var x = +this._x.call(null, d),
+ y = +this._y.call(null, d);
+ return add(this.cover(x, y), x, y, d);
+};
+
+function add(tree, x, y, d) {
+ if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points
+
+ var parent,
+ node = tree._root,
+ leaf = {data: d},
+ x0 = tree._x0,
+ y0 = tree._y0,
+ x1 = tree._x1,
+ y1 = tree._y1,
+ xm,
+ ym,
+ xp,
+ yp,
+ right,
+ bottom,
+ i,
+ j;
+
+ // If the tree is empty, initialize the root as a leaf.
+ if (!node) return tree._root = leaf, tree;
+
+ // Find the existing leaf for the new point, or add it.
+ while (node.length) {
+ if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
+ if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
+ if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
+ }
+
+ // Is the new point is exactly coincident with the existing point?
+ xp = +tree._x.call(null, node.data);
+ yp = +tree._y.call(null, node.data);
+ if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
+
+ // Otherwise, split the leaf node until the old and new point are separated.
+ do {
+ parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
+ if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
+ if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
+ } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));
+ return parent[j] = node, parent[i] = leaf, tree;
+}
+
+function addAll(data) {
+ var d, i, n = data.length,
+ x,
+ y,
+ xz = new Array(n),
+ yz = new Array(n),
+ x0 = Infinity,
+ y0 = Infinity,
+ x1 = -Infinity,
+ y1 = -Infinity;
+
+ // Compute the points and their extent.
+ for (i = 0; i < n; ++i) {
+ if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;
+ xz[i] = x;
+ yz[i] = y;
+ if (x < x0) x0 = x;
+ if (x > x1) x1 = x;
+ if (y < y0) y0 = y;
+ if (y > y1) y1 = y;
+ }
+
+ // If there were no (valid) points, inherit the existing extent.
+ if (x1 < x0) x0 = this._x0, x1 = this._x1;
+ if (y1 < y0) y0 = this._y0, y1 = this._y1;
+
+ // Expand the tree to cover the new points.
+ this.cover(x0, y0).cover(x1, y1);
+
+ // Add the new points.
+ for (i = 0; i < n; ++i) {
+ add(this, xz[i], yz[i], data[i]);
+ }
+
+ return this;
+}
+
+var tree_cover = function(x, y) {
+ if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points
+
+ var x0 = this._x0,
+ y0 = this._y0,
+ x1 = this._x1,
+ y1 = this._y1;
+
+ // If the quadtree has no extent, initialize them.
+ // Integer extent are necessary so that if we later double the extent,
+ // the existing quadrant boundaries don’t change due to floating point error!
+ if (isNaN(x0)) {
+ x1 = (x0 = Math.floor(x)) + 1;
+ y1 = (y0 = Math.floor(y)) + 1;
+ }
+
+ // Otherwise, double repeatedly to cover.
+ else if (x0 > x || x > x1 || y0 > y || y > y1) {
+ var z = x1 - x0,
+ node = this._root,
+ parent,
+ i;
+
+ switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) {
+ case 0: {
+ do parent = new Array(4), parent[i] = node, node = parent;
+ while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1);
+ break;
+ }
+ case 1: {
+ do parent = new Array(4), parent[i] = node, node = parent;
+ while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1);
+ break;
+ }
+ case 2: {
+ do parent = new Array(4), parent[i] = node, node = parent;
+ while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y);
+ break;
+ }
+ case 3: {
+ do parent = new Array(4), parent[i] = node, node = parent;
+ while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y);
+ break;
+ }
+ }
+
+ if (this._root && this._root.length) this._root = node;
+ }
+
+ // If the quadtree covers the point already, just return.
+ else return this;
+
+ this._x0 = x0;
+ this._y0 = y0;
+ this._x1 = x1;
+ this._y1 = y1;
+ return this;
+};
+
+var tree_data = function() {
+ var data = [];
+ this.visit(function(node) {
+ if (!node.length) do data.push(node.data); while (node = node.next)
+ });
+ return data;
+};
+
+var tree_extent = function(_) {
+ return arguments.length
+ ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])
+ : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];
+};
+
+var Quad = function(node, x0, y0, x1, y1) {
+ this.node = node;
+ this.x0 = x0;
+ this.y0 = y0;
+ this.x1 = x1;
+ this.y1 = y1;
+};
+
+var tree_find = function(x, y, radius) {
+ var data,
+ x0 = this._x0,
+ y0 = this._y0,
+ x1,
+ y1,
+ x2,
+ y2,
+ x3 = this._x1,
+ y3 = this._y1,
+ quads = [],
+ node = this._root,
+ q,
+ i;
+
+ if (node) quads.push(new Quad(node, x0, y0, x3, y3));
+ if (radius == null) radius = Infinity;
+ else {
+ x0 = x - radius, y0 = y - radius;
+ x3 = x + radius, y3 = y + radius;
+ radius *= radius;
+ }
+
+ while (q = quads.pop()) {
+
+ // Stop searching if this quadrant can’t contain a closer node.
+ if (!(node = q.node)
+ || (x1 = q.x0) > x3
+ || (y1 = q.y0) > y3
+ || (x2 = q.x1) < x0
+ || (y2 = q.y1) < y0) continue;
+
+ // Bisect the current quadrant.
+ if (node.length) {
+ var xm = (x1 + x2) / 2,
+ ym = (y1 + y2) / 2;
+
+ quads.push(
+ new Quad(node[3], xm, ym, x2, y2),
+ new Quad(node[2], x1, ym, xm, y2),
+ new Quad(node[1], xm, y1, x2, ym),
+ new Quad(node[0], x1, y1, xm, ym)
+ );
+
+ // Visit the closest quadrant first.
+ if (i = (y >= ym) << 1 | (x >= xm)) {
+ q = quads[quads.length - 1];
+ quads[quads.length - 1] = quads[quads.length - 1 - i];
+ quads[quads.length - 1 - i] = q;
+ }
+ }
+
+ // Visit this point. (Visiting coincident points isn’t necessary!)
+ else {
+ var dx = x - +this._x.call(null, node.data),
+ dy = y - +this._y.call(null, node.data),
+ d2 = dx * dx + dy * dy;
+ if (d2 < radius) {
+ var d = Math.sqrt(radius = d2);
+ x0 = x - d, y0 = y - d;
+ x3 = x + d, y3 = y + d;
+ data = node.data;
+ }
+ }
+ }
+
+ return data;
+};
+
+var tree_remove = function(d) {
+ if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points
+
+ var parent,
+ node = this._root,
+ retainer,
+ previous,
+ next,
+ x0 = this._x0,
+ y0 = this._y0,
+ x1 = this._x1,
+ y1 = this._y1,
+ x,
+ y,
+ xm,
+ ym,
+ right,
+ bottom,
+ i,
+ j;
+
+ // If the tree is empty, initialize the root as a leaf.
+ if (!node) return this;
+
+ // Find the leaf node for the point.
+ // While descending, also retain the deepest parent with a non-removed sibling.
+ if (node.length) while (true) {
+ if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
+ if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
+ if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
+ if (!node.length) break;
+ if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;
+ }
+
+ // Find the point to remove.
+ while (node.data !== d) if (!(previous = node, node = node.next)) return this;
+ if (next = node.next) delete node.next;
+
+ // If there are multiple coincident points, remove just the point.
+ if (previous) return (next ? previous.next = next : delete previous.next), this;
+
+ // If this is the root point, remove it.
+ if (!parent) return this._root = next, this;
+
+ // Remove this leaf.
+ next ? parent[i] = next : delete parent[i];
+
+ // If the parent now contains exactly one leaf, collapse superfluous parents.
+ if ((node = parent[0] || parent[1] || parent[2] || parent[3])
+ && node === (parent[3] || parent[2] || parent[1] || parent[0])
+ && !node.length) {
+ if (retainer) retainer[j] = node;
+ else this._root = node;
+ }
+
+ return this;
+};
+
+function removeAll(data) {
+ for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
+ return this;
+}
+
+var tree_root = function() {
+ return this._root;
+};
+
+var tree_size = function() {
+ var size = 0;
+ this.visit(function(node) {
+ if (!node.length) do ++size; while (node = node.next)
+ });
+ return size;
+};
+
+var tree_visit = function(callback) {
+ var quads = [], q, node = this._root, child, x0, y0, x1, y1;
+ if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));
+ while (q = quads.pop()) {
+ if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
+ var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
+ if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
+ if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
+ if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
+ if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
+ }
+ }
+ return this;
+};
+
+var tree_visitAfter = function(callback) {
+ var quads = [], next = [], q;
+ if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));
+ while (q = quads.pop()) {
+ var node = q.node;
+ if (node.length) {
+ var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
+ if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
+ if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
+ if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
+ if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
+ }
+ next.push(q);
+ }
+ while (q = next.pop()) {
+ callback(q.node, q.x0, q.y0, q.x1, q.y1);
+ }
+ return this;
+};
+
+function defaultX(d) {
+ return d[0];
+}
+
+var tree_x = function(_) {
+ return arguments.length ? (this._x = _, this) : this._x;
+};
+
+function defaultY(d) {
+ return d[1];
+}
+
+var tree_y = function(_) {
+ return arguments.length ? (this._y = _, this) : this._y;
+};
+
+function quadtree(nodes, x, y) {
+ var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN);
+ return nodes == null ? tree : tree.addAll(nodes);
+}
+
+function Quadtree(x, y, x0, y0, x1, y1) {
+ this._x = x;
+ this._y = y;
+ this._x0 = x0;
+ this._y0 = y0;
+ this._x1 = x1;
+ this._y1 = y1;
+ this._root = undefined;
+}
+
+function leaf_copy(leaf) {
+ var copy = {data: leaf.data}, next = copy;
+ while (leaf = leaf.next) next = next.next = {data: leaf.data};
+ return copy;
+}
+
+var treeProto = quadtree.prototype = Quadtree.prototype;
+
+treeProto.copy = function() {
+ var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),
+ node = this._root,
+ nodes,
+ child;
+
+ if (!node) return copy;
+
+ if (!node.length) return copy._root = leaf_copy(node), copy;
+
+ nodes = [{source: node, target: copy._root = new Array(4)}];
+ while (node = nodes.pop()) {
+ for (var i = 0; i < 4; ++i) {
+ if (child = node.source[i]) {
+ if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});
+ else node.target[i] = leaf_copy(child);
+ }
+ }
+ }
+
+ return copy;
+};
+
+treeProto.add = tree_add;
+treeProto.addAll = addAll;
+treeProto.cover = tree_cover;
+treeProto.data = tree_data;
+treeProto.extent = tree_extent;
+treeProto.find = tree_find;
+treeProto.remove = tree_remove;
+treeProto.removeAll = removeAll;
+treeProto.root = tree_root;
+treeProto.size = tree_size;
+treeProto.visit = tree_visit;
+treeProto.visitAfter = tree_visitAfter;
+treeProto.x = tree_x;
+treeProto.y = tree_y;
+
+function x(d) {
+ return d.x + d.vx;
+}
+
+function y(d) {
+ return d.y + d.vy;
+}
+
+var collide = function(radius) {
+ var nodes,
+ radii,
+ strength = 1,
+ iterations = 1;
+
+ if (typeof radius !== "function") radius = constant$6(radius == null ? 1 : +radius);
+
+ function force() {
+ var i, n = nodes.length,
+ tree,
+ node,
+ xi,
+ yi,
+ ri,
+ ri2;
+
+ for (var k = 0; k < iterations; ++k) {
+ tree = quadtree(nodes, x, y).visitAfter(prepare);
+ for (i = 0; i < n; ++i) {
+ node = nodes[i];
+ ri = radii[node.index], ri2 = ri * ri;
+ xi = node.x + node.vx;
+ yi = node.y + node.vy;
+ tree.visit(apply);
+ }
+ }
+
+ function apply(quad, x0, y0, x1, y1) {
+ var data = quad.data, rj = quad.r, r = ri + rj;
+ if (data) {
+ if (data.index > node.index) {
+ var x = xi - data.x - data.vx,
+ y = yi - data.y - data.vy,
+ l = x * x + y * y;
+ if (l < r * r) {
+ if (x === 0) x = jiggle(), l += x * x;
+ if (y === 0) y = jiggle(), l += y * y;
+ l = (r - (l = Math.sqrt(l))) / l * strength;
+ node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));
+ node.vy += (y *= l) * r;
+ data.vx -= x * (r = 1 - r);
+ data.vy -= y * r;
+ }
+ }
+ return;
+ }
+ return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
+ }
+ }
+
+ function prepare(quad) {
+ if (quad.data) return quad.r = radii[quad.data.index];
+ for (var i = quad.r = 0; i < 4; ++i) {
+ if (quad[i] && quad[i].r > quad.r) {
+ quad.r = quad[i].r;
+ }
+ }
+ }
+
+ function initialize() {
+ if (!nodes) return;
+ var i, n = nodes.length, node;
+ radii = new Array(n);
+ for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
+ }
+
+ force.initialize = function(_) {
+ nodes = _;
+ initialize();
+ };
+
+ force.iterations = function(_) {
+ return arguments.length ? (iterations = +_, force) : iterations;
+ };
+
+ force.strength = function(_) {
+ return arguments.length ? (strength = +_, force) : strength;
+ };
+
+ force.radius = function(_) {
+ return arguments.length ? (radius = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : radius;
+ };
+
+ return force;
+};
+
+function index$3(d) {
+ return d.index;
+}
+
+function find(nodeById, nodeId) {
+ var node = nodeById.get(nodeId);
+ if (!node) throw new Error("missing: " + nodeId);
+ return node;
+}
+
+var link = function(links) {
+ var id = index$3,
+ strength = defaultStrength,
+ strengths,
+ distance = constant$6(30),
+ distances,
+ nodes,
+ count,
+ bias,
+ iterations = 1;
+
+ if (links == null) links = [];
+
+ function defaultStrength(link) {
+ return 1 / Math.min(count[link.source.index], count[link.target.index]);
+ }
+
+ function force(alpha) {
+ for (var k = 0, n = links.length; k < iterations; ++k) {
+ for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {
+ link = links[i], source = link.source, target = link.target;
+ x = target.x + target.vx - source.x - source.vx || jiggle();
+ y = target.y + target.vy - source.y - source.vy || jiggle();
+ l = Math.sqrt(x * x + y * y);
+ l = (l - distances[i]) / l * alpha * strengths[i];
+ x *= l, y *= l;
+ target.vx -= x * (b = bias[i]);
+ target.vy -= y * b;
+ source.vx += x * (b = 1 - b);
+ source.vy += y * b;
+ }
+ }
+ }
+
+ function initialize() {
+ if (!nodes) return;
+
+ var i,
+ n = nodes.length,
+ m = links.length,
+ nodeById = map$1(nodes, id),
+ link;
+
+ for (i = 0, count = new Array(n); i < m; ++i) {
+ link = links[i], link.index = i;
+ if (typeof link.source !== "object") link.source = find(nodeById, link.source);
+ if (typeof link.target !== "object") link.target = find(nodeById, link.target);
+ count[link.source.index] = (count[link.source.index] || 0) + 1;
+ count[link.target.index] = (count[link.target.index] || 0) + 1;
+ }
+
+ for (i = 0, bias = new Array(m); i < m; ++i) {
+ link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);
+ }
+
+ strengths = new Array(m), initializeStrength();
+ distances = new Array(m), initializeDistance();
+ }
+
+ function initializeStrength() {
+ if (!nodes) return;
+
+ for (var i = 0, n = links.length; i < n; ++i) {
+ strengths[i] = +strength(links[i], i, links);
+ }
+ }
+
+ function initializeDistance() {
+ if (!nodes) return;
+
+ for (var i = 0, n = links.length; i < n; ++i) {
+ distances[i] = +distance(links[i], i, links);
+ }
+ }
+
+ force.initialize = function(_) {
+ nodes = _;
+ initialize();
+ };
+
+ force.links = function(_) {
+ return arguments.length ? (links = _, initialize(), force) : links;
+ };
+
+ force.id = function(_) {
+ return arguments.length ? (id = _, force) : id;
+ };
+
+ force.iterations = function(_) {
+ return arguments.length ? (iterations = +_, force) : iterations;
+ };
+
+ force.strength = function(_) {
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initializeStrength(), force) : strength;
+ };
+
+ force.distance = function(_) {
+ return arguments.length ? (distance = typeof _ === "function" ? _ : constant$6(+_), initializeDistance(), force) : distance;
+ };
+
+ return force;
+};
+
+function x$1(d) {
+ return d.x;
+}
+
+function y$1(d) {
+ return d.y;
+}
+
+var initialRadius = 10;
+var initialAngle = Math.PI * (3 - Math.sqrt(5));
+
+var simulation = function(nodes) {
+ var simulation,
+ alpha = 1,
+ alphaMin = 0.001,
+ alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
+ alphaTarget = 0,
+ velocityDecay = 0.6,
+ forces = map$1(),
+ stepper = timer(step),
+ event = dispatch("tick", "end");
+
+ if (nodes == null) nodes = [];
+
+ function step() {
+ tick();
+ event.call("tick", simulation);
+ if (alpha < alphaMin) {
+ stepper.stop();
+ event.call("end", simulation);
+ }
+ }
+
+ function tick() {
+ var i, n = nodes.length, node;
+
+ alpha += (alphaTarget - alpha) * alphaDecay;
+
+ forces.each(function(force) {
+ force(alpha);
+ });
+
+ for (i = 0; i < n; ++i) {
+ node = nodes[i];
+ if (node.fx == null) node.x += node.vx *= velocityDecay;
+ else node.x = node.fx, node.vx = 0;
+ if (node.fy == null) node.y += node.vy *= velocityDecay;
+ else node.y = node.fy, node.vy = 0;
+ }
+ }
+
+ function initializeNodes() {
+ for (var i = 0, n = nodes.length, node; i < n; ++i) {
+ node = nodes[i], node.index = i;
+ if (isNaN(node.x) || isNaN(node.y)) {
+ var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;
+ node.x = radius * Math.cos(angle);
+ node.y = radius * Math.sin(angle);
+ }
+ if (isNaN(node.vx) || isNaN(node.vy)) {
+ node.vx = node.vy = 0;
+ }
+ }
+ }
+
+ function initializeForce(force) {
+ if (force.initialize) force.initialize(nodes);
+ return force;
+ }
+
+ initializeNodes();
+
+ return simulation = {
+ tick: tick,
+
+ restart: function() {
+ return stepper.restart(step), simulation;
+ },
+
+ stop: function() {
+ return stepper.stop(), simulation;
+ },
+
+ nodes: function(_) {
+ return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;
+ },
+
+ alpha: function(_) {
+ return arguments.length ? (alpha = +_, simulation) : alpha;
+ },
+
+ alphaMin: function(_) {
+ return arguments.length ? (alphaMin = +_, simulation) : alphaMin;
+ },
+
+ alphaDecay: function(_) {
+ return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;
+ },
+
+ alphaTarget: function(_) {
+ return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
+ },
+
+ velocityDecay: function(_) {
+ return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;
+ },
+
+ force: function(name, _) {
+ return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);
+ },
+
+ find: function(x, y, radius) {
+ var i = 0,
+ n = nodes.length,
+ dx,
+ dy,
+ d2,
+ node,
+ closest;
+
+ if (radius == null) radius = Infinity;
+ else radius *= radius;
+
+ for (i = 0; i < n; ++i) {
+ node = nodes[i];
+ dx = x - node.x;
+ dy = y - node.y;
+ d2 = dx * dx + dy * dy;
+ if (d2 < radius) closest = node, radius = d2;
+ }
+
+ return closest;
+ },
+
+ on: function(name, _) {
+ return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
+ }
+ };
+};
+
+var manyBody = function() {
+ var nodes,
+ node,
+ alpha,
+ strength = constant$6(-30),
+ strengths,
+ distanceMin2 = 1,
+ distanceMax2 = Infinity,
+ theta2 = 0.81;
+
+ function force(_) {
+ var i, n = nodes.length, tree = quadtree(nodes, x$1, y$1).visitAfter(accumulate);
+ for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
+ }
+
+ function initialize() {
+ if (!nodes) return;
+ var i, n = nodes.length, node;
+ strengths = new Array(n);
+ for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);
+ }
+
+ function accumulate(quad) {
+ var strength = 0, q, c, x, y, i;
+
+ // For internal nodes, accumulate forces from child quadrants.
+ if (quad.length) {
+ for (x = y = i = 0; i < 4; ++i) {
+ if ((q = quad[i]) && (c = q.value)) {
+ strength += c, x += c * q.x, y += c * q.y;
+ }
+ }
+ quad.x = x / strength;
+ quad.y = y / strength;
+ }
+
+ // For leaf nodes, accumulate forces from coincident quadrants.
+ else {
+ q = quad;
+ q.x = q.data.x;
+ q.y = q.data.y;
+ do strength += strengths[q.data.index];
+ while (q = q.next);
+ }
+
+ quad.value = strength;
+ }
+
+ function apply(quad, x1, _, x2) {
+ if (!quad.value) return true;
+
+ var x = quad.x - node.x,
+ y = quad.y - node.y,
+ w = x2 - x1,
+ l = x * x + y * y;
+
+ // Apply the Barnes-Hut approximation if possible.
+ // Limit forces for very close nodes; randomize direction if coincident.
+ if (w * w / theta2 < l) {
+ if (l < distanceMax2) {
+ if (x === 0) x = jiggle(), l += x * x;
+ if (y === 0) y = jiggle(), l += y * y;
+ if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
+ node.vx += x * quad.value * alpha / l;
+ node.vy += y * quad.value * alpha / l;
+ }
+ return true;
+ }
+
+ // Otherwise, process points directly.
+ else if (quad.length || l >= distanceMax2) return;
+
+ // Limit forces for very close nodes; randomize direction if coincident.
+ if (quad.data !== node || quad.next) {
+ if (x === 0) x = jiggle(), l += x * x;
+ if (y === 0) y = jiggle(), l += y * y;
+ if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
+ }
+
+ do if (quad.data !== node) {
+ w = strengths[quad.data.index] * alpha / l;
+ node.vx += x * w;
+ node.vy += y * w;
+ } while (quad = quad.next);
+ }
+
+ force.initialize = function(_) {
+ nodes = _;
+ initialize();
+ };
+
+ force.strength = function(_) {
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : strength;
+ };
+
+ force.distanceMin = function(_) {
+ return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
+ };
+
+ force.distanceMax = function(_) {
+ return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
+ };
+
+ force.theta = function(_) {
+ return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
+ };
+
+ return force;
+};
+
+var x$2 = function(x) {
+ var strength = constant$6(0.1),
+ nodes,
+ strengths,
+ xz;
+
+ if (typeof x !== "function") x = constant$6(x == null ? 0 : +x);
+
+ function force(alpha) {
+ for (var i = 0, n = nodes.length, node; i < n; ++i) {
+ node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
+ }
+ }
+
+ function initialize() {
+ if (!nodes) return;
+ var i, n = nodes.length;
+ strengths = new Array(n);
+ xz = new Array(n);
+ for (i = 0; i < n; ++i) {
+ strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
+ }
+ }
+
+ force.initialize = function(_) {
+ nodes = _;
+ initialize();
+ };
+
+ force.strength = function(_) {
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : strength;
+ };
+
+ force.x = function(_) {
+ return arguments.length ? (x = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : x;
+ };
+
+ return force;
+};
+
+var y$2 = function(y) {
+ var strength = constant$6(0.1),
+ nodes,
+ strengths,
+ yz;
+
+ if (typeof y !== "function") y = constant$6(y == null ? 0 : +y);
+
+ function force(alpha) {
+ for (var i = 0, n = nodes.length, node; i < n; ++i) {
+ node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
+ }
+ }
+
+ function initialize() {
+ if (!nodes) return;
+ var i, n = nodes.length;
+ strengths = new Array(n);
+ yz = new Array(n);
+ for (i = 0; i < n; ++i) {
+ strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
+ }
+ }
+
+ force.initialize = function(_) {
+ nodes = _;
+ initialize();
+ };
+
+ force.strength = function(_) {
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : strength;
+ };
+
+ force.y = function(_) {
+ return arguments.length ? (y = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : y;
+ };
+
+ return force;
+};
+
+// Computes the decimal coefficient and exponent of the specified number x with
+// significant digits p, where x is positive and p is in [1, 21] or undefined.
+// For example, formatDecimal(1.23) returns ["123", 0].
+var formatDecimal = function(x, p) {
+ if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
+ var i, coefficient = x.slice(0, i);
+
+ // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
+ // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
+ return [
+ coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
+ +x.slice(i + 1)
+ ];
+};
+
+var exponent$1 = function(x) {
+ return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
+};
+
+var formatGroup = function(grouping, thousands) {
+ return function(value, width) {
+ var i = value.length,
+ t = [],
+ j = 0,
+ g = grouping[0],
+ length = 0;
+
+ while (i > 0 && g > 0) {
+ if (length + g + 1 > width) g = Math.max(1, width - length);
+ t.push(value.substring(i -= g, i + g));
+ if ((length += g + 1) > width) break;
+ g = grouping[j = (j + 1) % grouping.length];
+ }
+
+ return t.reverse().join(thousands);
+ };
+};
+
+var formatNumerals = function(numerals) {
+ return function(value) {
+ return value.replace(/[0-9]/g, function(i) {
+ return numerals[+i];
+ });
+ };
+};
+
+var formatDefault = function(x, p) {
+ x = x.toPrecision(p);
+
+ out: for (var n = x.length, i = 1, i0 = -1, i1; i < n; ++i) {
+ switch (x[i]) {
+ case ".": i0 = i1 = i; break;
+ case "0": if (i0 === 0) i0 = i; i1 = i; break;
+ case "e": break out;
+ default: if (i0 > 0) i0 = 0; break;
+ }
+ }
+
+ return i0 > 0 ? x.slice(0, i0) + x.slice(i1 + 1) : x;
+};
+
+var prefixExponent;
+
+var formatPrefixAuto = function(x, p) {
+ var d = formatDecimal(x, p);
+ if (!d) return x + "";
+ var coefficient = d[0],
+ exponent = d[1],
+ i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
+ n = coefficient.length;
+ return i === n ? coefficient
+ : i > n ? coefficient + new Array(i - n + 1).join("0")
+ : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
+ : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
+};
+
+var formatRounded = function(x, p) {
+ var d = formatDecimal(x, p);
+ if (!d) return x + "";
+ var coefficient = d[0],
+ exponent = d[1];
+ return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
+ : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
+ : coefficient + new Array(exponent - coefficient.length + 2).join("0");
+};
+
+var formatTypes = {
+ "": formatDefault,
+ "%": function(x, p) { return (x * 100).toFixed(p); },
+ "b": function(x) { return Math.round(x).toString(2); },
+ "c": function(x) { return x + ""; },
+ "d": function(x) { return Math.round(x).toString(10); },
+ "e": function(x, p) { return x.toExponential(p); },
+ "f": function(x, p) { return x.toFixed(p); },
+ "g": function(x, p) { return x.toPrecision(p); },
+ "o": function(x) { return Math.round(x).toString(8); },
+ "p": function(x, p) { return formatRounded(x * 100, p); },
+ "r": formatRounded,
+ "s": formatPrefixAuto,
+ "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
+ "x": function(x) { return Math.round(x).toString(16); }
+};
+
+// [[fill]align][sign][symbol][0][width][,][.precision][type]
+var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;
+
+function formatSpecifier(specifier) {
+ return new FormatSpecifier(specifier);
+}
+
+formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
+
+function FormatSpecifier(specifier) {
+ if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
+
+ var match,
+ fill = match[1] || " ",
+ align = match[2] || ">",
+ sign = match[3] || "-",
+ symbol = match[4] || "",
+ zero = !!match[5],
+ width = match[6] && +match[6],
+ comma = !!match[7],
+ precision = match[8] && +match[8].slice(1),
+ type = match[9] || "";
+
+ // The "n" type is an alias for ",g".
+ if (type === "n") comma = true, type = "g";
+
+ // Map invalid types to the default format.
+ else if (!formatTypes[type]) type = "";
+
+ // If zero fill is specified, padding goes after sign and before digits.
+ if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
+
+ this.fill = fill;
+ this.align = align;
+ this.sign = sign;
+ this.symbol = symbol;
+ this.zero = zero;
+ this.width = width;
+ this.comma = comma;
+ this.precision = precision;
+ this.type = type;
+}
+
+FormatSpecifier.prototype.toString = function() {
+ return this.fill
+ + this.align
+ + this.sign
+ + this.symbol
+ + (this.zero ? "0" : "")
+ + (this.width == null ? "" : Math.max(1, this.width | 0))
+ + (this.comma ? "," : "")
+ + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0))
+ + this.type;
+};
+
+var identity$3 = function(x) {
+ return x;
+};
+
+var prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
+
+var formatLocale = function(locale) {
+ var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3,
+ currency = locale.currency,
+ decimal = locale.decimal,
+ numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$3,
+ percent = locale.percent || "%";
+
+ function newFormat(specifier) {
+ specifier = formatSpecifier(specifier);
+
+ var fill = specifier.fill,
+ align = specifier.align,
+ sign = specifier.sign,
+ symbol = specifier.symbol,
+ zero = specifier.zero,
+ width = specifier.width,
+ comma = specifier.comma,
+ precision = specifier.precision,
+ type = specifier.type;
+
+ // Compute the prefix and suffix.
+ // For SI-prefix, the suffix is lazily computed.
+ var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
+ suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : "";
+
+ // What format function should we use?
+ // Is this an integer type?
+ // Can this type generate exponential notation?
+ var formatType = formatTypes[type],
+ maybeSuffix = !type || /[defgprs%]/.test(type);
+
+ // Set the default precision if not specified,
+ // or clamp the specified precision to the supported range.
+ // For significant precision, it must be in [1, 21].
+ // For fixed precision, it must be in [0, 20].
+ precision = precision == null ? (type ? 6 : 12)
+ : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
+ : Math.max(0, Math.min(20, precision));
+
+ function format(value) {
+ var valuePrefix = prefix,
+ valueSuffix = suffix,
+ i, n, c;
+
+ if (type === "c") {
+ valueSuffix = formatType(value) + valueSuffix;
+ value = "";
+ } else {
+ value = +value;
+
+ // Perform the initial formatting.
+ var valueNegative = value < 0;
+ value = formatType(Math.abs(value), precision);
+
+ // If a negative value rounds to zero during formatting, treat as positive.
+ if (valueNegative && +value === 0) valueNegative = false;
+
+ // Compute the prefix and suffix.
+ valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
+ valueSuffix = valueSuffix + (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + (valueNegative && sign === "(" ? ")" : "");
+
+ // Break the formatted value into the integer “value” part that can be
+ // grouped, and fractional or exponential “suffix” part that is not.
+ if (maybeSuffix) {
+ i = -1, n = value.length;
+ while (++i < n) {
+ if (c = value.charCodeAt(i), 48 > c || c > 57) {
+ valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
+ value = value.slice(0, i);
+ break;
+ }
+ }
+ }
+ }
+
+ // If the fill character is not "0", grouping is applied before padding.
+ if (comma && !zero) value = group(value, Infinity);
+
+ // Compute the padding.
+ var length = valuePrefix.length + value.length + valueSuffix.length,
+ padding = length < width ? new Array(width - length + 1).join(fill) : "";
+
+ // If the fill character is "0", grouping is applied after padding.
+ if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
+
+ // Reconstruct the final output based on the desired alignment.
+ switch (align) {
+ case "<": value = valuePrefix + value + valueSuffix + padding; break;
+ case "=": value = valuePrefix + padding + value + valueSuffix; break;
+ case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
+ default: value = padding + valuePrefix + value + valueSuffix; break;
+ }
+
+ return numerals(value);
+ }
+
+ format.toString = function() {
+ return specifier + "";
+ };
+
+ return format;
+ }
+
+ function formatPrefix(specifier, value) {
+ var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
+ e = Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3,
+ k = Math.pow(10, -e),
+ prefix = prefixes[8 + e / 3];
+ return function(value) {
+ return f(k * value) + prefix;
+ };
+ }
+
+ return {
+ format: newFormat,
+ formatPrefix: formatPrefix
+ };
+};
+
+var locale$1;
+var format;
+var formatPrefix;
+
+defaultLocale({
+ decimal: ".",
+ thousands: ",",
+ grouping: [3],
+ currency: ["$", ""]
+});
+
+function defaultLocale(definition) {
+ locale$1 = formatLocale(definition);
+ format = locale$1.format;
+ formatPrefix = locale$1.formatPrefix;
+ return locale$1;
+}
+
+var precisionFixed = function(step) {
+ return Math.max(0, -exponent$1(Math.abs(step)));
+};
+
+var precisionPrefix = function(step, value) {
+ return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3 - exponent$1(Math.abs(step)));
+};
+
+var precisionRound = function(step, max) {
+ step = Math.abs(step), max = Math.abs(max) - step;
+ return Math.max(0, exponent$1(max) - exponent$1(step)) + 1;
+};
+
+// Adds floating point numbers with twice the normal precision.
+// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
+// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
+// 305–363 (1997).
+// Code adapted from GeographicLib by Charles F. F. Karney,
+// http://geographiclib.sourceforge.net/
+
+var adder = function() {
+ return new Adder;
+};
+
+function Adder() {
+ this.reset();
+}
+
+Adder.prototype = {
+ constructor: Adder,
+ reset: function() {
+ this.s = // rounded value
+ this.t = 0; // exact error
+ },
+ add: function(y) {
+ add$1(temp, y, this.t);
+ add$1(this, temp.s, this.s);
+ if (this.s) this.t += temp.t;
+ else this.s = temp.t;
+ },
+ valueOf: function() {
+ return this.s;
+ }
+};
+
+var temp = new Adder;
+
+function add$1(adder, a, b) {
+ var x = adder.s = a + b,
+ bv = x - a,
+ av = x - bv;
+ adder.t = (a - av) + (b - bv);
+}
+
+var epsilon$2 = 1e-6;
+var epsilon2$1 = 1e-12;
+var pi$3 = Math.PI;
+var halfPi$2 = pi$3 / 2;
+var quarterPi = pi$3 / 4;
+var tau$3 = pi$3 * 2;
+
+var degrees$1 = 180 / pi$3;
+var radians = pi$3 / 180;
+
+var abs = Math.abs;
+var atan = Math.atan;
+var atan2 = Math.atan2;
+var cos$1 = Math.cos;
+var ceil = Math.ceil;
+var exp = Math.exp;
+
+var log = Math.log;
+var pow = Math.pow;
+var sin$1 = Math.sin;
+var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
+var sqrt = Math.sqrt;
+var tan = Math.tan;
+
+function acos(x) {
+ return x > 1 ? 0 : x < -1 ? pi$3 : Math.acos(x);
+}
+
+function asin(x) {
+ return x > 1 ? halfPi$2 : x < -1 ? -halfPi$2 : Math.asin(x);
+}
+
+function haversin(x) {
+ return (x = sin$1(x / 2)) * x;
+}
+
+function noop$1() {}
+
+function streamGeometry(geometry, stream) {
+ if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
+ streamGeometryType[geometry.type](geometry, stream);
+ }
+}
+
+var streamObjectType = {
+ Feature: function(object, stream) {
+ streamGeometry(object.geometry, stream);
+ },
+ FeatureCollection: function(object, stream) {
+ var features = object.features, i = -1, n = features.length;
+ while (++i < n) streamGeometry(features[i].geometry, stream);
+ }
+};
+
+var streamGeometryType = {
+ Sphere: function(object, stream) {
+ stream.sphere();
+ },
+ Point: function(object, stream) {
+ object = object.coordinates;
+ stream.point(object[0], object[1], object[2]);
+ },
+ MultiPoint: function(object, stream) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);
+ },
+ LineString: function(object, stream) {
+ streamLine(object.coordinates, stream, 0);
+ },
+ MultiLineString: function(object, stream) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) streamLine(coordinates[i], stream, 0);
+ },
+ Polygon: function(object, stream) {
+ streamPolygon(object.coordinates, stream);
+ },
+ MultiPolygon: function(object, stream) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) streamPolygon(coordinates[i], stream);
+ },
+ GeometryCollection: function(object, stream) {
+ var geometries = object.geometries, i = -1, n = geometries.length;
+ while (++i < n) streamGeometry(geometries[i], stream);
+ }
+};
+
+function streamLine(coordinates, stream, closed) {
+ var i = -1, n = coordinates.length - closed, coordinate;
+ stream.lineStart();
+ while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
+ stream.lineEnd();
+}
+
+function streamPolygon(coordinates, stream) {
+ var i = -1, n = coordinates.length;
+ stream.polygonStart();
+ while (++i < n) streamLine(coordinates[i], stream, 1);
+ stream.polygonEnd();
+}
+
+var geoStream = function(object, stream) {
+ if (object && streamObjectType.hasOwnProperty(object.type)) {
+ streamObjectType[object.type](object, stream);
+ } else {
+ streamGeometry(object, stream);
+ }
+};
+
+var areaRingSum = adder();
+
+var areaSum = adder();
+var lambda00;
+var phi00;
+var lambda0;
+var cosPhi0;
+var sinPhi0;
+
+var areaStream = {
+ point: noop$1,
+ lineStart: noop$1,
+ lineEnd: noop$1,
+ polygonStart: function() {
+ areaRingSum.reset();
+ areaStream.lineStart = areaRingStart;
+ areaStream.lineEnd = areaRingEnd;
+ },
+ polygonEnd: function() {
+ var areaRing = +areaRingSum;
+ areaSum.add(areaRing < 0 ? tau$3 + areaRing : areaRing);
+ this.lineStart = this.lineEnd = this.point = noop$1;
+ },
+ sphere: function() {
+ areaSum.add(tau$3);
+ }
+};
+
+function areaRingStart() {
+ areaStream.point = areaPointFirst;
+}
+
+function areaRingEnd() {
+ areaPoint(lambda00, phi00);
+}
+
+function areaPointFirst(lambda, phi) {
+ areaStream.point = areaPoint;
+ lambda00 = lambda, phi00 = phi;
+ lambda *= radians, phi *= radians;
+ lambda0 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi);
+}
+
+function areaPoint(lambda, phi) {
+ lambda *= radians, phi *= radians;
+ phi = phi / 2 + quarterPi; // 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 dLambda = lambda - lambda0,
+ sdLambda = dLambda >= 0 ? 1 : -1,
+ adLambda = sdLambda * dLambda,
+ cosPhi = cos$1(phi),
+ sinPhi = sin$1(phi),
+ k = sinPhi0 * sinPhi,
+ u = cosPhi0 * cosPhi + k * cos$1(adLambda),
+ v = k * sdLambda * sin$1(adLambda);
+ areaRingSum.add(atan2(v, u));
+
+ // Advance the previous points.
+ lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
+}
+
+var area = function(object) {
+ areaSum.reset();
+ geoStream(object, areaStream);
+ return areaSum * 2;
+};
+
+function spherical(cartesian) {
+ return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
+}
+
+function cartesian(spherical) {
+ var lambda = spherical[0], phi = spherical[1], cosPhi = cos$1(phi);
+ return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)];
+}
+
+function cartesianDot(a, b) {
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+}
+
+function cartesianCross(a, b) {
+ return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
+}
+
+// TODO return a
+function cartesianAddInPlace(a, b) {
+ a[0] += b[0], a[1] += b[1], a[2] += b[2];
+}
+
+function cartesianScale(vector, k) {
+ return [vector[0] * k, vector[1] * k, vector[2] * k];
+}
+
+// TODO return d
+function cartesianNormalizeInPlace(d) {
+ var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
+ d[0] /= l, d[1] /= l, d[2] /= l;
+}
+
+var lambda0$1;
+var phi0;
+var lambda1;
+var phi1;
+var lambda2;
+var lambda00$1;
+var phi00$1;
+var p0;
+var deltaSum = adder();
+var ranges;
+var range;
+
+var boundsStream = {
+ point: boundsPoint,
+ lineStart: boundsLineStart,
+ lineEnd: boundsLineEnd,
+ polygonStart: function() {
+ boundsStream.point = boundsRingPoint;
+ boundsStream.lineStart = boundsRingStart;
+ boundsStream.lineEnd = boundsRingEnd;
+ deltaSum.reset();
+ areaStream.polygonStart();
+ },
+ polygonEnd: function() {
+ areaStream.polygonEnd();
+ boundsStream.point = boundsPoint;
+ boundsStream.lineStart = boundsLineStart;
+ boundsStream.lineEnd = boundsLineEnd;
+ if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
+ else if (deltaSum > epsilon$2) phi1 = 90;
+ else if (deltaSum < -epsilon$2) phi0 = -90;
+ range[0] = lambda0$1, range[1] = lambda1;
+ }
+};
+
+function boundsPoint(lambda, phi) {
+ ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
+ if (phi < phi0) phi0 = phi;
+ if (phi > phi1) phi1 = phi;
+}
+
+function linePoint(lambda, phi) {
+ var p = cartesian([lambda * radians, phi * radians]);
+ if (p0) {
+ var normal = cartesianCross(p0, p),
+ equatorial = [normal[1], -normal[0], 0],
+ inflection = cartesianCross(equatorial, normal);
+ cartesianNormalizeInPlace(inflection);
+ inflection = spherical(inflection);
+ var delta = lambda - lambda2,
+ sign$$1 = delta > 0 ? 1 : -1,
+ lambdai = inflection[0] * degrees$1 * sign$$1,
+ phii,
+ antimeridian = abs(delta) > 180;
+ if (antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
+ phii = inflection[1] * degrees$1;
+ if (phii > phi1) phi1 = phii;
+ } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
+ phii = -inflection[1] * degrees$1;
+ if (phii < phi0) phi0 = phii;
+ } else {
+ if (phi < phi0) phi0 = phi;
+ if (phi > phi1) phi1 = phi;
+ }
+ if (antimeridian) {
+ if (lambda < lambda2) {
+ if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
+ } else {
+ if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
+ }
+ } else {
+ if (lambda1 >= lambda0$1) {
+ if (lambda < lambda0$1) lambda0$1 = lambda;
+ if (lambda > lambda1) lambda1 = lambda;
+ } else {
+ if (lambda > lambda2) {
+ if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
+ } else {
+ if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
+ }
+ }
+ }
+ } else {
+ ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
+ }
+ if (phi < phi0) phi0 = phi;
+ if (phi > phi1) phi1 = phi;
+ p0 = p, lambda2 = lambda;
+}
+
+function boundsLineStart() {
+ boundsStream.point = linePoint;
+}
+
+function boundsLineEnd() {
+ range[0] = lambda0$1, range[1] = lambda1;
+ boundsStream.point = boundsPoint;
+ p0 = null;
+}
+
+function boundsRingPoint(lambda, phi) {
+ if (p0) {
+ var delta = lambda - lambda2;
+ deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
+ } else {
+ lambda00$1 = lambda, phi00$1 = phi;
+ }
+ areaStream.point(lambda, phi);
+ linePoint(lambda, phi);
+}
+
+function boundsRingStart() {
+ areaStream.lineStart();
+}
+
+function boundsRingEnd() {
+ boundsRingPoint(lambda00$1, phi00$1);
+ areaStream.lineEnd();
+ if (abs(deltaSum) > epsilon$2) lambda0$1 = -(lambda1 = 180);
+ range[0] = lambda0$1, range[1] = lambda1;
+ p0 = null;
+}
+
+// Finds the left-right distance between two longitudes.
+// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
+// the distance between ±180° to be 360°.
+function angle(lambda0, lambda1) {
+ return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
+}
+
+function rangeCompare(a, b) {
+ return a[0] - b[0];
+}
+
+function rangeContains(range, x) {
+ return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
+}
+
+var bounds = function(feature) {
+ var i, n, a, b, merged, deltaMax, delta;
+
+ phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
+ ranges = [];
+ geoStream(feature, boundsStream);
+
+ // First, sort ranges by their minimum longitudes.
+ if (n = ranges.length) {
+ ranges.sort(rangeCompare);
+
+ // Then, merge any ranges that overlap.
+ for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
+ b = ranges[i];
+ if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
+ 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.
+ for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
+ b = merged[i];
+ if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
+ }
+ }
+
+ ranges = range = null;
+
+ return lambda0$1 === Infinity || phi0 === Infinity
+ ? [[NaN, NaN], [NaN, NaN]]
+ : [[lambda0$1, phi0], [lambda1, phi1]];
+};
+
+var W0;
+var W1;
+var X0;
+var Y0;
+var Z0;
+var X1;
+var Y1;
+var Z1;
+var X2;
+var Y2;
+var Z2;
+var lambda00$2;
+var phi00$2;
+var x0;
+var y0;
+var z0; // previous point
+
+var centroidStream = {
+ sphere: noop$1,
+ point: centroidPoint,
+ lineStart: centroidLineStart,
+ lineEnd: centroidLineEnd,
+ polygonStart: function() {
+ centroidStream.lineStart = centroidRingStart;
+ centroidStream.lineEnd = centroidRingEnd;
+ },
+ polygonEnd: function() {
+ centroidStream.lineStart = centroidLineStart;
+ centroidStream.lineEnd = centroidLineEnd;
+ }
+};
+
+// Arithmetic mean of Cartesian vectors.
+function centroidPoint(lambda, phi) {
+ lambda *= radians, phi *= radians;
+ var cosPhi = cos$1(phi);
+ centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi));
+}
+
+function centroidPointCartesian(x, y, z) {
+ ++W0;
+ X0 += (x - X0) / W0;
+ Y0 += (y - Y0) / W0;
+ Z0 += (z - Z0) / W0;
+}
+
+function centroidLineStart() {
+ centroidStream.point = centroidLinePointFirst;
+}
+
+function centroidLinePointFirst(lambda, phi) {
+ lambda *= radians, phi *= radians;
+ var cosPhi = cos$1(phi);
+ x0 = cosPhi * cos$1(lambda);
+ y0 = cosPhi * sin$1(lambda);
+ z0 = sin$1(phi);
+ centroidStream.point = centroidLinePoint;
+ centroidPointCartesian(x0, y0, z0);
+}
+
+function centroidLinePoint(lambda, phi) {
+ lambda *= radians, phi *= radians;
+ var cosPhi = cos$1(phi),
+ x = cosPhi * cos$1(lambda),
+ y = cosPhi * sin$1(lambda),
+ z = sin$1(phi),
+ w = atan2(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);
+ W1 += w;
+ X1 += w * (x0 + (x0 = x));
+ Y1 += w * (y0 + (y0 = y));
+ Z1 += w * (z0 + (z0 = z));
+ centroidPointCartesian(x0, y0, z0);
+}
+
+function centroidLineEnd() {
+ centroidStream.point = centroidPoint;
+}
+
+// See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
+// J. Applied Mechanics 42, 239 (1975).
+function centroidRingStart() {
+ centroidStream.point = centroidRingPointFirst;
+}
+
+function centroidRingEnd() {
+ centroidRingPoint(lambda00$2, phi00$2);
+ centroidStream.point = centroidPoint;
+}
+
+function centroidRingPointFirst(lambda, phi) {
+ lambda00$2 = lambda, phi00$2 = phi;
+ lambda *= radians, phi *= radians;
+ centroidStream.point = centroidRingPoint;
+ var cosPhi = cos$1(phi);
+ x0 = cosPhi * cos$1(lambda);
+ y0 = cosPhi * sin$1(lambda);
+ z0 = sin$1(phi);
+ centroidPointCartesian(x0, y0, z0);
+}
+
+function centroidRingPoint(lambda, phi) {
+ lambda *= radians, phi *= radians;
+ var cosPhi = cos$1(phi),
+ x = cosPhi * cos$1(lambda),
+ y = cosPhi * sin$1(lambda),
+ z = sin$1(phi),
+ cx = y0 * z - z0 * y,
+ cy = z0 * x - x0 * z,
+ cz = x0 * y - y0 * x,
+ m = sqrt(cx * cx + cy * cy + cz * cz),
+ w = asin(m), // line weight = angle
+ v = m && -w / m; // area weight multiplier
+ X2 += v * cx;
+ Y2 += v * cy;
+ Z2 += v * cz;
+ W1 += w;
+ X1 += w * (x0 + (x0 = x));
+ Y1 += w * (y0 + (y0 = y));
+ Z1 += w * (z0 + (z0 = z));
+ centroidPointCartesian(x0, y0, z0);
+}
+
+var d3GeoCentroid = function(object) {
+ W0 = W1 =
+ X0 = Y0 = Z0 =
+ X1 = Y1 = Z1 =
+ X2 = Y2 = Z2 = 0;
+ geoStream(object, centroidStream);
+
+ var x = X2,
+ y = Y2,
+ z = Z2,
+ m = x * x + y * y + z * z;
+
+ // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
+ if (m < epsilon2$1) {
+ x = X1, y = Y1, z = Z1;
+ // If the feature has zero length, fall back to arithmetic mean of point vectors.
+ if (W1 < epsilon$2) x = X0, y = Y0, z = Z0;
+ m = x * x + y * y + z * z;
+ // If the feature still has an undefined ccentroid, then return.
+ if (m < epsilon2$1) return [NaN, NaN];
+ }
+
+ return [atan2(y, x) * degrees$1, asin(z / sqrt(m)) * degrees$1];
+};
+
+var constant$7 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+var compose = function(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 rotationIdentity(lambda, phi) {
+ return [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi];
+}
+
+rotationIdentity.invert = rotationIdentity;
+
+function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
+ return (deltaLambda %= tau$3) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))
+ : rotationLambda(deltaLambda))
+ : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)
+ : rotationIdentity);
+}
+
+function forwardRotationLambda(deltaLambda) {
+ return function(lambda, phi) {
+ return lambda += deltaLambda, [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi];
+ };
+}
+
+function rotationLambda(deltaLambda) {
+ var rotation = forwardRotationLambda(deltaLambda);
+ rotation.invert = forwardRotationLambda(-deltaLambda);
+ return rotation;
+}
+
+function rotationPhiGamma(deltaPhi, deltaGamma) {
+ var cosDeltaPhi = cos$1(deltaPhi),
+ sinDeltaPhi = sin$1(deltaPhi),
+ cosDeltaGamma = cos$1(deltaGamma),
+ sinDeltaGamma = sin$1(deltaGamma);
+
+ function rotation(lambda, phi) {
+ var cosPhi = cos$1(phi),
+ x = cos$1(lambda) * cosPhi,
+ y = sin$1(lambda) * cosPhi,
+ z = sin$1(phi),
+ k = z * cosDeltaPhi + x * sinDeltaPhi;
+ return [
+ atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),
+ asin(k * cosDeltaGamma + y * sinDeltaGamma)
+ ];
+ }
+
+ rotation.invert = function(lambda, phi) {
+ var cosPhi = cos$1(phi),
+ x = cos$1(lambda) * cosPhi,
+ y = sin$1(lambda) * cosPhi,
+ z = sin$1(phi),
+ k = z * cosDeltaGamma - y * sinDeltaGamma;
+ return [
+ atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),
+ asin(k * cosDeltaPhi - x * sinDeltaPhi)
+ ];
+ };
+
+ return rotation;
+}
+
+var rotation = function(rotate) {
+ rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
+
+ function forward(coordinates) {
+ coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
+ return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
+ }
+
+ forward.invert = function(coordinates) {
+ coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
+ return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
+ };
+
+ return forward;
+};
+
+// Generates a circle centered at [0°, 0°], with a given radius and precision.
+function circleStream(stream, radius, delta, direction, t0, t1) {
+ if (!delta) return;
+ var cosRadius = cos$1(radius),
+ sinRadius = sin$1(radius),
+ step = direction * delta;
+ if (t0 == null) {
+ t0 = radius + direction * tau$3;
+ t1 = radius - step / 2;
+ } else {
+ t0 = circleRadius(cosRadius, t0);
+ t1 = circleRadius(cosRadius, t1);
+ if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$3;
+ }
+ for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
+ point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]);
+ stream.point(point[0], point[1]);
+ }
+}
+
+// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
+function circleRadius(cosRadius, point) {
+ point = cartesian(point), point[0] -= cosRadius;
+ cartesianNormalizeInPlace(point);
+ var radius = acos(-point[1]);
+ return ((-point[2] < 0 ? -radius : radius) + tau$3 - epsilon$2) % tau$3;
+}
+
+var circle = function() {
+ var center = constant$7([0, 0]),
+ radius = constant$7(90),
+ precision = constant$7(6),
+ ring,
+ rotate,
+ stream = {point: point};
+
+ function point(x, y) {
+ ring.push(x = rotate(x, y));
+ x[0] *= degrees$1, x[1] *= degrees$1;
+ }
+
+ function circle() {
+ var c = center.apply(this, arguments),
+ r = radius.apply(this, arguments) * radians,
+ p = precision.apply(this, arguments) * radians;
+ ring = [];
+ rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert;
+ circleStream(stream, r, p, 1);
+ c = {type: "Polygon", coordinates: [ring]};
+ ring = rotate = null;
+ return c;
+ }
+
+ circle.center = function(_) {
+ return arguments.length ? (center = typeof _ === "function" ? _ : constant$7([+_[0], +_[1]]), circle) : center;
+ };
+
+ circle.radius = function(_) {
+ return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), circle) : radius;
+ };
+
+ circle.precision = function(_) {
+ return arguments.length ? (precision = typeof _ === "function" ? _ : constant$7(+_), circle) : precision;
+ };
+
+ return circle;
+};
+
+var clipBuffer = function() {
+ var lines = [],
+ line;
+ return {
+ point: function(x, y) {
+ line.push([x, y]);
+ },
+ lineStart: function() {
+ lines.push(line = []);
+ },
+ lineEnd: noop$1,
+ rejoin: function() {
+ if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
+ },
+ result: function() {
+ var result = lines;
+ lines = [];
+ line = null;
+ return result;
+ }
+ };
+};
+
+var clipLine = function(a, b, x0, y0, x1, y1) {
+ var ax = a[0],
+ ay = a[1],
+ bx = b[0],
+ by = b[1],
+ 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) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
+ if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
+ return true;
+};
+
+var pointEqual = function(a, b) {
+ return abs(a[0] - b[0]) < epsilon$2 && abs(a[1] - b[1]) < epsilon$2;
+};
+
+function Intersection(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
+}
+
+// A generalized polygon clipping algorithm: given a polygon that has been cut
+// into its visible line segments, and rejoins the segments by interpolating
+// along the clip edge.
+var clipPolygon = function(segments, compareIntersection, startInside, interpolate, stream) {
+ var subject = [],
+ clip = [],
+ i,
+ n;
+
+ segments.forEach(function(segment) {
+ if ((n = segment.length - 1) <= 0) return;
+ var n, p0 = segment[0], p1 = segment[n], x;
+
+ // If the first and last points of a segment are coincident, then treat as a
+ // closed ring. TODO if all rings are closed, then the winding order of the
+ // exterior ring should be checked.
+ if (pointEqual(p0, p1)) {
+ stream.lineStart();
+ for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);
+ stream.lineEnd();
+ return;
+ }
+
+ subject.push(x = new Intersection(p0, segment, null, true));
+ clip.push(x.o = new Intersection(p0, null, x, false));
+ subject.push(x = new Intersection(p1, segment, null, false));
+ clip.push(x.o = new Intersection(p1, null, x, true));
+ });
+
+ if (!subject.length) return;
+
+ clip.sort(compareIntersection);
+ link$1(subject);
+ link$1(clip);
+
+ for (i = 0, n = clip.length; i < n; ++i) {
+ clip[i].e = startInside = !startInside;
+ }
+
+ var start = subject[0],
+ points,
+ point;
+
+ while (1) {
+ // Find first unvisited intersection.
+ var current = start,
+ isSubject = true;
+ while (current.v) if ((current = current.n) === start) return;
+ points = current.z;
+ stream.lineStart();
+ do {
+ current.v = current.o.v = true;
+ if (current.e) {
+ if (isSubject) {
+ for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);
+ } else {
+ interpolate(current.x, current.n.x, 1, stream);
+ }
+ current = current.n;
+ } else {
+ if (isSubject) {
+ points = current.p.z;
+ for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);
+ } else {
+ interpolate(current.x, current.p.x, -1, stream);
+ }
+ current = current.p;
+ }
+ current = current.o;
+ points = current.z;
+ isSubject = !isSubject;
+ } while (!current.v);
+ stream.lineEnd();
+ }
+};
+
+function link$1(array) {
+ if (!(n = array.length)) return;
+ var n,
+ i = 0,
+ a = array[0],
+ b;
+ while (++i < n) {
+ a.n = b = array[i];
+ b.p = a;
+ a = b;
+ }
+ a.n = b = array[0];
+ b.p = a;
+}
+
+var clipMax = 1e9;
+var clipMin = -clipMax;
+
+// TODO Use d3-polygon’s polygonContains here for the ring check?
+// TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
+
+function clipExtent(x0, y0, x1, y1) {
+
+ function visible(x, y) {
+ return x0 <= x && x <= x1 && y0 <= y && y <= y1;
+ }
+
+ function interpolate(from, to, direction, stream) {
+ var a = 0, a1 = 0;
+ if (from == null
+ || (a = corner(from, direction)) !== (a1 = corner(to, direction))
+ || comparePoint(from, to) < 0 ^ direction > 0) {
+ do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
+ while ((a = (a + direction + 4) % 4) !== a1);
+ } else {
+ stream.point(to[0], to[1]);
+ }
+ }
+
+ function corner(p, direction) {
+ return abs(p[0] - x0) < epsilon$2 ? direction > 0 ? 0 : 3
+ : abs(p[0] - x1) < epsilon$2 ? direction > 0 ? 2 : 1
+ : abs(p[1] - y0) < epsilon$2 ? direction > 0 ? 1 : 0
+ : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
+ }
+
+ function compareIntersection(a, b) {
+ return comparePoint(a.x, b.x);
+ }
+
+ function comparePoint(a, b) {
+ var ca = corner(a, 1),
+ cb = corner(b, 1);
+ return ca !== cb ? ca - cb
+ : ca === 0 ? b[1] - a[1]
+ : ca === 1 ? a[0] - b[0]
+ : ca === 2 ? a[1] - b[1]
+ : b[0] - a[0];
+ }
+
+ return function(stream) {
+ var activeStream = stream,
+ bufferStream = clipBuffer(),
+ segments,
+ polygon,
+ ring,
+ x__, y__, v__, // first point
+ x_, y_, v_, // previous point
+ first,
+ clean;
+
+ var clipStream = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: polygonStart,
+ polygonEnd: polygonEnd
+ };
+
+ function point(x, y) {
+ if (visible(x, y)) activeStream.point(x, y);
+ }
+
+ function polygonInside() {
+ var winding = 0;
+
+ for (var i = 0, n = polygon.length; i < n; ++i) {
+ for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
+ a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
+ if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }
+ else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }
+ }
+ }
+
+ return winding;
+ }
+
+ // Buffer geometry within a polygon and then clip it en masse.
+ function polygonStart() {
+ activeStream = bufferStream, segments = [], polygon = [], clean = true;
+ }
+
+ function polygonEnd() {
+ var startInside = polygonInside(),
+ cleanInside = clean && startInside,
+ visible = (segments = merge(segments)).length;
+ if (cleanInside || visible) {
+ stream.polygonStart();
+ if (cleanInside) {
+ stream.lineStart();
+ interpolate(null, null, 1, stream);
+ stream.lineEnd();
+ }
+ if (visible) {
+ clipPolygon(segments, compareIntersection, startInside, interpolate, stream);
+ }
+ stream.polygonEnd();
+ }
+ activeStream = stream, segments = polygon = ring = null;
+ }
+
+ function lineStart() {
+ clipStream.point = linePoint;
+ if (polygon) polygon.push(ring = []);
+ first = true;
+ v_ = false;
+ x_ = y_ = NaN;
+ }
+
+ // TODO rather than special-case polygons, simply handle them separately.
+ // Ideally, coincident intersection points should be jittered to avoid
+ // clipping issues.
+ function lineEnd() {
+ if (segments) {
+ linePoint(x__, y__);
+ if (v__ && v_) bufferStream.rejoin();
+ segments.push(bufferStream.result());
+ }
+ clipStream.point = point;
+ if (v_) activeStream.lineEnd();
+ }
+
+ function linePoint(x, y) {
+ var v = visible(x, y);
+ if (polygon) ring.push([x, y]);
+ if (first) {
+ x__ = x, y__ = y, v__ = v;
+ first = false;
+ if (v) {
+ activeStream.lineStart();
+ activeStream.point(x, y);
+ }
+ } else {
+ if (v && v_) activeStream.point(x, y);
+ else {
+ var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
+ b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
+ if (clipLine(a, b, x0, y0, x1, y1)) {
+ if (!v_) {
+ activeStream.lineStart();
+ activeStream.point(a[0], a[1]);
+ }
+ activeStream.point(b[0], b[1]);
+ if (!v) activeStream.lineEnd();
+ clean = false;
+ } else if (v) {
+ activeStream.lineStart();
+ activeStream.point(x, y);
+ clean = false;
+ }
+ }
+ }
+ x_ = x, y_ = y, v_ = v;
+ }
+
+ return clipStream;
+ };
+}
+
+var extent$1 = function() {
+ var x0 = 0,
+ y0 = 0,
+ x1 = 960,
+ y1 = 500,
+ cache,
+ cacheStream,
+ clip;
+
+ return clip = {
+ stream: function(stream) {
+ return cache && cacheStream === stream ? cache : cache = clipExtent(x0, y0, x1, y1)(cacheStream = stream);
+ },
+ extent: function(_) {
+ return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]];
+ }
+ };
+};
+
+var sum$1 = adder();
+
+var polygonContains = function(polygon, point) {
+ var lambda = point[0],
+ phi = point[1],
+ normal = [sin$1(lambda), -cos$1(lambda), 0],
+ angle = 0,
+ winding = 0;
+
+ sum$1.reset();
+
+ for (var i = 0, n = polygon.length; i < n; ++i) {
+ if (!(m = (ring = polygon[i]).length)) continue;
+ var ring,
+ m,
+ point0 = ring[m - 1],
+ lambda0 = point0[0],
+ phi0 = point0[1] / 2 + quarterPi,
+ sinPhi0 = sin$1(phi0),
+ cosPhi0 = cos$1(phi0);
+
+ for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
+ var point1 = ring[j],
+ lambda1 = point1[0],
+ phi1 = point1[1] / 2 + quarterPi,
+ sinPhi1 = sin$1(phi1),
+ cosPhi1 = cos$1(phi1),
+ delta = lambda1 - lambda0,
+ sign$$1 = delta >= 0 ? 1 : -1,
+ absDelta = sign$$1 * delta,
+ antimeridian = absDelta > pi$3,
+ k = sinPhi0 * sinPhi1;
+
+ sum$1.add(atan2(k * sign$$1 * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta)));
+ angle += antimeridian ? delta + sign$$1 * tau$3 : delta;
+
+ // Are the longitudes either side of the point’s meridian (lambda),
+ // and are the latitudes smaller than the parallel (phi)?
+ if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
+ var arc = cartesianCross(cartesian(point0), cartesian(point1));
+ cartesianNormalizeInPlace(arc);
+ var intersection = cartesianCross(normal, arc);
+ cartesianNormalizeInPlace(intersection);
+ var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
+ if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
+ winding += antimeridian ^ delta >= 0 ? 1 : -1;
+ }
+ }
+ }
+ }
+
+ // First, determine whether the South pole is inside or outside:
+ //
+ // It is inside if:
+ // * the polygon winds around it in a clockwise direction.
+ // * the polygon does not (cumulatively) wind around it, but has a negative
+ // (counter-clockwise) area.
+ //
+ // Second, count the (signed) number of times a segment crosses a lambda
+ // from the point to the South pole. If it is zero, then the point is the
+ // same side as the South pole.
+
+ return (angle < -epsilon$2 || angle < epsilon$2 && sum$1 < -epsilon$2) ^ (winding & 1);
+};
+
+var lengthSum = adder();
+var lambda0$2;
+var sinPhi0$1;
+var cosPhi0$1;
+
+var lengthStream = {
+ sphere: noop$1,
+ point: noop$1,
+ lineStart: lengthLineStart,
+ lineEnd: noop$1,
+ polygonStart: noop$1,
+ polygonEnd: noop$1
+};
+
+function lengthLineStart() {
+ lengthStream.point = lengthPointFirst;
+ lengthStream.lineEnd = lengthLineEnd;
+}
+
+function lengthLineEnd() {
+ lengthStream.point = lengthStream.lineEnd = noop$1;
+}
+
+function lengthPointFirst(lambda, phi) {
+ lambda *= radians, phi *= radians;
+ lambda0$2 = lambda, sinPhi0$1 = sin$1(phi), cosPhi0$1 = cos$1(phi);
+ lengthStream.point = lengthPoint;
+}
+
+function lengthPoint(lambda, phi) {
+ lambda *= radians, phi *= radians;
+ var sinPhi = sin$1(phi),
+ cosPhi = cos$1(phi),
+ delta = abs(lambda - lambda0$2),
+ cosDelta = cos$1(delta),
+ sinDelta = sin$1(delta),
+ x = cosPhi * sinDelta,
+ y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
+ z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
+ lengthSum.add(atan2(sqrt(x * x + y * y), z));
+ lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
+}
+
+var d3GeoLength = function(object) {
+ lengthSum.reset();
+ geoStream(object, lengthStream);
+ return +lengthSum;
+};
+
+var coordinates = [null, null];
+var object$1 = {type: "LineString", coordinates: coordinates};
+
+var distance = function(a, b) {
+ coordinates[0] = a;
+ coordinates[1] = b;
+ return d3GeoLength(object$1);
+};
+
+var containsObjectType = {
+ Feature: function(object, point) {
+ return containsGeometry(object.geometry, point);
+ },
+ FeatureCollection: function(object, point) {
+ var features = object.features, i = -1, n = features.length;
+ while (++i < n) if (containsGeometry(features[i].geometry, point)) return true;
+ return false;
+ }
+};
+
+var containsGeometryType = {
+ Sphere: function() {
+ return true;
+ },
+ Point: function(object, point) {
+ return containsPoint(object.coordinates, point);
+ },
+ MultiPoint: function(object, point) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) if (containsPoint(coordinates[i], point)) return true;
+ return false;
+ },
+ LineString: function(object, point) {
+ return containsLine(object.coordinates, point);
+ },
+ MultiLineString: function(object, point) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) if (containsLine(coordinates[i], point)) return true;
+ return false;
+ },
+ Polygon: function(object, point) {
+ return containsPolygon(object.coordinates, point);
+ },
+ MultiPolygon: function(object, point) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) if (containsPolygon(coordinates[i], point)) return true;
+ return false;
+ },
+ GeometryCollection: function(object, point) {
+ var geometries = object.geometries, i = -1, n = geometries.length;
+ while (++i < n) if (containsGeometry(geometries[i], point)) return true;
+ return false;
+ }
+};
+
+function containsGeometry(geometry, point) {
+ return geometry && containsGeometryType.hasOwnProperty(geometry.type)
+ ? containsGeometryType[geometry.type](geometry, point)
+ : false;
+}
+
+function containsPoint(coordinates, point) {
+ return distance(coordinates, point) === 0;
+}
+
+function containsLine(coordinates, point) {
+ var ab = distance(coordinates[0], coordinates[1]),
+ ao = distance(coordinates[0], point),
+ ob = distance(point, coordinates[1]);
+ return ao + ob <= ab + epsilon$2;
+}
+
+function containsPolygon(coordinates, point) {
+ return !!polygonContains(coordinates.map(ringRadians), pointRadians(point));
+}
+
+function ringRadians(ring) {
+ return ring = ring.map(pointRadians), ring.pop(), ring;
+}
+
+function pointRadians(point) {
+ return [point[0] * radians, point[1] * radians];
+}
+
+var contains = function(object, point) {
+ return (object && containsObjectType.hasOwnProperty(object.type)
+ ? containsObjectType[object.type]
+ : containsGeometry)(object, point);
+};
+
+function graticuleX(y0, y1, dy) {
+ var y = sequence(y0, y1 - epsilon$2, dy).concat(y1);
+ return function(x) { return y.map(function(y) { return [x, y]; }); };
+}
+
+function graticuleY(x0, x1, dx) {
+ var x = sequence(x0, x1 - epsilon$2, dx).concat(x1);
+ return function(y) { return x.map(function(x) { return [x, y]; }); };
+}
+
+function graticule() {
+ var x1, x0, X1, X0,
+ y1, y0, Y1, Y0,
+ dx = 10, dy = dx, DX = 90, DY = 360,
+ x, y, X, Y,
+ precision = 2.5;
+
+ function graticule() {
+ return {type: "MultiLineString", coordinates: lines()};
+ }
+
+ function lines() {
+ return sequence(ceil(X0 / DX) * DX, X1, DX).map(X)
+ .concat(sequence(ceil(Y0 / DY) * DY, Y1, DY).map(Y))
+ .concat(sequence(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon$2; }).map(x))
+ .concat(sequence(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon$2; }).map(y));
+ }
+
+ graticule.lines = function() {
+ return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; });
+ };
+
+ graticule.outline = function() {
+ return {
+ type: "Polygon",
+ coordinates: [
+ X(X0).concat(
+ Y(Y1).slice(1),
+ X(X1).reverse().slice(1),
+ Y(Y0).reverse().slice(1))
+ ]
+ };
+ };
+
+ graticule.extent = function(_) {
+ if (!arguments.length) return graticule.extentMinor();
+ return graticule.extentMajor(_).extentMinor(_);
+ };
+
+ graticule.extentMajor = function(_) {
+ if (!arguments.length) return [[X0, Y0], [X1, Y1]];
+ X0 = +_[0][0], X1 = +_[1][0];
+ Y0 = +_[0][1], Y1 = +_[1][1];
+ if (X0 > X1) _ = X0, X0 = X1, X1 = _;
+ if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
+ return graticule.precision(precision);
+ };
+
+ graticule.extentMinor = function(_) {
+ if (!arguments.length) return [[x0, y0], [x1, y1]];
+ x0 = +_[0][0], x1 = +_[1][0];
+ y0 = +_[0][1], y1 = +_[1][1];
+ if (x0 > x1) _ = x0, x0 = x1, x1 = _;
+ if (y0 > y1) _ = y0, y0 = y1, y1 = _;
+ return graticule.precision(precision);
+ };
+
+ graticule.step = function(_) {
+ if (!arguments.length) return graticule.stepMinor();
+ return graticule.stepMajor(_).stepMinor(_);
+ };
+
+ graticule.stepMajor = function(_) {
+ if (!arguments.length) return [DX, DY];
+ DX = +_[0], DY = +_[1];
+ return graticule;
+ };
+
+ graticule.stepMinor = function(_) {
+ if (!arguments.length) return [dx, dy];
+ dx = +_[0], dy = +_[1];
+ return graticule;
+ };
+
+ graticule.precision = function(_) {
+ if (!arguments.length) return precision;
+ precision = +_;
+ x = graticuleX(y0, y1, 90);
+ y = graticuleY(x0, x1, precision);
+ X = graticuleX(Y0, Y1, 90);
+ Y = graticuleY(X0, X1, precision);
+ return graticule;
+ };
+
+ return graticule
+ .extentMajor([[-180, -90 + epsilon$2], [180, 90 - epsilon$2]])
+ .extentMinor([[-180, -80 - epsilon$2], [180, 80 + epsilon$2]]);
+}
+
+function graticule10() {
+ return graticule()();
+}
+
+var interpolate$1 = function(a, b) {
+ var x0 = a[0] * radians,
+ y0 = a[1] * radians,
+ x1 = b[0] * radians,
+ y1 = b[1] * radians,
+ cy0 = cos$1(y0),
+ sy0 = sin$1(y0),
+ cy1 = cos$1(y1),
+ sy1 = sin$1(y1),
+ kx0 = cy0 * cos$1(x0),
+ ky0 = cy0 * sin$1(x0),
+ kx1 = cy1 * cos$1(x1),
+ ky1 = cy1 * sin$1(x1),
+ d = 2 * asin(sqrt(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))),
+ k = sin$1(d);
+
+ var interpolate = d ? function(t) {
+ var B = sin$1(t *= d) / k,
+ A = sin$1(d - t) / k,
+ x = A * kx0 + B * kx1,
+ y = A * ky0 + B * ky1,
+ z = A * sy0 + B * sy1;
+ return [
+ atan2(y, x) * degrees$1,
+ atan2(z, sqrt(x * x + y * y)) * degrees$1
+ ];
+ } : function() {
+ return [x0 * degrees$1, y0 * degrees$1];
+ };
+
+ interpolate.distance = d;
+
+ return interpolate;
+};
+
+var identity$4 = function(x) {
+ return x;
+};
+
+var areaSum$1 = adder();
+var areaRingSum$1 = adder();
+var x00;
+var y00;
+var x0$1;
+var y0$1;
+
+var areaStream$1 = {
+ point: noop$1,
+ lineStart: noop$1,
+ lineEnd: noop$1,
+ polygonStart: function() {
+ areaStream$1.lineStart = areaRingStart$1;
+ areaStream$1.lineEnd = areaRingEnd$1;
+ },
+ polygonEnd: function() {
+ areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$1;
+ areaSum$1.add(abs(areaRingSum$1));
+ areaRingSum$1.reset();
+ },
+ result: function() {
+ var area = areaSum$1 / 2;
+ areaSum$1.reset();
+ return area;
+ }
+};
+
+function areaRingStart$1() {
+ areaStream$1.point = areaPointFirst$1;
+}
+
+function areaPointFirst$1(x, y) {
+ areaStream$1.point = areaPoint$1;
+ x00 = x0$1 = x, y00 = y0$1 = y;
+}
+
+function areaPoint$1(x, y) {
+ areaRingSum$1.add(y0$1 * x - x0$1 * y);
+ x0$1 = x, y0$1 = y;
+}
+
+function areaRingEnd$1() {
+ areaPoint$1(x00, y00);
+}
+
+var x0$2 = Infinity;
+var y0$2 = x0$2;
+var x1 = -x0$2;
+var y1 = x1;
+
+var boundsStream$1 = {
+ point: boundsPoint$1,
+ lineStart: noop$1,
+ lineEnd: noop$1,
+ polygonStart: noop$1,
+ polygonEnd: noop$1,
+ result: function() {
+ var bounds = [[x0$2, y0$2], [x1, y1]];
+ x1 = y1 = -(y0$2 = x0$2 = Infinity);
+ return bounds;
+ }
+};
+
+function boundsPoint$1(x, y) {
+ if (x < x0$2) x0$2 = x;
+ if (x > x1) x1 = x;
+ if (y < y0$2) y0$2 = y;
+ if (y > y1) y1 = y;
+}
+
+// TODO Enforce positive area for exterior, negative area for interior?
+
+var X0$1 = 0;
+var Y0$1 = 0;
+var Z0$1 = 0;
+var X1$1 = 0;
+var Y1$1 = 0;
+var Z1$1 = 0;
+var X2$1 = 0;
+var Y2$1 = 0;
+var Z2$1 = 0;
+var x00$1;
+var y00$1;
+var x0$3;
+var y0$3;
+
+var centroidStream$1 = {
+ point: centroidPoint$1,
+ lineStart: centroidLineStart$1,
+ lineEnd: centroidLineEnd$1,
+ polygonStart: function() {
+ centroidStream$1.lineStart = centroidRingStart$1;
+ centroidStream$1.lineEnd = centroidRingEnd$1;
+ },
+ polygonEnd: function() {
+ centroidStream$1.point = centroidPoint$1;
+ centroidStream$1.lineStart = centroidLineStart$1;
+ centroidStream$1.lineEnd = centroidLineEnd$1;
+ },
+ result: function() {
+ var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1]
+ : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1]
+ : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1]
+ : [NaN, NaN];
+ X0$1 = Y0$1 = Z0$1 =
+ X1$1 = Y1$1 = Z1$1 =
+ X2$1 = Y2$1 = Z2$1 = 0;
+ return centroid;
+ }
+};
+
+function centroidPoint$1(x, y) {
+ X0$1 += x;
+ Y0$1 += y;
+ ++Z0$1;
+}
+
+function centroidLineStart$1() {
+ centroidStream$1.point = centroidPointFirstLine;
+}
+
+function centroidPointFirstLine(x, y) {
+ centroidStream$1.point = centroidPointLine;
+ centroidPoint$1(x0$3 = x, y0$3 = y);
+}
+
+function centroidPointLine(x, y) {
+ var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy);
+ X1$1 += z * (x0$3 + x) / 2;
+ Y1$1 += z * (y0$3 + y) / 2;
+ Z1$1 += z;
+ centroidPoint$1(x0$3 = x, y0$3 = y);
+}
+
+function centroidLineEnd$1() {
+ centroidStream$1.point = centroidPoint$1;
+}
+
+function centroidRingStart$1() {
+ centroidStream$1.point = centroidPointFirstRing;
+}
+
+function centroidRingEnd$1() {
+ centroidPointRing(x00$1, y00$1);
+}
+
+function centroidPointFirstRing(x, y) {
+ centroidStream$1.point = centroidPointRing;
+ centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
+}
+
+function centroidPointRing(x, y) {
+ var dx = x - x0$3,
+ dy = y - y0$3,
+ z = sqrt(dx * dx + dy * dy);
+
+ X1$1 += z * (x0$3 + x) / 2;
+ Y1$1 += z * (y0$3 + y) / 2;
+ Z1$1 += z;
+
+ z = y0$3 * x - x0$3 * y;
+ X2$1 += z * (x0$3 + x);
+ Y2$1 += z * (y0$3 + y);
+ Z2$1 += z * 3;
+ centroidPoint$1(x0$3 = x, y0$3 = y);
+}
+
+function PathContext(context) {
+ this._context = context;
+}
+
+PathContext.prototype = {
+ _radius: 4.5,
+ pointRadius: function(_) {
+ return this._radius = _, this;
+ },
+ polygonStart: function() {
+ this._line = 0;
+ },
+ polygonEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (this._line === 0) this._context.closePath();
+ this._point = NaN;
+ },
+ point: function(x, y) {
+ switch (this._point) {
+ case 0: {
+ this._context.moveTo(x, y);
+ this._point = 1;
+ break;
+ }
+ case 1: {
+ this._context.lineTo(x, y);
+ break;
+ }
+ default: {
+ this._context.moveTo(x + this._radius, y);
+ this._context.arc(x, y, this._radius, 0, tau$3);
+ break;
+ }
+ }
+ },
+ result: noop$1
+};
+
+var lengthSum$1 = adder();
+var lengthRing;
+var x00$2;
+var y00$2;
+var x0$4;
+var y0$4;
+
+var lengthStream$1 = {
+ point: noop$1,
+ lineStart: function() {
+ lengthStream$1.point = lengthPointFirst$1;
+ },
+ lineEnd: function() {
+ if (lengthRing) lengthPoint$1(x00$2, y00$2);
+ lengthStream$1.point = noop$1;
+ },
+ polygonStart: function() {
+ lengthRing = true;
+ },
+ polygonEnd: function() {
+ lengthRing = null;
+ },
+ result: function() {
+ var length = +lengthSum$1;
+ lengthSum$1.reset();
+ return length;
+ }
+};
+
+function lengthPointFirst$1(x, y) {
+ lengthStream$1.point = lengthPoint$1;
+ x00$2 = x0$4 = x, y00$2 = y0$4 = y;
+}
+
+function lengthPoint$1(x, y) {
+ x0$4 -= x, y0$4 -= y;
+ lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4));
+ x0$4 = x, y0$4 = y;
+}
+
+function PathString() {
+ this._string = [];
+}
+
+PathString.prototype = {
+ _radius: 4.5,
+ _circle: circle$1(4.5),
+ pointRadius: function(_) {
+ if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
+ return this;
+ },
+ polygonStart: function() {
+ this._line = 0;
+ },
+ polygonEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (this._line === 0) this._string.push("Z");
+ this._point = NaN;
+ },
+ point: function(x, y) {
+ switch (this._point) {
+ case 0: {
+ this._string.push("M", x, ",", y);
+ this._point = 1;
+ break;
+ }
+ case 1: {
+ this._string.push("L", x, ",", y);
+ break;
+ }
+ default: {
+ if (this._circle == null) this._circle = circle$1(this._radius);
+ this._string.push("M", x, ",", y, this._circle);
+ break;
+ }
+ }
+ },
+ result: function() {
+ if (this._string.length) {
+ var result = this._string.join("");
+ this._string = [];
+ return result;
+ } else {
+ return null;
+ }
+ }
+};
+
+function circle$1(radius) {
+ return "m0," + radius
+ + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
+ + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
+ + "z";
+}
+
+var index$4 = function(projection, context) {
+ var pointRadius = 4.5,
+ projectionStream,
+ contextStream;
+
+ function path(object) {
+ if (object) {
+ if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
+ geoStream(object, projectionStream(contextStream));
+ }
+ return contextStream.result();
+ }
+
+ path.area = function(object) {
+ geoStream(object, projectionStream(areaStream$1));
+ return areaStream$1.result();
+ };
+
+ path.measure = function(object) {
+ geoStream(object, projectionStream(lengthStream$1));
+ return lengthStream$1.result();
+ };
+
+ path.bounds = function(object) {
+ geoStream(object, projectionStream(boundsStream$1));
+ return boundsStream$1.result();
+ };
+
+ path.centroid = function(object) {
+ geoStream(object, projectionStream(centroidStream$1));
+ return centroidStream$1.result();
+ };
+
+ path.projection = function(_) {
+ return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection;
+ };
+
+ path.context = function(_) {
+ if (!arguments.length) return context;
+ contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _);
+ if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
+ return path;
+ };
+
+ path.pointRadius = function(_) {
+ if (!arguments.length) return pointRadius;
+ pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
+ return path;
+ };
+
+ return path.projection(projection).context(context);
+};
+
+var clip = function(pointVisible, clipLine, interpolate, start) {
+ return function(rotate, sink) {
+ var line = clipLine(sink),
+ rotatedStart = rotate.invert(start[0], start[1]),
+ ringBuffer = clipBuffer(),
+ ringSink = clipLine(ringBuffer),
+ polygonStarted = false,
+ polygon,
+ segments,
+ ring;
+
+ var clip = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ clip.point = pointRing;
+ clip.lineStart = ringStart;
+ clip.lineEnd = ringEnd;
+ segments = [];
+ polygon = [];
+ },
+ polygonEnd: function() {
+ clip.point = point;
+ clip.lineStart = lineStart;
+ clip.lineEnd = lineEnd;
+ segments = merge(segments);
+ var startInside = polygonContains(polygon, rotatedStart);
+ if (segments.length) {
+ if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
+ clipPolygon(segments, compareIntersection, startInside, interpolate, sink);
+ } else if (startInside) {
+ if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
+ sink.lineStart();
+ interpolate(null, null, 1, sink);
+ sink.lineEnd();
+ }
+ if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
+ segments = polygon = null;
+ },
+ sphere: function() {
+ sink.polygonStart();
+ sink.lineStart();
+ interpolate(null, null, 1, sink);
+ sink.lineEnd();
+ sink.polygonEnd();
+ }
+ };
+
+ function point(lambda, phi) {
+ var point = rotate(lambda, phi);
+ if (pointVisible(lambda = point[0], phi = point[1])) sink.point(lambda, phi);
+ }
+
+ function pointLine(lambda, phi) {
+ var point = rotate(lambda, phi);
+ line.point(point[0], point[1]);
+ }
+
+ function lineStart() {
+ clip.point = pointLine;
+ line.lineStart();
+ }
+
+ function lineEnd() {
+ clip.point = point;
+ line.lineEnd();
+ }
+
+ function pointRing(lambda, phi) {
+ ring.push([lambda, phi]);
+ var point = rotate(lambda, phi);
+ ringSink.point(point[0], point[1]);
+ }
+
+ function ringStart() {
+ ringSink.lineStart();
+ ring = [];
+ }
+
+ function ringEnd() {
+ pointRing(ring[0][0], ring[0][1]);
+ ringSink.lineEnd();
+
+ var clean = ringSink.clean(),
+ ringSegments = ringBuffer.result(),
+ i, n = ringSegments.length, m,
+ segment,
+ point;
+
+ ring.pop();
+ polygon.push(ring);
+ ring = null;
+
+ if (!n) return;
+
+ // No intersections.
+ if (clean & 1) {
+ segment = ringSegments[0];
+ if ((m = segment.length - 1) > 0) {
+ if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
+ sink.lineStart();
+ for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
+ sink.lineEnd();
+ }
+ return;
+ }
+
+ // Rejoin connected segments.
+ // TODO reuse ringBuffer.rejoin()?
+ if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
+
+ segments.push(ringSegments.filter(validSegment));
+ }
+
+ return clip;
+ };
+};
+
+function validSegment(segment) {
+ return segment.length > 1;
+}
+
+// Intersections are sorted along the clip edge. For both antimeridian cutting
+// and circle clipping, the same comparison is used.
+function compareIntersection(a, b) {
+ return ((a = a.x)[0] < 0 ? a[1] - halfPi$2 - epsilon$2 : halfPi$2 - a[1])
+ - ((b = b.x)[0] < 0 ? b[1] - halfPi$2 - epsilon$2 : halfPi$2 - b[1]);
+}
+
+var clipAntimeridian = clip(
+ function() { return true; },
+ clipAntimeridianLine,
+ clipAntimeridianInterpolate,
+ [-pi$3, -halfPi$2]
+);
+
+// Takes a line and cuts into visible segments. Return values: 0 - there were
+// intersections or the line was empty; 1 - no intersections; 2 - there were
+// intersections, and the first and last segments should be rejoined.
+function clipAntimeridianLine(stream) {
+ var lambda0 = NaN,
+ phi0 = NaN,
+ sign0 = NaN,
+ clean; // no intersections
+
+ return {
+ lineStart: function() {
+ stream.lineStart();
+ clean = 1;
+ },
+ point: function(lambda1, phi1) {
+ var sign1 = lambda1 > 0 ? pi$3 : -pi$3,
+ delta = abs(lambda1 - lambda0);
+ if (abs(delta - pi$3) < epsilon$2) { // line crosses a pole
+ stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$2 : -halfPi$2);
+ stream.point(sign0, phi0);
+ stream.lineEnd();
+ stream.lineStart();
+ stream.point(sign1, phi0);
+ stream.point(lambda1, phi0);
+ clean = 0;
+ } else if (sign0 !== sign1 && delta >= pi$3) { // line crosses antimeridian
+ if (abs(lambda0 - sign0) < epsilon$2) lambda0 -= sign0 * epsilon$2; // handle degeneracies
+ if (abs(lambda1 - sign1) < epsilon$2) lambda1 -= sign1 * epsilon$2;
+ phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
+ stream.point(sign0, phi0);
+ stream.lineEnd();
+ stream.lineStart();
+ stream.point(sign1, phi0);
+ clean = 0;
+ }
+ stream.point(lambda0 = lambda1, phi0 = phi1);
+ sign0 = sign1;
+ },
+ lineEnd: function() {
+ stream.lineEnd();
+ lambda0 = phi0 = NaN;
+ },
+ clean: function() {
+ return 2 - clean; // if intersections, rejoin first and last segments
+ }
+ };
+}
+
+function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
+ var cosPhi0,
+ cosPhi1,
+ sinLambda0Lambda1 = sin$1(lambda0 - lambda1);
+ return abs(sinLambda0Lambda1) > epsilon$2
+ ? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1)
+ - sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0))
+ / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))
+ : (phi0 + phi1) / 2;
+}
+
+function clipAntimeridianInterpolate(from, to, direction, stream) {
+ var phi;
+ if (from == null) {
+ phi = direction * halfPi$2;
+ stream.point(-pi$3, phi);
+ stream.point(0, phi);
+ stream.point(pi$3, phi);
+ stream.point(pi$3, 0);
+ stream.point(pi$3, -phi);
+ stream.point(0, -phi);
+ stream.point(-pi$3, -phi);
+ stream.point(-pi$3, 0);
+ stream.point(-pi$3, phi);
+ } else if (abs(from[0] - to[0]) > epsilon$2) {
+ var lambda = from[0] < to[0] ? pi$3 : -pi$3;
+ phi = direction * lambda / 2;
+ stream.point(-lambda, phi);
+ stream.point(0, phi);
+ stream.point(lambda, phi);
+ } else {
+ stream.point(to[0], to[1]);
+ }
+}
+
+var clipCircle = function(radius, delta) {
+ var cr = cos$1(radius),
+ smallRadius = cr > 0,
+ notHemisphere = abs(cr) > epsilon$2; // TODO optimise for this common case
+
+ function interpolate(from, to, direction, stream) {
+ circleStream(stream, radius, delta, direction, from, to);
+ }
+
+ function visible(lambda, phi) {
+ return cos$1(lambda) * cos$1(phi) > cr;
+ }
+
+ // Takes a line and cuts into visible segments. Return values used for polygon
+ // clipping: 0 - there were intersections or the line was empty; 1 - no
+ // intersections 2 - there were intersections, and the first and last segments
+ // should be rejoined.
+ function clipLine(stream) {
+ var point0, // previous point
+ c0, // code for previous point
+ v0, // visibility of previous point
+ v00, // visibility of first point
+ clean; // no intersections
+ return {
+ lineStart: function() {
+ v00 = v0 = false;
+ clean = 1;
+ },
+ point: function(lambda, phi) {
+ var point1 = [lambda, phi],
+ point2,
+ v = visible(lambda, phi),
+ c = smallRadius
+ ? v ? 0 : code(lambda, phi)
+ : v ? code(lambda + (lambda < 0 ? pi$3 : -pi$3), phi) : 0;
+ if (!point0 && (v00 = v0 = v)) stream.lineStart();
+ // Handle degeneracies.
+ // TODO ignore if not clipping polygons.
+ if (v !== v0) {
+ point2 = intersect(point0, point1);
+ if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) {
+ point1[0] += epsilon$2;
+ point1[1] += epsilon$2;
+ v = visible(point1[0], point1[1]);
+ }
+ }
+ if (v !== v0) {
+ clean = 0;
+ if (v) {
+ // outside going in
+ stream.lineStart();
+ point2 = intersect(point1, point0);
+ stream.point(point2[0], point2[1]);
+ } else {
+ // inside going out
+ point2 = intersect(point0, point1);
+ stream.point(point2[0], point2[1]);
+ stream.lineEnd();
+ }
+ point0 = point2;
+ } else if (notHemisphere && point0 && smallRadius ^ v) {
+ var t;
+ // If the codes for two points are different, or are both zero,
+ // and there this segment intersects with the small circle.
+ if (!(c & c0) && (t = intersect(point1, point0, true))) {
+ clean = 0;
+ if (smallRadius) {
+ stream.lineStart();
+ stream.point(t[0][0], t[0][1]);
+ stream.point(t[1][0], t[1][1]);
+ stream.lineEnd();
+ } else {
+ stream.point(t[1][0], t[1][1]);
+ stream.lineEnd();
+ stream.lineStart();
+ stream.point(t[0][0], t[0][1]);
+ }
+ }
+ }
+ if (v && (!point0 || !pointEqual(point0, point1))) {
+ stream.point(point1[0], point1[1]);
+ }
+ point0 = point1, v0 = v, c0 = c;
+ },
+ lineEnd: function() {
+ if (v0) stream.lineEnd();
+ point0 = null;
+ },
+ // Rejoin first and last segments if there were intersections and the first
+ // and last points were visible.
+ clean: function() {
+ return clean | ((v00 && v0) << 1);
+ }
+ };
+ }
+
+ // Intersects the great circle between a and b with the clip circle.
+ function intersect(a, b, two) {
+ var pa = cartesian(a),
+ pb = cartesian(b);
+
+ // We have two planes, n1.p = d1 and n2.p = d2.
+ // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
+ var n1 = [1, 0, 0], // normal
+ n2 = cartesianCross(pa, pb),
+ n2n2 = cartesianDot(n2, n2),
+ n1n2 = n2[0], // cartesianDot(n1, n2),
+ determinant = n2n2 - n1n2 * n1n2;
+
+ // Two polar points.
+ if (!determinant) return !two && a;
+
+ var c1 = cr * n2n2 / determinant,
+ c2 = -cr * n1n2 / determinant,
+ n1xn2 = cartesianCross(n1, n2),
+ A = cartesianScale(n1, c1),
+ B = cartesianScale(n2, c2);
+ cartesianAddInPlace(A, B);
+
+ // Solve |p(t)|^2 = 1.
+ var u = n1xn2,
+ w = cartesianDot(A, u),
+ uu = cartesianDot(u, u),
+ t2 = w * w - uu * (cartesianDot(A, A) - 1);
+
+ if (t2 < 0) return;
+
+ var t = sqrt(t2),
+ q = cartesianScale(u, (-w - t) / uu);
+ cartesianAddInPlace(q, A);
+ q = spherical(q);
+
+ if (!two) return q;
+
+ // Two intersection points.
+ var lambda0 = a[0],
+ lambda1 = b[0],
+ phi0 = a[1],
+ phi1 = b[1],
+ z;
+
+ if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
+
+ var delta = lambda1 - lambda0,
+ polar = abs(delta - pi$3) < epsilon$2,
+ meridian = polar || delta < epsilon$2;
+
+ if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;
+
+ // Check that the first point is between a and b.
+ if (meridian
+ ? polar
+ ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon$2 ? phi0 : phi1)
+ : phi0 <= q[1] && q[1] <= phi1
+ : delta > pi$3 ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
+ var q1 = cartesianScale(u, (-w + t) / uu);
+ cartesianAddInPlace(q1, A);
+ return [q, spherical(q1)];
+ }
+ }
+
+ // Generates a 4-bit vector representing the location of a point relative to
+ // the small circle's bounding box.
+ function code(lambda, phi) {
+ var r = smallRadius ? radius : pi$3 - radius,
+ code = 0;
+ if (lambda < -r) code |= 1; // left
+ else if (lambda > r) code |= 2; // right
+ if (phi < -r) code |= 4; // below
+ else if (phi > r) code |= 8; // above
+ return code;
+ }
+
+ return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$3, radius - pi$3]);
+};
+
+var transform = function(methods) {
+ return {
+ stream: transformer(methods)
+ };
+};
+
+function transformer(methods) {
+ return function(stream) {
+ var s = new TransformStream;
+ for (var key in methods) s[key] = methods[key];
+ s.stream = stream;
+ return s;
+ };
+}
+
+function TransformStream() {}
+
+TransformStream.prototype = {
+ constructor: TransformStream,
+ 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 fitExtent(projection, extent, object) {
+ var w = extent[1][0] - extent[0][0],
+ h = extent[1][1] - extent[0][1],
+ clip = projection.clipExtent && projection.clipExtent();
+
+ projection
+ .scale(150)
+ .translate([0, 0]);
+
+ if (clip != null) projection.clipExtent(null);
+
+ geoStream(object, projection.stream(boundsStream$1));
+
+ var b = boundsStream$1.result(),
+ k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
+ x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
+ y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
+
+ if (clip != null) projection.clipExtent(clip);
+
+ return projection
+ .scale(k * 150)
+ .translate([x, y]);
+}
+
+function fitSize(projection, size, object) {
+ return fitExtent(projection, [[0, 0], size], object);
+}
+
+var maxDepth = 16;
+var cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance)
+
+var resample = function(project, delta2) {
+ return +delta2 ? resample$1(project, delta2) : resampleNone(project);
+};
+
+function resampleNone(project) {
+ return transformer({
+ point: function(x, y) {
+ x = project(x, y);
+ this.stream.point(x[0], x[1]);
+ }
+ });
+}
+
+function resample$1(project, delta2) {
+
+ function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
+ var dx = x1 - x0,
+ dy = y1 - y0,
+ d2 = dx * dx + dy * dy;
+ if (d2 > 4 * delta2 && depth--) {
+ var a = a0 + a1,
+ b = b0 + b1,
+ c = c0 + c1,
+ m = sqrt(a * a + b * b + c * c),
+ phi2 = asin(c /= m),
+ lambda2 = abs(abs(c) - 1) < epsilon$2 || abs(lambda0 - lambda1) < epsilon$2 ? (lambda0 + lambda1) / 2 : atan2(b, a),
+ p = project(lambda2, phi2),
+ x2 = p[0],
+ y2 = p[1],
+ dx2 = x2 - x0,
+ dy2 = y2 - y0,
+ dz = dy * dx2 - dx * dy2;
+ if (dz * dz / d2 > delta2 // perpendicular projected distance
+ || abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
+ || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
+ resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
+ stream.point(x2, y2);
+ resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
+ }
+ }
+ }
+ return function(stream) {
+ var lambda00, x00, y00, a00, b00, c00, // first point
+ lambda0, x0, y0, a0, b0, c0; // previous point
+
+ var resampleStream = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
+ polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
+ };
+
+ function point(x, y) {
+ x = project(x, y);
+ stream.point(x[0], x[1]);
+ }
+
+ function lineStart() {
+ x0 = NaN;
+ resampleStream.point = linePoint;
+ stream.lineStart();
+ }
+
+ function linePoint(lambda, phi) {
+ var c = cartesian([lambda, phi]), p = project(lambda, phi);
+ resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
+ stream.point(x0, y0);
+ }
+
+ function lineEnd() {
+ resampleStream.point = point;
+ stream.lineEnd();
+ }
+
+ function ringStart() {
+ lineStart();
+ resampleStream.point = ringPoint;
+ resampleStream.lineEnd = ringEnd;
+ }
+
+ function ringPoint(lambda, phi) {
+ linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
+ resampleStream.point = linePoint;
+ }
+
+ function ringEnd() {
+ resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
+ resampleStream.lineEnd = lineEnd;
+ lineEnd();
+ }
+
+ return resampleStream;
+ };
+}
+
+var transformRadians = transformer({
+ point: function(x, y) {
+ this.stream.point(x * radians, y * radians);
+ }
+});
+
+function projection(project) {
+ return projectionMutator(function() { return project; })();
+}
+
+function projectionMutator(projectAt) {
+ var project,
+ k = 150, // scale
+ x = 480, y = 250, // translate
+ dx, dy, lambda = 0, phi = 0, // center
+ deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, projectRotate, // rotate
+ theta = null, preclip = clipAntimeridian, // clip angle
+ x0 = null, y0, x1, y1, postclip = identity$4, // clip extent
+ delta2 = 0.5, projectResample = resample(projectTransform, delta2), // precision
+ cache,
+ cacheStream;
+
+ function projection(point) {
+ point = projectRotate(point[0] * radians, point[1] * radians);
+ return [point[0] * k + dx, dy - point[1] * k];
+ }
+
+ function invert(point) {
+ point = projectRotate.invert((point[0] - dx) / k, (dy - point[1]) / k);
+ return point && [point[0] * degrees$1, point[1] * degrees$1];
+ }
+
+ function projectTransform(x, y) {
+ return x = project(x, y), [x[0] * k + dx, dy - x[1] * k];
+ }
+
+ projection.stream = function(stream) {
+ return cache && cacheStream === stream ? cache : cache = transformRadians(preclip(rotate, projectResample(postclip(cacheStream = stream))));
+ };
+
+ projection.clipAngle = function(_) {
+ return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians, 6 * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1;
+ };
+
+ projection.clipExtent = function(_) {
+ return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
+ };
+
+ projection.scale = function(_) {
+ return arguments.length ? (k = +_, recenter()) : k;
+ };
+
+ projection.translate = function(_) {
+ return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
+ };
+
+ projection.center = function(_) {
+ return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1];
+ };
+
+ projection.rotate = function(_) {
+ return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1];
+ };
+
+ projection.precision = function(_) {
+ return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
+ };
+
+ projection.fitExtent = function(extent, object) {
+ return fitExtent(projection, extent, object);
+ };
+
+ projection.fitSize = function(size, object) {
+ return fitSize(projection, size, object);
+ };
+
+ function recenter() {
+ projectRotate = compose(rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma), project);
+ var center = project(lambda, phi);
+ dx = x - center[0] * k;
+ dy = y + center[1] * k;
+ return reset();
+ }
+
+ function reset() {
+ cache = cacheStream = null;
+ return projection;
+ }
+
+ return function() {
+ project = projectAt.apply(this, arguments);
+ projection.invert = project.invert && invert;
+ return recenter();
+ };
+}
+
+function conicProjection(projectAt) {
+ var phi0 = 0,
+ phi1 = pi$3 / 3,
+ m = projectionMutator(projectAt),
+ p = m(phi0, phi1);
+
+ p.parallels = function(_) {
+ return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees$1, phi1 * degrees$1];
+ };
+
+ return p;
+}
+
+function cylindricalEqualAreaRaw(phi0) {
+ var cosPhi0 = cos$1(phi0);
+
+ function forward(lambda, phi) {
+ return [lambda * cosPhi0, sin$1(phi) / cosPhi0];
+ }
+
+ forward.invert = function(x, y) {
+ return [x / cosPhi0, asin(y * cosPhi0)];
+ };
+
+ return forward;
+}
+
+function conicEqualAreaRaw(y0, y1) {
+ var sy0 = sin$1(y0), n = (sy0 + sin$1(y1)) / 2;
+
+ // Are the parallels symmetrical around the Equator?
+ if (abs(n) < epsilon$2) return cylindricalEqualAreaRaw(y0);
+
+ var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt(c) / n;
+
+ function project(x, y) {
+ var r = sqrt(c - 2 * n * sin$1(y)) / n;
+ return [r * sin$1(x *= n), r0 - r * cos$1(x)];
+ }
+
+ project.invert = function(x, y) {
+ var r0y = r0 - y;
+ return [atan2(x, abs(r0y)) / n * sign(r0y), asin((c - (x * x + r0y * r0y) * n * n) / (2 * n))];
+ };
+
+ return project;
+}
+
+var conicEqualArea = function() {
+ return conicProjection(conicEqualAreaRaw)
+ .scale(155.424)
+ .center([0, 33.6442]);
+};
+
+var albers = function() {
+ return conicEqualArea()
+ .parallels([29.5, 45.5])
+ .scale(1070)
+ .translate([480, 250])
+ .rotate([96, 0])
+ .center([-0.6, 38.7]);
+};
+
+// The projections must have mutually exclusive clip regions on the sphere,
+// as this will avoid emitting interleaving lines and polygons.
+function multiplex(streams) {
+ var n = streams.length;
+ return {
+ point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); },
+ sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); },
+ lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); },
+ lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); },
+ polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); },
+ polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); }
+ };
+}
+
+// A composite projection for the United States, configured by default for
+// 960×500. The projection also works quite well at 960×600 if you change the
+// scale to 1285 and adjust the translate accordingly. 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
+var albersUsa = function() {
+ var cache,
+ cacheStream,
+ lower48 = albers(), lower48Point,
+ alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338
+ hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007
+ point, pointStream = {point: function(x, y) { point = [x, y]; }};
+
+ function albersUsa(coordinates) {
+ var x = coordinates[0], y = coordinates[1];
+ return point = null,
+ (lower48Point.point(x, y), point)
+ || (alaskaPoint.point(x, y), point)
+ || (hawaiiPoint.point(x, y), point);
+ }
+
+ 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 >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska
+ : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii
+ : lower48).invert(coordinates);
+ };
+
+ albersUsa.stream = function(stream) {
+ return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);
+ };
+
+ albersUsa.precision = function(_) {
+ if (!arguments.length) return lower48.precision();
+ lower48.precision(_), alaska.precision(_), hawaii.precision(_);
+ return reset();
+ };
+
+ albersUsa.scale = function(_) {
+ if (!arguments.length) return lower48.scale();
+ lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);
+ return albersUsa.translate(lower48.translate());
+ };
+
+ albersUsa.translate = function(_) {
+ if (!arguments.length) return lower48.translate();
+ var k = lower48.scale(), x = +_[0], y = +_[1];
+
+ lower48Point = lower48
+ .translate(_)
+ .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])
+ .stream(pointStream);
+
+ alaskaPoint = alaska
+ .translate([x - 0.307 * k, y + 0.201 * k])
+ .clipExtent([[x - 0.425 * k + epsilon$2, y + 0.120 * k + epsilon$2], [x - 0.214 * k - epsilon$2, y + 0.234 * k - epsilon$2]])
+ .stream(pointStream);
+
+ hawaiiPoint = hawaii
+ .translate([x - 0.205 * k, y + 0.212 * k])
+ .clipExtent([[x - 0.214 * k + epsilon$2, y + 0.166 * k + epsilon$2], [x - 0.115 * k - epsilon$2, y + 0.234 * k - epsilon$2]])
+ .stream(pointStream);
+
+ return reset();
+ };
+
+ albersUsa.fitExtent = function(extent, object) {
+ return fitExtent(albersUsa, extent, object);
+ };
+
+ albersUsa.fitSize = function(size, object) {
+ return fitSize(albersUsa, size, object);
+ };
+
+ function reset() {
+ cache = cacheStream = null;
+ return albersUsa;
+ }
+
+ return albersUsa.scale(1070);
+};
+
+function azimuthalRaw(scale) {
+ return function(x, y) {
+ var cx = cos$1(x),
+ cy = cos$1(y),
+ k = scale(cx * cy);
+ return [
+ k * cy * sin$1(x),
+ k * sin$1(y)
+ ];
+ }
+}
+
+function azimuthalInvert(angle) {
+ return function(x, y) {
+ var z = sqrt(x * x + y * y),
+ c = angle(z),
+ sc = sin$1(c),
+ cc = cos$1(c);
+ return [
+ atan2(x * sc, z * cc),
+ asin(z && y * sc / z)
+ ];
+ }
+}
+
+var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) {
+ return sqrt(2 / (1 + cxcy));
+});
+
+azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) {
+ return 2 * asin(z / 2);
+});
+
+var azimuthalEqualArea = function() {
+ return projection(azimuthalEqualAreaRaw)
+ .scale(124.75)
+ .clipAngle(180 - 1e-3);
+};
+
+var azimuthalEquidistantRaw = azimuthalRaw(function(c) {
+ return (c = acos(c)) && c / sin$1(c);
+});
+
+azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) {
+ return z;
+});
+
+var azimuthalEquidistant = function() {
+ return projection(azimuthalEquidistantRaw)
+ .scale(79.4188)
+ .clipAngle(180 - 1e-3);
+};
+
+function mercatorRaw(lambda, phi) {
+ return [lambda, log(tan((halfPi$2 + phi) / 2))];
+}
+
+mercatorRaw.invert = function(x, y) {
+ return [x, 2 * atan(exp(y)) - halfPi$2];
+};
+
+var mercator = function() {
+ return mercatorProjection(mercatorRaw)
+ .scale(961 / tau$3);
+};
+
+function mercatorProjection(project) {
+ var m = projection(project),
+ center = m.center,
+ scale = m.scale,
+ translate = m.translate,
+ clipExtent = m.clipExtent,
+ x0 = null, y0, x1, y1; // clip extent
+
+ m.scale = function(_) {
+ return arguments.length ? (scale(_), reclip()) : scale();
+ };
+
+ m.translate = function(_) {
+ return arguments.length ? (translate(_), reclip()) : translate();
+ };
+
+ m.center = function(_) {
+ return arguments.length ? (center(_), reclip()) : center();
+ };
+
+ m.clipExtent = function(_) {
+ return arguments.length ? ((_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1])), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];
+ };
+
+ function reclip() {
+ var k = pi$3 * scale(),
+ t = m(rotation(m.rotate()).invert([0, 0]));
+ return clipExtent(x0 == null
+ ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw
+ ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]
+ : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
+ }
+
+ return reclip();
+}
+
+function tany(y) {
+ return tan((halfPi$2 + y) / 2);
+}
+
+function conicConformalRaw(y0, y1) {
+ var cy0 = cos$1(y0),
+ n = y0 === y1 ? sin$1(y0) : log(cy0 / cos$1(y1)) / log(tany(y1) / tany(y0)),
+ f = cy0 * pow(tany(y0), n) / n;
+
+ if (!n) return mercatorRaw;
+
+ function project(x, y) {
+ if (f > 0) { if (y < -halfPi$2 + epsilon$2) y = -halfPi$2 + epsilon$2; }
+ else { if (y > halfPi$2 - epsilon$2) y = halfPi$2 - epsilon$2; }
+ var r = f / pow(tany(y), n);
+ return [r * sin$1(n * x), f - r * cos$1(n * x)];
+ }
+
+ project.invert = function(x, y) {
+ var fy = f - y, r = sign(n) * sqrt(x * x + fy * fy);
+ return [atan2(x, abs(fy)) / n * sign(fy), 2 * atan(pow(f / r, 1 / n)) - halfPi$2];
+ };
+
+ return project;
+}
+
+var conicConformal = function() {
+ return conicProjection(conicConformalRaw)
+ .scale(109.5)
+ .parallels([30, 30]);
+};
+
+function equirectangularRaw(lambda, phi) {
+ return [lambda, phi];
+}
+
+equirectangularRaw.invert = equirectangularRaw;
+
+var equirectangular = function() {
+ return projection(equirectangularRaw)
+ .scale(152.63);
+};
+
+function conicEquidistantRaw(y0, y1) {
+ var cy0 = cos$1(y0),
+ n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0),
+ g = cy0 / n + y0;
+
+ if (abs(n) < epsilon$2) return equirectangularRaw;
+
+ function project(x, y) {
+ var gy = g - y, nx = n * x;
+ return [gy * sin$1(nx), g - gy * cos$1(nx)];
+ }
+
+ project.invert = function(x, y) {
+ var gy = g - y;
+ return [atan2(x, abs(gy)) / n * sign(gy), g - sign(n) * sqrt(x * x + gy * gy)];
+ };
+
+ return project;
+}
+
+var conicEquidistant = function() {
+ return conicProjection(conicEquidistantRaw)
+ .scale(131.154)
+ .center([0, 13.9389]);
+};
+
+function gnomonicRaw(x, y) {
+ var cy = cos$1(y), k = cos$1(x) * cy;
+ return [cy * sin$1(x) / k, sin$1(y) / k];
+}
+
+gnomonicRaw.invert = azimuthalInvert(atan);
+
+var gnomonic = function() {
+ return projection(gnomonicRaw)
+ .scale(144.049)
+ .clipAngle(60);
+};
+
+function scaleTranslate(kx, ky, tx, ty) {
+ return kx === 1 && ky === 1 && tx === 0 && ty === 0 ? identity$4 : transformer({
+ point: function(x, y) {
+ this.stream.point(x * kx + tx, y * ky + ty);
+ }
+ });
+}
+
+var identity$5 = function() {
+ var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, transform$$1 = identity$4, // scale, translate and reflect
+ x0 = null, y0, x1, y1, clip = identity$4, // clip extent
+ cache,
+ cacheStream,
+ projection;
+
+ function reset() {
+ cache = cacheStream = null;
+ return projection;
+ }
+
+ return projection = {
+ stream: function(stream) {
+ return cache && cacheStream === stream ? cache : cache = transform$$1(clip(cacheStream = stream));
+ },
+ clipExtent: function(_) {
+ return arguments.length ? (clip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
+ },
+ scale: function(_) {
+ return arguments.length ? (transform$$1 = scaleTranslate((k = +_) * sx, k * sy, tx, ty), reset()) : k;
+ },
+ translate: function(_) {
+ return arguments.length ? (transform$$1 = scaleTranslate(k * sx, k * sy, tx = +_[0], ty = +_[1]), reset()) : [tx, ty];
+ },
+ reflectX: function(_) {
+ return arguments.length ? (transform$$1 = scaleTranslate(k * (sx = _ ? -1 : 1), k * sy, tx, ty), reset()) : sx < 0;
+ },
+ reflectY: function(_) {
+ return arguments.length ? (transform$$1 = scaleTranslate(k * sx, k * (sy = _ ? -1 : 1), tx, ty), reset()) : sy < 0;
+ },
+ fitExtent: function(extent, object) {
+ return fitExtent(projection, extent, object);
+ },
+ fitSize: function(size, object) {
+ return fitSize(projection, size, object);
+ }
+ };
+};
+
+function orthographicRaw(x, y) {
+ return [cos$1(y) * sin$1(x), sin$1(y)];
+}
+
+orthographicRaw.invert = azimuthalInvert(asin);
+
+var orthographic = function() {
+ return projection(orthographicRaw)
+ .scale(249.5)
+ .clipAngle(90 + epsilon$2);
+};
+
+function stereographicRaw(x, y) {
+ var cy = cos$1(y), k = 1 + cos$1(x) * cy;
+ return [cy * sin$1(x) / k, sin$1(y) / k];
+}
+
+stereographicRaw.invert = azimuthalInvert(function(z) {
+ return 2 * atan(z);
+});
+
+var stereographic = function() {
+ return projection(stereographicRaw)
+ .scale(250)
+ .clipAngle(142);
+};
+
+function transverseMercatorRaw(lambda, phi) {
+ return [log(tan((halfPi$2 + phi) / 2)), -lambda];
+}
+
+transverseMercatorRaw.invert = function(x, y) {
+ return [-y, 2 * atan(exp(x)) - halfPi$2];
+};
+
+var transverseMercator = function() {
+ var m = mercatorProjection(transverseMercatorRaw),
+ center = m.center,
+ rotate = m.rotate;
+
+ m.center = function(_) {
+ return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);
+ };
+
+ m.rotate = function(_) {
+ return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);
+ };
+
+ return rotate([0, 0, 90])
+ .scale(159.155);
+};
+
+function defaultSeparation(a, b) {
+ return a.parent === b.parent ? 1 : 2;
+}
+
+function meanX(children) {
+ return children.reduce(meanXReduce, 0) / children.length;
+}
+
+function meanXReduce(x, c) {
+ return x + c.x;
+}
+
+function maxY(children) {
+ return 1 + children.reduce(maxYReduce, 0);
+}
+
+function maxYReduce(y, c) {
+ return Math.max(y, c.y);
+}
+
+function leafLeft(node) {
+ var children;
+ while (children = node.children) node = children[0];
+ return node;
+}
+
+function leafRight(node) {
+ var children;
+ while (children = node.children) node = children[children.length - 1];
+ return node;
+}
+
+var cluster = function() {
+ var separation = defaultSeparation,
+ dx = 1,
+ dy = 1,
+ nodeSize = false;
+
+ function cluster(root) {
+ var previousNode,
+ x = 0;
+
+ // First walk, computing the initial x & y values.
+ root.eachAfter(function(node) {
+ var children = node.children;
+ if (children) {
+ node.x = meanX(children);
+ node.y = maxY(children);
+ } else {
+ node.x = previousNode ? x += separation(node, previousNode) : 0;
+ node.y = 0;
+ previousNode = node;
+ }
+ });
+
+ var left = leafLeft(root),
+ right = leafRight(root),
+ x0 = left.x - separation(left, right) / 2,
+ x1 = right.x + separation(right, left) / 2;
+
+ // Second walk, normalizing x & y to the desired size.
+ return root.eachAfter(nodeSize ? function(node) {
+ node.x = (node.x - root.x) * dx;
+ node.y = (root.y - node.y) * dy;
+ } : function(node) {
+ node.x = (node.x - x0) / (x1 - x0) * dx;
+ node.y = (1 - (root.y ? node.y / root.y : 1)) * dy;
+ });
+ }
+
+ cluster.separation = function(x) {
+ return arguments.length ? (separation = x, cluster) : separation;
+ };
+
+ cluster.size = function(x) {
+ return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]);
+ };
+
+ cluster.nodeSize = function(x) {
+ return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null);
+ };
+
+ return cluster;
+};
+
+function count(node) {
+ var sum = 0,
+ children = node.children,
+ i = children && children.length;
+ if (!i) sum = 1;
+ else while (--i >= 0) sum += children[i].value;
+ node.value = sum;
+}
+
+var node_count = function() {
+ return this.eachAfter(count);
+};
+
+var node_each = function(callback) {
+ var node = this, current, next = [node], children, i, n;
+ do {
+ current = next.reverse(), next = [];
+ while (node = current.pop()) {
+ callback(node), children = node.children;
+ if (children) for (i = 0, n = children.length; i < n; ++i) {
+ next.push(children[i]);
+ }
+ }
+ } while (next.length);
+ return this;
+};
+
+var node_eachBefore = function(callback) {
+ var node = this, nodes = [node], children, i;
+ while (node = nodes.pop()) {
+ callback(node), children = node.children;
+ if (children) for (i = children.length - 1; i >= 0; --i) {
+ nodes.push(children[i]);
+ }
+ }
+ return this;
+};
+
+var node_eachAfter = function(callback) {
+ var node = this, nodes = [node], next = [], children, i, n;
+ while (node = nodes.pop()) {
+ next.push(node), children = node.children;
+ if (children) for (i = 0, n = children.length; i < n; ++i) {
+ nodes.push(children[i]);
+ }
+ }
+ while (node = next.pop()) {
+ callback(node);
+ }
+ return this;
+};
+
+var node_sum = function(value) {
+ return this.eachAfter(function(node) {
+ var sum = +value(node.data) || 0,
+ children = node.children,
+ i = children && children.length;
+ while (--i >= 0) sum += children[i].value;
+ node.value = sum;
+ });
+};
+
+var node_sort = function(compare) {
+ return this.eachBefore(function(node) {
+ if (node.children) {
+ node.children.sort(compare);
+ }
+ });
+};
+
+var node_path = function(end) {
+ var start = this,
+ ancestor = leastCommonAncestor(start, end),
+ nodes = [start];
+ while (start !== ancestor) {
+ start = start.parent;
+ nodes.push(start);
+ }
+ var k = nodes.length;
+ while (end !== ancestor) {
+ nodes.splice(k, 0, end);
+ end = end.parent;
+ }
+ return nodes;
+};
+
+function leastCommonAncestor(a, b) {
+ if (a === b) return a;
+ var aNodes = a.ancestors(),
+ bNodes = b.ancestors(),
+ c = null;
+ a = aNodes.pop();
+ b = bNodes.pop();
+ while (a === b) {
+ c = a;
+ a = aNodes.pop();
+ b = bNodes.pop();
+ }
+ return c;
+}
+
+var node_ancestors = function() {
+ var node = this, nodes = [node];
+ while (node = node.parent) {
+ nodes.push(node);
+ }
+ return nodes;
+};
+
+var node_descendants = function() {
+ var nodes = [];
+ this.each(function(node) {
+ nodes.push(node);
+ });
+ return nodes;
+};
+
+var node_leaves = function() {
+ var leaves = [];
+ this.eachBefore(function(node) {
+ if (!node.children) {
+ leaves.push(node);
+ }
+ });
+ return leaves;
+};
+
+var node_links = function() {
+ var root = this, links = [];
+ root.each(function(node) {
+ if (node !== root) { // Don’t include the root’s parent, if any.
+ links.push({source: node.parent, target: node});
+ }
+ });
+ return links;
+};
+
+function hierarchy(data, children) {
+ var root = new Node(data),
+ valued = +data.value && (root.value = data.value),
+ node,
+ nodes = [root],
+ child,
+ childs,
+ i,
+ n;
+
+ if (children == null) children = defaultChildren;
+
+ while (node = nodes.pop()) {
+ if (valued) node.value = +node.data.value;
+ if ((childs = children(node.data)) && (n = childs.length)) {
+ node.children = new Array(n);
+ for (i = n - 1; i >= 0; --i) {
+ nodes.push(child = node.children[i] = new Node(childs[i]));
+ child.parent = node;
+ child.depth = node.depth + 1;
+ }
+ }
+ }
+
+ return root.eachBefore(computeHeight);
+}
+
+function node_copy() {
+ return hierarchy(this).eachBefore(copyData);
+}
+
+function defaultChildren(d) {
+ return d.children;
+}
+
+function copyData(node) {
+ node.data = node.data.data;
+}
+
+function computeHeight(node) {
+ var height = 0;
+ do node.height = height;
+ while ((node = node.parent) && (node.height < ++height));
+}
+
+function Node(data) {
+ this.data = data;
+ this.depth =
+ this.height = 0;
+ this.parent = null;
+}
+
+Node.prototype = hierarchy.prototype = {
+ constructor: Node,
+ count: node_count,
+ each: node_each,
+ eachAfter: node_eachAfter,
+ eachBefore: node_eachBefore,
+ sum: node_sum,
+ sort: node_sort,
+ path: node_path,
+ ancestors: node_ancestors,
+ descendants: node_descendants,
+ leaves: node_leaves,
+ links: node_links,
+ copy: node_copy
+};
+
+function Node$2(value) {
+ this._ = value;
+ this.next = null;
+}
+
+var shuffle$1 = function(array) {
+ var i,
+ n = (array = array.slice()).length,
+ head = null,
+ node = head;
+
+ while (n) {
+ var next = new Node$2(array[n - 1]);
+ if (node) node = node.next = next;
+ else node = head = next;
+ array[i] = array[--n];
+ }
+
+ return {
+ head: head,
+ tail: node
+ };
+};
+
+var enclose = function(circles) {
+ return encloseN(shuffle$1(circles), []);
+};
+
+function encloses(a, b) {
+ var dx = b.x - a.x,
+ dy = b.y - a.y,
+ dr = a.r - b.r;
+ return dr * dr + 1e-6 > dx * dx + dy * dy;
+}
+
+// Returns the smallest circle that contains circles L and intersects circles B.
+function encloseN(L, B) {
+ var circle,
+ l0 = null,
+ l1 = L.head,
+ l2,
+ p1;
+
+ switch (B.length) {
+ case 1: circle = enclose1(B[0]); break;
+ case 2: circle = enclose2(B[0], B[1]); break;
+ case 3: circle = enclose3(B[0], B[1], B[2]); break;
+ }
+
+ while (l1) {
+ p1 = l1._, l2 = l1.next;
+ if (!circle || !encloses(circle, p1)) {
+
+ // Temporarily truncate L before l1.
+ if (l0) L.tail = l0, l0.next = null;
+ else L.head = L.tail = null;
+
+ B.push(p1);
+ circle = encloseN(L, B); // Note: reorders L!
+ B.pop();
+
+ // Move l1 to the front of L and reconnect the truncated list L.
+ if (L.head) l1.next = L.head, L.head = l1;
+ else l1.next = null, L.head = L.tail = l1;
+ l0 = L.tail, l0.next = l2;
+
+ } else {
+ l0 = l1;
+ }
+ l1 = l2;
+ }
+
+ L.tail = l0;
+ return circle;
+}
+
+function enclose1(a) {
+ return {
+ x: a.x,
+ y: a.y,
+ r: a.r
+ };
+}
+
+function enclose2(a, b) {
+ var x1 = a.x, y1 = a.y, r1 = a.r,
+ x2 = b.x, y2 = b.y, r2 = b.r,
+ x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1,
+ l = Math.sqrt(x21 * x21 + y21 * y21);
+ return {
+ x: (x1 + x2 + x21 / l * r21) / 2,
+ y: (y1 + y2 + y21 / l * r21) / 2,
+ r: (l + r1 + r2) / 2
+ };
+}
+
+function enclose3(a, b, c) {
+ var x1 = a.x, y1 = a.y, r1 = a.r,
+ x2 = b.x, y2 = b.y, r2 = b.r,
+ x3 = c.x, y3 = c.y, r3 = c.r,
+ a2 = 2 * (x1 - x2),
+ b2 = 2 * (y1 - y2),
+ c2 = 2 * (r2 - r1),
+ d2 = x1 * x1 + y1 * y1 - r1 * r1 - x2 * x2 - y2 * y2 + r2 * r2,
+ a3 = 2 * (x1 - x3),
+ b3 = 2 * (y1 - y3),
+ c3 = 2 * (r3 - r1),
+ d3 = x1 * x1 + y1 * y1 - r1 * r1 - x3 * x3 - y3 * y3 + r3 * r3,
+ ab = a3 * b2 - a2 * b3,
+ xa = (b2 * d3 - b3 * d2) / ab - x1,
+ xb = (b3 * c2 - b2 * c3) / ab,
+ ya = (a3 * d2 - a2 * d3) / ab - y1,
+ yb = (a2 * c3 - a3 * c2) / ab,
+ A = xb * xb + yb * yb - 1,
+ B = 2 * (xa * xb + ya * yb + r1),
+ C = xa * xa + ya * ya - r1 * r1,
+ r = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A);
+ return {
+ x: xa + xb * r + x1,
+ y: ya + yb * r + y1,
+ r: r
+ };
+}
+
+function place(a, b, c) {
+ var ax = a.x,
+ ay = a.y,
+ da = b.r + c.r,
+ db = a.r + c.r,
+ dx = b.x - ax,
+ dy = b.y - ay,
+ dc = dx * dx + dy * dy;
+ if (dc) {
+ var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc),
+ y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
+ c.x = ax + x * dx + y * dy;
+ c.y = ay + x * dy - y * dx;
+ } else {
+ c.x = ax + db;
+ c.y = ay;
+ }
+}
+
+function intersects(a, b) {
+ var dx = b.x - a.x,
+ dy = b.y - a.y,
+ dr = a.r + b.r;
+ return dr * dr - 1e-6 > dx * dx + dy * dy;
+}
+
+function distance2(node, x, y) {
+ var a = node._,
+ b = node.next._,
+ ab = a.r + b.r,
+ dx = (a.x * b.r + b.x * a.r) / ab - x,
+ dy = (a.y * b.r + b.y * a.r) / ab - y;
+ return dx * dx + dy * dy;
+}
+
+function Node$1(circle) {
+ this._ = circle;
+ this.next = null;
+ this.previous = null;
+}
+
+function packEnclose(circles) {
+ if (!(n = circles.length)) return 0;
+
+ var a, b, c, n;
+
+ // Place the first circle.
+ a = circles[0], a.x = 0, a.y = 0;
+ if (!(n > 1)) return a.r;
+
+ // Place the second circle.
+ b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;
+ if (!(n > 2)) return a.r + b.r;
+
+ // Place the third circle.
+ place(b, a, c = circles[2]);
+
+ // Initialize the weighted centroid.
+ var aa = a.r * a.r,
+ ba = b.r * b.r,
+ ca = c.r * c.r,
+ oa = aa + ba + ca,
+ ox = aa * a.x + ba * b.x + ca * c.x,
+ oy = aa * a.y + ba * b.y + ca * c.y,
+ cx, cy, i, j, k, sj, sk;
+
+ // Initialize the front-chain using the first three circles a, b and c.
+ a = new Node$1(a), b = new Node$1(b), c = new Node$1(c);
+ a.next = c.previous = b;
+ b.next = a.previous = c;
+ c.next = b.previous = a;
+
+ // Attempt to place each remaining circle…
+ pack: for (i = 3; i < n; ++i) {
+ place(a._, b._, c = circles[i]), c = new Node$1(c);
+
+ // Find the closest intersecting circle on the front-chain, if any.
+ // “Closeness” is determined by linear distance along the front-chain.
+ // “Ahead” or “behind” is likewise determined by linear distance.
+ j = b.next, k = a.previous, sj = b._.r, sk = a._.r;
+ do {
+ if (sj <= sk) {
+ if (intersects(j._, c._)) {
+ b = j, a.next = b, b.previous = a, --i;
+ continue pack;
+ }
+ sj += j._.r, j = j.next;
+ } else {
+ if (intersects(k._, c._)) {
+ a = k, a.next = b, b.previous = a, --i;
+ continue pack;
+ }
+ sk += k._.r, k = k.previous;
+ }
+ } while (j !== k.next);
+
+ // Success! Insert the new circle c between a and b.
+ c.previous = a, c.next = b, a.next = b.previous = b = c;
+
+ // Update the weighted centroid.
+ oa += ca = c._.r * c._.r;
+ ox += ca * c._.x;
+ oy += ca * c._.y;
+
+ // Compute the new closest circle pair to the centroid.
+ aa = distance2(a, cx = ox / oa, cy = oy / oa);
+ while ((c = c.next) !== b) {
+ if ((ca = distance2(c, cx, cy)) < aa) {
+ a = c, aa = ca;
+ }
+ }
+ b = a.next;
+ }
+
+ // Compute the enclosing circle of the front chain.
+ a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a);
+
+ // Translate the circles to put the enclosing circle around the origin.
+ for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;
+
+ return c.r;
+}
+
+var siblings = function(circles) {
+ packEnclose(circles);
+ return circles;
+};
+
+function optional(f) {
+ return f == null ? null : required(f);
+}
+
+function required(f) {
+ if (typeof f !== "function") throw new Error;
+ return f;
+}
+
+function constantZero() {
+ return 0;
+}
+
+var constant$8 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+function defaultRadius$1(d) {
+ return Math.sqrt(d.value);
+}
+
+var index$5 = function() {
+ var radius = null,
+ dx = 1,
+ dy = 1,
+ padding = constantZero;
+
+ function pack(root) {
+ root.x = dx / 2, root.y = dy / 2;
+ if (radius) {
+ root.eachBefore(radiusLeaf(radius))
+ .eachAfter(packChildren(padding, 0.5))
+ .eachBefore(translateChild(1));
+ } else {
+ root.eachBefore(radiusLeaf(defaultRadius$1))
+ .eachAfter(packChildren(constantZero, 1))
+ .eachAfter(packChildren(padding, root.r / Math.min(dx, dy)))
+ .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));
+ }
+ return root;
+ }
+
+ pack.radius = function(x) {
+ return arguments.length ? (radius = optional(x), pack) : radius;
+ };
+
+ pack.size = function(x) {
+ return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy];
+ };
+
+ pack.padding = function(x) {
+ return arguments.length ? (padding = typeof x === "function" ? x : constant$8(+x), pack) : padding;
+ };
+
+ return pack;
+};
+
+function radiusLeaf(radius) {
+ return function(node) {
+ if (!node.children) {
+ node.r = Math.max(0, +radius(node) || 0);
+ }
+ };
+}
+
+function packChildren(padding, k) {
+ return function(node) {
+ if (children = node.children) {
+ var children,
+ i,
+ n = children.length,
+ r = padding(node) * k || 0,
+ e;
+
+ if (r) for (i = 0; i < n; ++i) children[i].r += r;
+ e = packEnclose(children);
+ if (r) for (i = 0; i < n; ++i) children[i].r -= r;
+ node.r = e + r;
+ }
+ };
+}
+
+function translateChild(k) {
+ return function(node) {
+ var parent = node.parent;
+ node.r *= k;
+ if (parent) {
+ node.x = parent.x + k * node.x;
+ node.y = parent.y + k * node.y;
+ }
+ };
+}
+
+var roundNode = function(node) {
+ node.x0 = Math.round(node.x0);
+ node.y0 = Math.round(node.y0);
+ node.x1 = Math.round(node.x1);
+ node.y1 = Math.round(node.y1);
+};
+
+var treemapDice = function(parent, x0, y0, x1, y1) {
+ var nodes = parent.children,
+ node,
+ i = -1,
+ n = nodes.length,
+ k = parent.value && (x1 - x0) / parent.value;
+
+ while (++i < n) {
+ node = nodes[i], node.y0 = y0, node.y1 = y1;
+ node.x0 = x0, node.x1 = x0 += node.value * k;
+ }
+};
+
+var partition = function() {
+ var dx = 1,
+ dy = 1,
+ padding = 0,
+ round = false;
+
+ function partition(root) {
+ var n = root.height + 1;
+ root.x0 =
+ root.y0 = padding;
+ root.x1 = dx;
+ root.y1 = dy / n;
+ root.eachBefore(positionNode(dy, n));
+ if (round) root.eachBefore(roundNode);
+ return root;
+ }
+
+ function positionNode(dy, n) {
+ return function(node) {
+ if (node.children) {
+ treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);
+ }
+ var x0 = node.x0,
+ y0 = node.y0,
+ x1 = node.x1 - padding,
+ y1 = node.y1 - padding;
+ if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
+ if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
+ node.x0 = x0;
+ node.y0 = y0;
+ node.x1 = x1;
+ node.y1 = y1;
+ };
+ }
+
+ partition.round = function(x) {
+ return arguments.length ? (round = !!x, partition) : round;
+ };
+
+ partition.size = function(x) {
+ return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];
+ };
+
+ partition.padding = function(x) {
+ return arguments.length ? (padding = +x, partition) : padding;
+ };
+
+ return partition;
+};
+
+var keyPrefix$1 = "$";
+var preroot = {depth: -1};
+var ambiguous = {};
+
+function defaultId(d) {
+ return d.id;
+}
+
+function defaultParentId(d) {
+ return d.parentId;
+}
+
+var stratify = function() {
+ var id = defaultId,
+ parentId = defaultParentId;
+
+ function stratify(data) {
+ var d,
+ i,
+ n = data.length,
+ root,
+ parent,
+ node,
+ nodes = new Array(n),
+ nodeId,
+ nodeKey,
+ nodeByKey = {};
+
+ for (i = 0; i < n; ++i) {
+ d = data[i], node = nodes[i] = new Node(d);
+ if ((nodeId = id(d, i, data)) != null && (nodeId += "")) {
+ nodeKey = keyPrefix$1 + (node.id = nodeId);
+ nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node;
+ }
+ }
+
+ for (i = 0; i < n; ++i) {
+ node = nodes[i], nodeId = parentId(data[i], i, data);
+ if (nodeId == null || !(nodeId += "")) {
+ if (root) throw new Error("multiple roots");
+ root = node;
+ } else {
+ parent = nodeByKey[keyPrefix$1 + nodeId];
+ if (!parent) throw new Error("missing: " + nodeId);
+ if (parent === ambiguous) throw new Error("ambiguous: " + nodeId);
+ if (parent.children) parent.children.push(node);
+ else parent.children = [node];
+ node.parent = parent;
+ }
+ }
+
+ if (!root) throw new Error("no root");
+ root.parent = preroot;
+ root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight);
+ root.parent = null;
+ if (n > 0) throw new Error("cycle");
+
+ return root;
+ }
+
+ stratify.id = function(x) {
+ return arguments.length ? (id = required(x), stratify) : id;
+ };
+
+ stratify.parentId = function(x) {
+ return arguments.length ? (parentId = required(x), stratify) : parentId;
+ };
+
+ return stratify;
+};
+
+function defaultSeparation$1(a, b) {
+ return a.parent === b.parent ? 1 : 2;
+}
+
+// function radialSeparation(a, b) {
+// return (a.parent === b.parent ? 1 : 2) / a.depth;
+// }
+
+// This function is used to traverse the left contour of a subtree (or
+// subforest). It returns the successor of v on this contour. This successor is
+// either given by the leftmost child of v or by the thread of v. The function
+// returns null if and only if v is on the highest level of its subtree.
+function nextLeft(v) {
+ var children = v.children;
+ return children ? children[0] : v.t;
+}
+
+// This function works analogously to nextLeft.
+function nextRight(v) {
+ var children = v.children;
+ return children ? children[children.length - 1] : v.t;
+}
+
+// Shifts the current subtree rooted at w+. This is done by increasing
+// prelim(w+) and mod(w+) by shift.
+function moveSubtree(wm, wp, shift) {
+ var change = shift / (wp.i - wm.i);
+ wp.c -= change;
+ wp.s += shift;
+ wm.c += change;
+ wp.z += shift;
+ wp.m += shift;
+}
+
+// All other shifts, applied to the smaller subtrees between w- and w+, are
+// performed by this function. To prepare the shifts, we have to adjust
+// change(w+), shift(w+), and change(w-).
+function executeShifts(v) {
+ var shift = 0,
+ change = 0,
+ children = v.children,
+ i = children.length,
+ w;
+ while (--i >= 0) {
+ w = children[i];
+ w.z += shift;
+ w.m += shift;
+ shift += w.s + (change += w.c);
+ }
+}
+
+// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,
+// returns the specified (default) ancestor.
+function nextAncestor(vim, v, ancestor) {
+ return vim.a.parent === v.parent ? vim.a : ancestor;
+}
+
+function TreeNode(node, i) {
+ this._ = node;
+ this.parent = null;
+ this.children = null;
+ this.A = null; // default ancestor
+ this.a = this; // ancestor
+ this.z = 0; // prelim
+ this.m = 0; // mod
+ this.c = 0; // change
+ this.s = 0; // shift
+ this.t = null; // thread
+ this.i = i; // number
+}
+
+TreeNode.prototype = Object.create(Node.prototype);
+
+function treeRoot(root) {
+ var tree = new TreeNode(root, 0),
+ node,
+ nodes = [tree],
+ child,
+ children,
+ i,
+ n;
+
+ while (node = nodes.pop()) {
+ if (children = node._.children) {
+ node.children = new Array(n = children.length);
+ for (i = n - 1; i >= 0; --i) {
+ nodes.push(child = node.children[i] = new TreeNode(children[i], i));
+ child.parent = node;
+ }
+ }
+ }
+
+ (tree.parent = new TreeNode(null, 0)).children = [tree];
+ return tree;
+}
+
+// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
+var tree = function() {
+ var separation = defaultSeparation$1,
+ dx = 1,
+ dy = 1,
+ nodeSize = null;
+
+ function tree(root) {
+ var t = treeRoot(root);
+
+ // Compute the layout using Buchheim et al.’s algorithm.
+ t.eachAfter(firstWalk), t.parent.m = -t.z;
+ t.eachBefore(secondWalk);
+
+ // If a fixed node size is specified, scale x and y.
+ if (nodeSize) root.eachBefore(sizeNode);
+
+ // If a fixed tree size is specified, scale x and y based on the extent.
+ // Compute the left-most, right-most, and depth-most nodes for extents.
+ else {
+ var left = root,
+ right = root,
+ bottom = root;
+ root.eachBefore(function(node) {
+ if (node.x < left.x) left = node;
+ if (node.x > right.x) right = node;
+ if (node.depth > bottom.depth) bottom = node;
+ });
+ var s = left === right ? 1 : separation(left, right) / 2,
+ tx = s - left.x,
+ kx = dx / (right.x + s + tx),
+ ky = dy / (bottom.depth || 1);
+ root.eachBefore(function(node) {
+ node.x = (node.x + tx) * kx;
+ node.y = node.depth * ky;
+ });
+ }
+
+ return root;
+ }
+
+ // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is
+ // applied recursively to the children of v, as well as the function
+ // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the
+ // node v is placed to the midpoint of its outermost children.
+ function firstWalk(v) {
+ var children = v.children,
+ siblings = v.parent.children,
+ w = v.i ? siblings[v.i - 1] : null;
+ if (children) {
+ executeShifts(v);
+ var midpoint = (children[0].z + children[children.length - 1].z) / 2;
+ if (w) {
+ v.z = w.z + separation(v._, w._);
+ v.m = v.z - midpoint;
+ } else {
+ v.z = midpoint;
+ }
+ } else if (w) {
+ v.z = w.z + separation(v._, w._);
+ }
+ v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
+ }
+
+ // Computes all real x-coordinates by summing up the modifiers recursively.
+ function secondWalk(v) {
+ v._.x = v.z + v.parent.m;
+ v.m += v.parent.m;
+ }
+
+ // The core of the algorithm. Here, a new subtree is combined with the
+ // previous subtrees. Threads are used to traverse the inside and outside
+ // contours of the left and right subtree up to the highest common level. The
+ // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the
+ // superscript o means outside and i means inside, the subscript - means left
+ // subtree and + means right subtree. For summing up the modifiers along the
+ // contour, we use respective variables si+, si-, so-, and so+. Whenever two
+ // nodes of the inside contours conflict, we compute the left one of the
+ // greatest uncommon ancestors using the function ANCESTOR and call MOVE
+ // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.
+ // Finally, we add a new thread (if necessary).
+ function apportion(v, w, ancestor) {
+ if (w) {
+ var vip = v,
+ vop = v,
+ vim = w,
+ vom = vip.parent.children[0],
+ sip = vip.m,
+ sop = vop.m,
+ sim = vim.m,
+ som = vom.m,
+ shift;
+ while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {
+ vom = nextLeft(vom);
+ vop = nextRight(vop);
+ vop.a = v;
+ shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
+ if (shift > 0) {
+ moveSubtree(nextAncestor(vim, v, ancestor), v, shift);
+ sip += shift;
+ sop += shift;
+ }
+ sim += vim.m;
+ sip += vip.m;
+ som += vom.m;
+ sop += vop.m;
+ }
+ if (vim && !nextRight(vop)) {
+ vop.t = vim;
+ vop.m += sim - sop;
+ }
+ if (vip && !nextLeft(vom)) {
+ vom.t = vip;
+ vom.m += sip - som;
+ ancestor = v;
+ }
+ }
+ return ancestor;
+ }
+
+ function sizeNode(node) {
+ node.x *= dx;
+ node.y = node.depth * dy;
+ }
+
+ tree.separation = function(x) {
+ return arguments.length ? (separation = x, tree) : separation;
+ };
+
+ tree.size = function(x) {
+ return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]);
+ };
+
+ tree.nodeSize = function(x) {
+ return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null);
+ };
+
+ return tree;
+};
+
+var treemapSlice = function(parent, x0, y0, x1, y1) {
+ var nodes = parent.children,
+ node,
+ i = -1,
+ n = nodes.length,
+ k = parent.value && (y1 - y0) / parent.value;
+
+ while (++i < n) {
+ node = nodes[i], node.x0 = x0, node.x1 = x1;
+ node.y0 = y0, node.y1 = y0 += node.value * k;
+ }
+};
+
+var phi = (1 + Math.sqrt(5)) / 2;
+
+function squarifyRatio(ratio, parent, x0, y0, x1, y1) {
+ var rows = [],
+ nodes = parent.children,
+ row,
+ nodeValue,
+ i0 = 0,
+ i1 = 0,
+ n = nodes.length,
+ dx, dy,
+ value = parent.value,
+ sumValue,
+ minValue,
+ maxValue,
+ newRatio,
+ minRatio,
+ alpha,
+ beta;
+
+ while (i0 < n) {
+ dx = x1 - x0, dy = y1 - y0;
+
+ // Find the next non-empty node.
+ do sumValue = nodes[i1++].value; while (!sumValue && i1 < n);
+ minValue = maxValue = sumValue;
+ alpha = Math.max(dy / dx, dx / dy) / (value * ratio);
+ beta = sumValue * sumValue * alpha;
+ minRatio = Math.max(maxValue / beta, beta / minValue);
+
+ // Keep adding nodes while the aspect ratio maintains or improves.
+ for (; i1 < n; ++i1) {
+ sumValue += nodeValue = nodes[i1].value;
+ if (nodeValue < minValue) minValue = nodeValue;
+ if (nodeValue > maxValue) maxValue = nodeValue;
+ beta = sumValue * sumValue * alpha;
+ newRatio = Math.max(maxValue / beta, beta / minValue);
+ if (newRatio > minRatio) { sumValue -= nodeValue; break; }
+ minRatio = newRatio;
+ }
+
+ // Position and record the row orientation.
+ rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)});
+ if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);
+ else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);
+ value -= sumValue, i0 = i1;
+ }
+
+ return rows;
+}
+
+var squarify = ((function custom(ratio) {
+
+ function squarify(parent, x0, y0, x1, y1) {
+ squarifyRatio(ratio, parent, x0, y0, x1, y1);
+ }
+
+ squarify.ratio = function(x) {
+ return custom((x = +x) > 1 ? x : 1);
+ };
+
+ return squarify;
+}))(phi);
+
+var index$6 = function() {
+ var tile = squarify,
+ round = false,
+ dx = 1,
+ dy = 1,
+ paddingStack = [0],
+ paddingInner = constantZero,
+ paddingTop = constantZero,
+ paddingRight = constantZero,
+ paddingBottom = constantZero,
+ paddingLeft = constantZero;
+
+ function treemap(root) {
+ root.x0 =
+ root.y0 = 0;
+ root.x1 = dx;
+ root.y1 = dy;
+ root.eachBefore(positionNode);
+ paddingStack = [0];
+ if (round) root.eachBefore(roundNode);
+ return root;
+ }
+
+ function positionNode(node) {
+ var p = paddingStack[node.depth],
+ x0 = node.x0 + p,
+ y0 = node.y0 + p,
+ x1 = node.x1 - p,
+ y1 = node.y1 - p;
+ if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
+ if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
+ node.x0 = x0;
+ node.y0 = y0;
+ node.x1 = x1;
+ node.y1 = y1;
+ if (node.children) {
+ p = paddingStack[node.depth + 1] = paddingInner(node) / 2;
+ x0 += paddingLeft(node) - p;
+ y0 += paddingTop(node) - p;
+ x1 -= paddingRight(node) - p;
+ y1 -= paddingBottom(node) - p;
+ if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
+ if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
+ tile(node, x0, y0, x1, y1);
+ }
+ }
+
+ treemap.round = function(x) {
+ return arguments.length ? (round = !!x, treemap) : round;
+ };
+
+ treemap.size = function(x) {
+ return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];
+ };
+
+ treemap.tile = function(x) {
+ return arguments.length ? (tile = required(x), treemap) : tile;
+ };
+
+ treemap.padding = function(x) {
+ return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();
+ };
+
+ treemap.paddingInner = function(x) {
+ return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$8(+x), treemap) : paddingInner;
+ };
+
+ treemap.paddingOuter = function(x) {
+ return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();
+ };
+
+ treemap.paddingTop = function(x) {
+ return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$8(+x), treemap) : paddingTop;
+ };
+
+ treemap.paddingRight = function(x) {
+ return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$8(+x), treemap) : paddingRight;
+ };
+
+ treemap.paddingBottom = function(x) {
+ return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$8(+x), treemap) : paddingBottom;
+ };
+
+ treemap.paddingLeft = function(x) {
+ return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$8(+x), treemap) : paddingLeft;
+ };
+
+ return treemap;
+};
+
+var binary = function(parent, x0, y0, x1, y1) {
+ var nodes = parent.children,
+ i, n = nodes.length,
+ sum, sums = new Array(n + 1);
+
+ for (sums[0] = sum = i = 0; i < n; ++i) {
+ sums[i + 1] = sum += nodes[i].value;
+ }
+
+ partition(0, n, parent.value, x0, y0, x1, y1);
+
+ function partition(i, j, value, x0, y0, x1, y1) {
+ if (i >= j - 1) {
+ var node = nodes[i];
+ node.x0 = x0, node.y0 = y0;
+ node.x1 = x1, node.y1 = y1;
+ return;
+ }
+
+ var valueOffset = sums[i],
+ valueTarget = (value / 2) + valueOffset,
+ k = i + 1,
+ hi = j - 1;
+
+ while (k < hi) {
+ var mid = k + hi >>> 1;
+ if (sums[mid] < valueTarget) k = mid + 1;
+ else hi = mid;
+ }
+
+ if ((valueTarget - sums[k - 1]) < (sums[k] - valueTarget) && i + 1 < k) --k;
+
+ var valueLeft = sums[k] - valueOffset,
+ valueRight = value - valueLeft;
+
+ if ((x1 - x0) > (y1 - y0)) {
+ var xk = (x0 * valueRight + x1 * valueLeft) / value;
+ partition(i, k, valueLeft, x0, y0, xk, y1);
+ partition(k, j, valueRight, xk, y0, x1, y1);
+ } else {
+ var yk = (y0 * valueRight + y1 * valueLeft) / value;
+ partition(i, k, valueLeft, x0, y0, x1, yk);
+ partition(k, j, valueRight, x0, yk, x1, y1);
+ }
+ }
+};
+
+var sliceDice = function(parent, x0, y0, x1, y1) {
+ (parent.depth & 1 ? treemapSlice : treemapDice)(parent, x0, y0, x1, y1);
+};
+
+var resquarify = ((function custom(ratio) {
+
+ function resquarify(parent, x0, y0, x1, y1) {
+ if ((rows = parent._squarify) && (rows.ratio === ratio)) {
+ var rows,
+ row,
+ nodes,
+ i,
+ j = -1,
+ n,
+ m = rows.length,
+ value = parent.value;
+
+ while (++j < m) {
+ row = rows[j], nodes = row.children;
+ for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value;
+ if (row.dice) treemapDice(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value);
+ else treemapSlice(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1);
+ value -= row.value;
+ }
+ } else {
+ parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1);
+ rows.ratio = ratio;
+ }
+ }
+
+ resquarify.ratio = function(x) {
+ return custom((x = +x) > 1 ? x : 1);
+ };
+
+ return resquarify;
+}))(phi);
+
+var d3polygonArea = function(polygon) {
+ var i = -1,
+ n = polygon.length,
+ a,
+ b = polygon[n - 1],
+ area = 0;
+
+ while (++i < n) {
+ a = b;
+ b = polygon[i];
+ area += a[1] * b[0] - a[0] * b[1];
+ }
+
+ return area / 2;
+};
+
+var d3polygonCentroid = function(polygon) {
+ var i = -1,
+ n = polygon.length,
+ x = 0,
+ y = 0,
+ a,
+ b = polygon[n - 1],
+ c,
+ k = 0;
+
+ while (++i < n) {
+ a = b;
+ b = polygon[i];
+ k += c = a[0] * b[1] - b[0] * a[1];
+ x += (a[0] + b[0]) * c;
+ y += (a[1] + b[1]) * c;
+ }
+
+ return k *= 3, [x / k, y / k];
+};
+
+// 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.
+var cross$1 = function(a, b, c) {
+ return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
+};
+
+function lexicographicOrder(a, b) {
+ return a[0] - b[0] || a[1] - b[1];
+}
+
+// Computes the upper convex hull per the monotone chain algorithm.
+// Assumes points.length >= 3, is sorted by x, unique in y.
+// Returns an array of indices into points in left-to-right order.
+function computeUpperHullIndexes(points) {
+ var n = points.length,
+ indexes = [0, 1],
+ size = 2;
+
+ for (var i = 2; i < n; ++i) {
+ while (size > 1 && cross$1(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;
+ indexes[size++] = i;
+ }
+
+ return indexes.slice(0, size); // remove popped points
+}
+
+var d3polygonHull = function(points) {
+ if ((n = points.length) < 3) return null;
+
+ var i,
+ n,
+ sortedPoints = new Array(n),
+ flippedPoints = new Array(n);
+
+ for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];
+ sortedPoints.sort(lexicographicOrder);
+ for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
+
+ var upperIndexes = computeUpperHullIndexes(sortedPoints),
+ lowerIndexes = computeUpperHullIndexes(flippedPoints);
+
+ // Construct the hull polygon, removing possible duplicate endpoints.
+ var skipLeft = lowerIndexes[0] === upperIndexes[0],
+ skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
+ hull = [];
+
+ // Add upper hull in right-to-l order.
+ // Then add lower hull in left-to-right order.
+ for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);
+ for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
+
+ return hull;
+};
+
+var contains$1 = function(polygon, point) {
+ var n = polygon.length,
+ p = polygon[n - 1],
+ x = point[0], y = point[1],
+ x0 = p[0], y0 = p[1],
+ x1, y1,
+ inside = false;
+
+ for (var i = 0; i < n; ++i) {
+ p = polygon[i], x1 = p[0], y1 = p[1];
+ if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside;
+ x0 = x1, y0 = y1;
+ }
+
+ return inside;
+};
+
+var length$1 = function(polygon) {
+ var i = -1,
+ n = polygon.length,
+ b = polygon[n - 1],
+ xa,
+ ya,
+ xb = b[0],
+ yb = b[1],
+ perimeter = 0;
+
+ while (++i < n) {
+ xa = xb;
+ ya = yb;
+ b = polygon[i];
+ xb = b[0];
+ yb = b[1];
+ xa -= xb;
+ ya -= yb;
+ perimeter += Math.sqrt(xa * xa + ya * ya);
+ }
+
+ return perimeter;
+};
+
+var slice$3 = [].slice;
+
+var noabort = {};
+
+function Queue(size) {
+ this._size = size;
+ this._call =
+ this._error = null;
+ this._tasks = [];
+ this._data = [];
+ this._waiting =
+ this._active =
+ this._ended =
+ this._start = 0; // inside a synchronous task callback?
+}
+
+Queue.prototype = queue.prototype = {
+ constructor: Queue,
+ defer: function(callback) {
+ if (typeof callback !== "function") throw new Error("invalid callback");
+ if (this._call) throw new Error("defer after await");
+ if (this._error != null) return this;
+ var t = slice$3.call(arguments, 1);
+ t.push(callback);
+ ++this._waiting, this._tasks.push(t);
+ poke$1(this);
+ return this;
+ },
+ abort: function() {
+ if (this._error == null) abort(this, new Error("abort"));
+ return this;
+ },
+ await: function(callback) {
+ if (typeof callback !== "function") throw new Error("invalid callback");
+ if (this._call) throw new Error("multiple await");
+ this._call = function(error, results) { callback.apply(null, [error].concat(results)); };
+ maybeNotify(this);
+ return this;
+ },
+ awaitAll: function(callback) {
+ if (typeof callback !== "function") throw new Error("invalid callback");
+ if (this._call) throw new Error("multiple await");
+ this._call = callback;
+ maybeNotify(this);
+ return this;
+ }
+};
+
+function poke$1(q) {
+ if (!q._start) {
+ try { start$1(q); } // let the current task complete
+ catch (e) {
+ if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously
+ else if (!q._data) throw e; // await callback errored synchronously
+ }
+ }
+}
+
+function start$1(q) {
+ while (q._start = q._waiting && q._active < q._size) {
+ var i = q._ended + q._active,
+ t = q._tasks[i],
+ j = t.length - 1,
+ c = t[j];
+ t[j] = end(q, i);
+ --q._waiting, ++q._active;
+ t = c.apply(null, t);
+ if (!q._tasks[i]) continue; // task finished synchronously
+ q._tasks[i] = t || noabort;
+ }
+}
+
+function end(q, i) {
+ return function(e, r) {
+ if (!q._tasks[i]) return; // ignore multiple callbacks
+ --q._active, ++q._ended;
+ q._tasks[i] = null;
+ if (q._error != null) return; // ignore secondary errors
+ if (e != null) {
+ abort(q, e);
+ } else {
+ q._data[i] = r;
+ if (q._waiting) poke$1(q);
+ else maybeNotify(q);
+ }
+ };
+}
+
+function abort(q, e) {
+ var i = q._tasks.length, t;
+ q._error = e; // ignore active callbacks
+ q._data = undefined; // allow gc
+ q._waiting = NaN; // prevent starting
+
+ while (--i >= 0) {
+ if (t = q._tasks[i]) {
+ q._tasks[i] = null;
+ if (t.abort) {
+ try { t.abort(); }
+ catch (e) { /* ignore */ }
+ }
+ }
+ }
+
+ q._active = NaN; // allow notification
+ maybeNotify(q);
+}
+
+function maybeNotify(q) {
+ if (!q._active && q._call) {
+ var d = q._data;
+ q._data = undefined; // allow gc
+ q._call(q._error, d);
+ }
+}
+
+function queue(concurrency) {
+ if (concurrency == null) concurrency = Infinity;
+ else if (!((concurrency = +concurrency) >= 1)) throw new Error("invalid concurrency");
+ return new Queue(concurrency);
+}
+
+var defaultSource$1 = function() {
+ return Math.random();
+};
+
+var uniform = ((function sourceRandomUniform(source) {
+ function randomUniform(min, max) {
+ min = min == null ? 0 : +min;
+ max = max == null ? 1 : +max;
+ if (arguments.length === 1) max = min, min = 0;
+ else max -= min;
+ return function() {
+ return source() * max + min;
+ };
+ }
+
+ randomUniform.source = sourceRandomUniform;
+
+ return randomUniform;
+}))(defaultSource$1);
+
+var normal = ((function sourceRandomNormal(source) {
+ function randomNormal(mu, sigma) {
+ var x, r;
+ mu = mu == null ? 0 : +mu;
+ sigma = sigma == null ? 1 : +sigma;
+ return function() {
+ var y;
+
+ // If available, use the second previously-generated uniform random.
+ if (x != null) y = x, x = null;
+
+ // Otherwise, generate a new x and y.
+ else do {
+ x = source() * 2 - 1;
+ y = source() * 2 - 1;
+ r = x * x + y * y;
+ } while (!r || r > 1);
+
+ return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r);
+ };
+ }
+
+ randomNormal.source = sourceRandomNormal;
+
+ return randomNormal;
+}))(defaultSource$1);
+
+var logNormal = ((function sourceRandomLogNormal(source) {
+ function randomLogNormal() {
+ var randomNormal = normal.source(source).apply(this, arguments);
+ return function() {
+ return Math.exp(randomNormal());
+ };
+ }
+
+ randomLogNormal.source = sourceRandomLogNormal;
+
+ return randomLogNormal;
+}))(defaultSource$1);
+
+var irwinHall = ((function sourceRandomIrwinHall(source) {
+ function randomIrwinHall(n) {
+ return function() {
+ for (var sum = 0, i = 0; i < n; ++i) sum += source();
+ return sum;
+ };
+ }
+
+ randomIrwinHall.source = sourceRandomIrwinHall;
+
+ return randomIrwinHall;
+}))(defaultSource$1);
+
+var bates = ((function sourceRandomBates(source) {
+ function randomBates(n) {
+ var randomIrwinHall = irwinHall.source(source)(n);
+ return function() {
+ return randomIrwinHall() / n;
+ };
+ }
+
+ randomBates.source = sourceRandomBates;
+
+ return randomBates;
+}))(defaultSource$1);
+
+var exponential$1 = ((function sourceRandomExponential(source) {
+ function randomExponential(lambda) {
+ return function() {
+ return -Math.log(1 - source()) / lambda;
+ };
+ }
+
+ randomExponential.source = sourceRandomExponential;
+
+ return randomExponential;
+}))(defaultSource$1);
+
+var request = function(url, callback) {
+ var request,
+ event = dispatch("beforesend", "progress", "load", "error"),
+ mimeType,
+ headers = map$1(),
+ xhr = new XMLHttpRequest,
+ user = null,
+ password = null,
+ response,
+ responseType,
+ timeout = 0;
+
+ // If IE does not support CORS, use XDomainRequest.
+ if (typeof XDomainRequest !== "undefined"
+ && !("withCredentials" in xhr)
+ && /^(http(s)?:)?\/\//.test(url)) xhr = new XDomainRequest;
+
+ "onload" in xhr
+ ? xhr.onload = xhr.onerror = xhr.ontimeout = respond
+ : xhr.onreadystatechange = function(o) { xhr.readyState > 3 && respond(o); };
+
+ function respond(o) {
+ var status = xhr.status, result;
+ if (!status && hasResponse(xhr)
+ || status >= 200 && status < 300
+ || status === 304) {
+ if (response) {
+ try {
+ result = response.call(request, xhr);
+ } catch (e) {
+ event.call("error", request, e);
+ return;
+ }
+ } else {
+ result = xhr;
+ }
+ event.call("load", request, result);
+ } else {
+ event.call("error", request, o);
+ }
+ }
+
+ xhr.onprogress = function(e) {
+ event.call("progress", request, e);
+ };
+
+ request = {
+ header: function(name, value) {
+ name = (name + "").toLowerCase();
+ if (arguments.length < 2) return headers.get(name);
+ if (value == null) headers.remove(name);
+ else headers.set(name, value + "");
+ return request;
+ },
+
+ // If mimeType is non-null and no Accept header is set, a default is used.
+ mimeType: function(value) {
+ if (!arguments.length) return mimeType;
+ mimeType = value == null ? null : value + "";
+ return request;
+ },
+
+ // Specifies what type the response value should take;
+ // for instance, arraybuffer, blob, document, or text.
+ responseType: function(value) {
+ if (!arguments.length) return responseType;
+ responseType = value;
+ return request;
+ },
+
+ timeout: function(value) {
+ if (!arguments.length) return timeout;
+ timeout = +value;
+ return request;
+ },
+
+ user: function(value) {
+ return arguments.length < 1 ? user : (user = value == null ? null : value + "", request);
+ },
+
+ password: function(value) {
+ return arguments.length < 1 ? password : (password = value == null ? null : value + "", request);
+ },
+
+ // Specify how to convert the response content to a specific type;
+ // changes the callback value on "load" events.
+ response: function(value) {
+ response = value;
+ return request;
+ },
+
+ // Alias for send("GET", …).
+ get: function(data, callback) {
+ return request.send("GET", data, callback);
+ },
+
+ // Alias for send("POST", …).
+ post: function(data, callback) {
+ return request.send("POST", data, callback);
+ },
+
+ // If callback is non-null, it will be used for error and load events.
+ send: function(method, data, callback) {
+ xhr.open(method, url, true, user, password);
+ if (mimeType != null && !headers.has("accept")) headers.set("accept", mimeType + ",*/*");
+ if (xhr.setRequestHeader) headers.each(function(value, name) { xhr.setRequestHeader(name, value); });
+ if (mimeType != null && xhr.overrideMimeType) xhr.overrideMimeType(mimeType);
+ if (responseType != null) xhr.responseType = responseType;
+ if (timeout > 0) xhr.timeout = timeout;
+ if (callback == null && typeof data === "function") callback = data, data = null;
+ if (callback != null && callback.length === 1) callback = fixCallback(callback);
+ if (callback != null) request.on("error", callback).on("load", function(xhr) { callback(null, xhr); });
+ event.call("beforesend", request, xhr);
+ xhr.send(data == null ? null : data);
+ return request;
+ },
+
+ abort: function() {
+ xhr.abort();
+ return request;
+ },
+
+ on: function() {
+ var value = event.on.apply(event, arguments);
+ return value === event ? request : value;
+ }
+ };
+
+ if (callback != null) {
+ if (typeof callback !== "function") throw new Error("invalid callback: " + callback);
+ return request.get(callback);
+ }
+
+ return request;
+};
+
+function fixCallback(callback) {
+ return function(error, xhr) {
+ callback(error == null ? xhr : null);
+ };
+}
+
+function hasResponse(xhr) {
+ var type = xhr.responseType;
+ return type && type !== "text"
+ ? xhr.response // null on error
+ : xhr.responseText; // "" on error
+}
+
+var type$3 = function(defaultMimeType, response) {
+ return function(url, callback) {
+ var r = request(url).mimeType(defaultMimeType).response(response);
+ if (callback != null) {
+ if (typeof callback !== "function") throw new Error("invalid callback: " + callback);
+ return r.get(callback);
+ }
+ return r;
+ };
+};
+
+var html = type$3("text/html", function(xhr) {
+ return document.createRange().createContextualFragment(xhr.responseText);
+});
+
+var json = type$3("application/json", function(xhr) {
+ return JSON.parse(xhr.responseText);
+});
+
+var text = type$3("text/plain", function(xhr) {
+ return xhr.responseText;
+});
+
+var xml = type$3("application/xml", function(xhr) {
+ var xml = xhr.responseXML;
+ if (!xml) throw new Error("parse error");
+ return xml;
+});
+
+var dsv$1 = function(defaultMimeType, parse) {
+ return function(url, row, callback) {
+ if (arguments.length < 3) callback = row, row = null;
+ var r = request(url).mimeType(defaultMimeType);
+ r.row = function(_) { return arguments.length ? r.response(responseOf(parse, row = _)) : row; };
+ r.row(row);
+ return callback ? r.get(callback) : r;
+ };
+};
+
+function responseOf(parse, row) {
+ return function(request$$1) {
+ return parse(request$$1.responseText, row);
+ };
+}
+
+var csv$1 = dsv$1("text/csv", csvParse);
+
+var tsv$1 = dsv$1("text/tab-separated-values", tsvParse);
+
+var array$2 = Array.prototype;
+
+var map$3 = array$2.map;
+var slice$4 = array$2.slice;
+
+var implicit = {name: "implicit"};
+
+function ordinal(range) {
+ var index = map$1(),
+ domain = [],
+ unknown = implicit;
+
+ range = range == null ? [] : slice$4.call(range);
+
+ function scale(d) {
+ var key = d + "", i = index.get(key);
+ if (!i) {
+ if (unknown !== implicit) return unknown;
+ index.set(key, i = domain.push(d));
+ }
+ return range[(i - 1) % range.length];
+ }
+
+ scale.domain = function(_) {
+ if (!arguments.length) return domain.slice();
+ domain = [], index = map$1();
+ var i = -1, n = _.length, d, key;
+ while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d));
+ return scale;
+ };
+
+ scale.range = function(_) {
+ return arguments.length ? (range = slice$4.call(_), scale) : range.slice();
+ };
+
+ scale.unknown = function(_) {
+ return arguments.length ? (unknown = _, scale) : unknown;
+ };
+
+ scale.copy = function() {
+ return ordinal()
+ .domain(domain)
+ .range(range)
+ .unknown(unknown);
+ };
+
+ return scale;
+}
+
+function band() {
+ var scale = ordinal().unknown(undefined),
+ domain = scale.domain,
+ ordinalRange = scale.range,
+ range = [0, 1],
+ step,
+ bandwidth,
+ round = false,
+ paddingInner = 0,
+ paddingOuter = 0,
+ align = 0.5;
+
+ delete scale.unknown;
+
+ function rescale() {
+ var n = domain().length,
+ reverse = range[1] < range[0],
+ start = range[reverse - 0],
+ stop = range[1 - reverse];
+ step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);
+ if (round) step = Math.floor(step);
+ start += (stop - start - step * (n - paddingInner)) * align;
+ bandwidth = step * (1 - paddingInner);
+ if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);
+ var values = sequence(n).map(function(i) { return start + step * i; });
+ return ordinalRange(reverse ? values.reverse() : values);
+ }
+
+ scale.domain = function(_) {
+ return arguments.length ? (domain(_), rescale()) : domain();
+ };
+
+ scale.range = function(_) {
+ return arguments.length ? (range = [+_[0], +_[1]], rescale()) : range.slice();
+ };
+
+ scale.rangeRound = function(_) {
+ return range = [+_[0], +_[1]], round = true, rescale();
+ };
+
+ scale.bandwidth = function() {
+ return bandwidth;
+ };
+
+ scale.step = function() {
+ return step;
+ };
+
+ scale.round = function(_) {
+ return arguments.length ? (round = !!_, rescale()) : round;
+ };
+
+ scale.padding = function(_) {
+ return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
+ };
+
+ scale.paddingInner = function(_) {
+ return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
+ };
+
+ scale.paddingOuter = function(_) {
+ return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter;
+ };
+
+ scale.align = function(_) {
+ return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;
+ };
+
+ scale.copy = function() {
+ return band()
+ .domain(domain())
+ .range(range)
+ .round(round)
+ .paddingInner(paddingInner)
+ .paddingOuter(paddingOuter)
+ .align(align);
+ };
+
+ return rescale();
+}
+
+function pointish(scale) {
+ var copy = scale.copy;
+
+ scale.padding = scale.paddingOuter;
+ delete scale.paddingInner;
+ delete scale.paddingOuter;
+
+ scale.copy = function() {
+ return pointish(copy());
+ };
+
+ return scale;
+}
+
+function point$1() {
+ return pointish(band().paddingInner(1));
+}
+
+var constant$9 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+var number$1 = function(x) {
+ return +x;
+};
+
+var unit = [0, 1];
+
+function deinterpolateLinear(a, b) {
+ return (b -= (a = +a))
+ ? function(x) { return (x - a) / b; }
+ : constant$9(b);
+}
+
+function deinterpolateClamp(deinterpolate) {
+ return function(a, b) {
+ var d = deinterpolate(a = +a, b = +b);
+ return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };
+ };
+}
+
+function reinterpolateClamp(reinterpolate$$1) {
+ return function(a, b) {
+ var r = reinterpolate$$1(a = +a, b = +b);
+ return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };
+ };
+}
+
+function bimap(domain, range, deinterpolate, reinterpolate$$1) {
+ var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
+ if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate$$1(r1, r0);
+ else d0 = deinterpolate(d0, d1), r0 = reinterpolate$$1(r0, r1);
+ return function(x) { return r0(d0(x)); };
+}
+
+function polymap(domain, range, deinterpolate, reinterpolate$$1) {
+ var j = Math.min(domain.length, range.length) - 1,
+ d = new Array(j),
+ r = new Array(j),
+ i = -1;
+
+ // Reverse descending domains.
+ if (domain[j] < domain[0]) {
+ domain = domain.slice().reverse();
+ range = range.slice().reverse();
+ }
+
+ while (++i < j) {
+ d[i] = deinterpolate(domain[i], domain[i + 1]);
+ r[i] = reinterpolate$$1(range[i], range[i + 1]);
+ }
+
+ return function(x) {
+ var i = bisectRight(domain, x, 1, j) - 1;
+ return r[i](d[i](x));
+ };
+}
+
+function copy(source, target) {
+ return target
+ .domain(source.domain())
+ .range(source.range())
+ .interpolate(source.interpolate())
+ .clamp(source.clamp());
+}
+
+// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
+// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].
+function continuous(deinterpolate, reinterpolate$$1) {
+ var domain = unit,
+ range = unit,
+ interpolate = interpolateValue,
+ clamp = false,
+ piecewise,
+ output,
+ input;
+
+ function rescale() {
+ piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
+ output = input = null;
+ return scale;
+ }
+
+ function scale(x) {
+ return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate)))(+x);
+ }
+
+ scale.invert = function(y) {
+ return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate$$1) : reinterpolate$$1)))(+y);
+ };
+
+ scale.domain = function(_) {
+ return arguments.length ? (domain = map$3.call(_, number$1), rescale()) : domain.slice();
+ };
+
+ scale.range = function(_) {
+ return arguments.length ? (range = slice$4.call(_), rescale()) : range.slice();
+ };
+
+ scale.rangeRound = function(_) {
+ return range = slice$4.call(_), interpolate = interpolateRound, rescale();
+ };
+
+ scale.clamp = function(_) {
+ return arguments.length ? (clamp = !!_, rescale()) : clamp;
+ };
+
+ scale.interpolate = function(_) {
+ return arguments.length ? (interpolate = _, rescale()) : interpolate;
+ };
+
+ return rescale();
+}
+
+var tickFormat = function(domain, count, specifier) {
+ var start = domain[0],
+ stop = domain[domain.length - 1],
+ step = tickStep(start, stop, count == null ? 10 : count),
+ precision;
+ specifier = formatSpecifier(specifier == null ? ",f" : specifier);
+ switch (specifier.type) {
+ case "s": {
+ var value = Math.max(Math.abs(start), Math.abs(stop));
+ if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
+ return formatPrefix(specifier, value);
+ }
+ case "":
+ case "e":
+ case "g":
+ case "p":
+ case "r": {
+ if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
+ break;
+ }
+ case "f":
+ case "%": {
+ if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
+ break;
+ }
+ }
+ return format(specifier);
+};
+
+function linearish(scale) {
+ var domain = scale.domain;
+
+ scale.ticks = function(count) {
+ var d = domain();
+ return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
+ };
+
+ scale.tickFormat = function(count, specifier) {
+ return tickFormat(domain(), count, specifier);
+ };
+
+ scale.nice = function(count) {
+ if (count == null) count = 10;
+
+ var d = domain(),
+ i0 = 0,
+ i1 = d.length - 1,
+ start = d[i0],
+ stop = d[i1],
+ step;
+
+ if (stop < start) {
+ step = start, start = stop, stop = step;
+ step = i0, i0 = i1, i1 = step;
+ }
+
+ step = tickIncrement(start, stop, count);
+
+ if (step > 0) {
+ start = Math.floor(start / step) * step;
+ stop = Math.ceil(stop / step) * step;
+ step = tickIncrement(start, stop, count);
+ } else if (step < 0) {
+ start = Math.ceil(start * step) / step;
+ stop = Math.floor(stop * step) / step;
+ step = tickIncrement(start, stop, count);
+ }
+
+ if (step > 0) {
+ d[i0] = Math.floor(start / step) * step;
+ d[i1] = Math.ceil(stop / step) * step;
+ domain(d);
+ } else if (step < 0) {
+ d[i0] = Math.ceil(start * step) / step;
+ d[i1] = Math.floor(stop * step) / step;
+ domain(d);
+ }
+
+ return scale;
+ };
+
+ return scale;
+}
+
+function linear$2() {
+ var scale = continuous(deinterpolateLinear, reinterpolate);
+
+ scale.copy = function() {
+ return copy(scale, linear$2());
+ };
+
+ return linearish(scale);
+}
+
+function identity$6() {
+ var domain = [0, 1];
+
+ function scale(x) {
+ return +x;
+ }
+
+ scale.invert = scale;
+
+ scale.domain = scale.range = function(_) {
+ return arguments.length ? (domain = map$3.call(_, number$1), scale) : domain.slice();
+ };
+
+ scale.copy = function() {
+ return identity$6().domain(domain);
+ };
+
+ return linearish(scale);
+}
+
+var nice = function(domain, interval) {
+ domain = domain.slice();
+
+ var i0 = 0,
+ i1 = domain.length - 1,
+ x0 = domain[i0],
+ x1 = domain[i1],
+ t;
+
+ if (x1 < x0) {
+ t = i0, i0 = i1, i1 = t;
+ t = x0, x0 = x1, x1 = t;
+ }
+
+ domain[i0] = interval.floor(x0);
+ domain[i1] = interval.ceil(x1);
+ return domain;
+};
+
+function deinterpolate(a, b) {
+ return (b = Math.log(b / a))
+ ? function(x) { return Math.log(x / a) / b; }
+ : constant$9(b);
+}
+
+function reinterpolate$1(a, b) {
+ return a < 0
+ ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); }
+ : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); };
+}
+
+function pow10(x) {
+ return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x;
+}
+
+function powp(base) {
+ return base === 10 ? pow10
+ : base === Math.E ? Math.exp
+ : function(x) { return Math.pow(base, x); };
+}
+
+function logp(base) {
+ return base === Math.E ? Math.log
+ : base === 10 && Math.log10
+ || base === 2 && Math.log2
+ || (base = Math.log(base), function(x) { return Math.log(x) / base; });
+}
+
+function reflect(f) {
+ return function(x) {
+ return -f(-x);
+ };
+}
+
+function log$1() {
+ var scale = continuous(deinterpolate, reinterpolate$1).domain([1, 10]),
+ domain = scale.domain,
+ base = 10,
+ logs = logp(10),
+ pows = powp(10);
+
+ function rescale() {
+ logs = logp(base), pows = powp(base);
+ if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows);
+ return scale;
+ }
+
+ scale.base = function(_) {
+ return arguments.length ? (base = +_, rescale()) : base;
+ };
+
+ scale.domain = function(_) {
+ return arguments.length ? (domain(_), rescale()) : domain();
+ };
+
+ scale.ticks = function(count) {
+ var d = domain(),
+ u = d[0],
+ v = d[d.length - 1],
+ r;
+
+ if (r = v < u) i = u, u = v, v = i;
+
+ var i = logs(u),
+ j = logs(v),
+ p,
+ k,
+ t,
+ n = count == null ? 10 : +count,
+ z = [];
+
+ if (!(base % 1) && j - i < n) {
+ i = Math.round(i) - 1, j = Math.round(j) + 1;
+ if (u > 0) for (; i < j; ++i) {
+ for (k = 1, p = pows(i); k < base; ++k) {
+ t = p * k;
+ if (t < u) continue;
+ if (t > v) break;
+ z.push(t);
+ }
+ } else for (; i < j; ++i) {
+ for (k = base - 1, p = pows(i); k >= 1; --k) {
+ t = p * k;
+ if (t < u) continue;
+ if (t > v) break;
+ z.push(t);
+ }
+ }
+ } else {
+ z = ticks(i, j, Math.min(j - i, n)).map(pows);
+ }
+
+ return r ? z.reverse() : z;
+ };
+
+ scale.tickFormat = function(count, specifier) {
+ if (specifier == null) specifier = base === 10 ? ".0e" : ",";
+ if (typeof specifier !== "function") specifier = format(specifier);
+ if (count === Infinity) return specifier;
+ if (count == null) count = 10;
+ var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?
+ return function(d) {
+ var i = d / pows(Math.round(logs(d)));
+ if (i * base < base - 0.5) i *= base;
+ return i <= k ? specifier(d) : "";
+ };
+ };
+
+ scale.nice = function() {
+ return domain(nice(domain(), {
+ floor: function(x) { return pows(Math.floor(logs(x))); },
+ ceil: function(x) { return pows(Math.ceil(logs(x))); }
+ }));
+ };
+
+ scale.copy = function() {
+ return copy(scale, log$1().base(base));
+ };
+
+ return scale;
+}
+
+function raise$1(x, exponent) {
+ return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);
+}
+
+function pow$1() {
+ var exponent = 1,
+ scale = continuous(deinterpolate, reinterpolate),
+ domain = scale.domain;
+
+ function deinterpolate(a, b) {
+ return (b = raise$1(b, exponent) - (a = raise$1(a, exponent)))
+ ? function(x) { return (raise$1(x, exponent) - a) / b; }
+ : constant$9(b);
+ }
+
+ function reinterpolate(a, b) {
+ b = raise$1(b, exponent) - (a = raise$1(a, exponent));
+ return function(t) { return raise$1(a + b * t, 1 / exponent); };
+ }
+
+ scale.exponent = function(_) {
+ return arguments.length ? (exponent = +_, domain(domain())) : exponent;
+ };
+
+ scale.copy = function() {
+ return copy(scale, pow$1().exponent(exponent));
+ };
+
+ return linearish(scale);
+}
+
+function sqrt$1() {
+ return pow$1().exponent(0.5);
+}
+
+function quantile() {
+ var domain = [],
+ range = [],
+ thresholds = [];
+
+ function rescale() {
+ var i = 0, n = Math.max(1, range.length);
+ thresholds = new Array(n - 1);
+ while (++i < n) thresholds[i - 1] = threshold(domain, i / n);
+ return scale;
+ }
+
+ function scale(x) {
+ if (!isNaN(x = +x)) return range[bisectRight(thresholds, x)];
+ }
+
+ scale.invertExtent = function(y) {
+ var i = range.indexOf(y);
+ return i < 0 ? [NaN, NaN] : [
+ i > 0 ? thresholds[i - 1] : domain[0],
+ i < thresholds.length ? thresholds[i] : domain[domain.length - 1]
+ ];
+ };
+
+ scale.domain = function(_) {
+ if (!arguments.length) return domain.slice();
+ domain = [];
+ for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d);
+ domain.sort(ascending);
+ return rescale();
+ };
+
+ scale.range = function(_) {
+ return arguments.length ? (range = slice$4.call(_), rescale()) : range.slice();
+ };
+
+ scale.quantiles = function() {
+ return thresholds.slice();
+ };
+
+ scale.copy = function() {
+ return quantile()
+ .domain(domain)
+ .range(range);
+ };
+
+ return scale;
+}
+
+function quantize$1() {
+ var x0 = 0,
+ x1 = 1,
+ n = 1,
+ domain = [0.5],
+ range = [0, 1];
+
+ function scale(x) {
+ if (x <= x) return range[bisectRight(domain, x, 0, n)];
+ }
+
+ function rescale() {
+ var i = -1;
+ domain = new Array(n);
+ while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
+ return scale;
+ }
+
+ scale.domain = function(_) {
+ return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
+ };
+
+ scale.range = function(_) {
+ return arguments.length ? (n = (range = slice$4.call(_)).length - 1, rescale()) : range.slice();
+ };
+
+ scale.invertExtent = function(y) {
+ var i = range.indexOf(y);
+ return i < 0 ? [NaN, NaN]
+ : i < 1 ? [x0, domain[0]]
+ : i >= n ? [domain[n - 1], x1]
+ : [domain[i - 1], domain[i]];
+ };
+
+ scale.copy = function() {
+ return quantize$1()
+ .domain([x0, x1])
+ .range(range);
+ };
+
+ return linearish(scale);
+}
+
+function threshold$1() {
+ var domain = [0.5],
+ range = [0, 1],
+ n = 1;
+
+ function scale(x) {
+ if (x <= x) return range[bisectRight(domain, x, 0, n)];
+ }
+
+ scale.domain = function(_) {
+ return arguments.length ? (domain = slice$4.call(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice();
+ };
+
+ scale.range = function(_) {
+ return arguments.length ? (range = slice$4.call(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice();
+ };
+
+ scale.invertExtent = function(y) {
+ var i = range.indexOf(y);
+ return [domain[i - 1], domain[i]];
+ };
+
+ scale.copy = function() {
+ return threshold$1()
+ .domain(domain)
+ .range(range);
+ };
+
+ return scale;
+}
+
+var t0$1 = new Date;
+var t1$1 = new Date;
+
+function newInterval(floori, offseti, count, field) {
+
+ function interval(date) {
+ return floori(date = new Date(+date)), date;
+ }
+
+ interval.floor = interval;
+
+ interval.ceil = function(date) {
+ return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
+ };
+
+ interval.round = function(date) {
+ var d0 = interval(date),
+ d1 = interval.ceil(date);
+ return date - d0 < d1 - date ? d0 : d1;
+ };
+
+ interval.offset = function(date, step) {
+ return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
+ };
+
+ interval.range = function(start, stop, step) {
+ var range = [];
+ start = interval.ceil(start);
+ step = step == null ? 1 : Math.floor(step);
+ if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
+ do range.push(new Date(+start)); while (offseti(start, step), floori(start), start < stop)
+ return range;
+ };
+
+ interval.filter = function(test) {
+ return newInterval(function(date) {
+ if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
+ }, function(date, step) {
+ if (date >= date) while (--step >= 0) while (offseti(date, 1), !test(date)) {} // eslint-disable-line no-empty
+ });
+ };
+
+ if (count) {
+ interval.count = function(start, end) {
+ t0$1.setTime(+start), t1$1.setTime(+end);
+ floori(t0$1), floori(t1$1);
+ return Math.floor(count(t0$1, t1$1));
+ };
+
+ interval.every = function(step) {
+ step = Math.floor(step);
+ return !isFinite(step) || !(step > 0) ? null
+ : !(step > 1) ? interval
+ : interval.filter(field
+ ? function(d) { return field(d) % step === 0; }
+ : function(d) { return interval.count(0, d) % step === 0; });
+ };
+ }
+
+ return interval;
+}
+
+var millisecond = newInterval(function() {
+ // noop
+}, function(date, step) {
+ date.setTime(+date + step);
+}, function(start, end) {
+ return end - start;
+});
+
+// An optimized implementation for this simple case.
+millisecond.every = function(k) {
+ k = Math.floor(k);
+ if (!isFinite(k) || !(k > 0)) return null;
+ if (!(k > 1)) return millisecond;
+ return newInterval(function(date) {
+ date.setTime(Math.floor(date / k) * k);
+ }, function(date, step) {
+ date.setTime(+date + step * k);
+ }, function(start, end) {
+ return (end - start) / k;
+ });
+};
+
+var milliseconds = millisecond.range;
+
+var durationSecond$1 = 1e3;
+var durationMinute$1 = 6e4;
+var durationHour$1 = 36e5;
+var durationDay$1 = 864e5;
+var durationWeek$1 = 6048e5;
+
+var second = newInterval(function(date) {
+ date.setTime(Math.floor(date / durationSecond$1) * durationSecond$1);
+}, function(date, step) {
+ date.setTime(+date + step * durationSecond$1);
+}, function(start, end) {
+ return (end - start) / durationSecond$1;
+}, function(date) {
+ return date.getUTCSeconds();
+});
+
+var seconds = second.range;
+
+var minute = newInterval(function(date) {
+ date.setTime(Math.floor(date / durationMinute$1) * durationMinute$1);
+}, function(date, step) {
+ date.setTime(+date + step * durationMinute$1);
+}, function(start, end) {
+ return (end - start) / durationMinute$1;
+}, function(date) {
+ return date.getMinutes();
+});
+
+var minutes = minute.range;
+
+var hour = newInterval(function(date) {
+ var offset = date.getTimezoneOffset() * durationMinute$1 % durationHour$1;
+ if (offset < 0) offset += durationHour$1;
+ date.setTime(Math.floor((+date - offset) / durationHour$1) * durationHour$1 + offset);
+}, function(date, step) {
+ date.setTime(+date + step * durationHour$1);
+}, function(start, end) {
+ return (end - start) / durationHour$1;
+}, function(date) {
+ return date.getHours();
+});
+
+var hours = hour.range;
+
+var day = newInterval(function(date) {
+ date.setHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setDate(date.getDate() + step);
+}, function(start, end) {
+ return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationDay$1;
+}, function(date) {
+ return date.getDate() - 1;
+});
+
+var days = day.range;
+
+function weekday(i) {
+ return newInterval(function(date) {
+ date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
+ date.setHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setDate(date.getDate() + step * 7);
+ }, function(start, end) {
+ return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationWeek$1;
+ });
+}
+
+var sunday = weekday(0);
+var monday = weekday(1);
+var tuesday = weekday(2);
+var wednesday = weekday(3);
+var thursday = weekday(4);
+var friday = weekday(5);
+var saturday = weekday(6);
+
+var sundays = sunday.range;
+var mondays = monday.range;
+var tuesdays = tuesday.range;
+var wednesdays = wednesday.range;
+var thursdays = thursday.range;
+var fridays = friday.range;
+var saturdays = saturday.range;
+
+var month = newInterval(function(date) {
+ date.setDate(1);
+ date.setHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setMonth(date.getMonth() + step);
+}, function(start, end) {
+ return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
+}, function(date) {
+ return date.getMonth();
+});
+
+var months = month.range;
+
+var year = newInterval(function(date) {
+ date.setMonth(0, 1);
+ date.setHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setFullYear(date.getFullYear() + step);
+}, function(start, end) {
+ return end.getFullYear() - start.getFullYear();
+}, function(date) {
+ return date.getFullYear();
+});
+
+// An optimized implementation for this simple case.
+year.every = function(k) {
+ return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
+ date.setFullYear(Math.floor(date.getFullYear() / k) * k);
+ date.setMonth(0, 1);
+ date.setHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setFullYear(date.getFullYear() + step * k);
+ });
+};
+
+var years = year.range;
+
+var utcMinute = newInterval(function(date) {
+ date.setUTCSeconds(0, 0);
+}, function(date, step) {
+ date.setTime(+date + step * durationMinute$1);
+}, function(start, end) {
+ return (end - start) / durationMinute$1;
+}, function(date) {
+ return date.getUTCMinutes();
+});
+
+var utcMinutes = utcMinute.range;
+
+var utcHour = newInterval(function(date) {
+ date.setUTCMinutes(0, 0, 0);
+}, function(date, step) {
+ date.setTime(+date + step * durationHour$1);
+}, function(start, end) {
+ return (end - start) / durationHour$1;
+}, function(date) {
+ return date.getUTCHours();
+});
+
+var utcHours = utcHour.range;
+
+var utcDay = newInterval(function(date) {
+ date.setUTCHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setUTCDate(date.getUTCDate() + step);
+}, function(start, end) {
+ return (end - start) / durationDay$1;
+}, function(date) {
+ return date.getUTCDate() - 1;
+});
+
+var utcDays = utcDay.range;
+
+function utcWeekday(i) {
+ return newInterval(function(date) {
+ date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
+ date.setUTCHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setUTCDate(date.getUTCDate() + step * 7);
+ }, function(start, end) {
+ return (end - start) / durationWeek$1;
+ });
+}
+
+var utcSunday = utcWeekday(0);
+var utcMonday = utcWeekday(1);
+var utcTuesday = utcWeekday(2);
+var utcWednesday = utcWeekday(3);
+var utcThursday = utcWeekday(4);
+var utcFriday = utcWeekday(5);
+var utcSaturday = utcWeekday(6);
+
+var utcSundays = utcSunday.range;
+var utcMondays = utcMonday.range;
+var utcTuesdays = utcTuesday.range;
+var utcWednesdays = utcWednesday.range;
+var utcThursdays = utcThursday.range;
+var utcFridays = utcFriday.range;
+var utcSaturdays = utcSaturday.range;
+
+var utcMonth = newInterval(function(date) {
+ date.setUTCDate(1);
+ date.setUTCHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setUTCMonth(date.getUTCMonth() + step);
+}, function(start, end) {
+ return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
+}, function(date) {
+ return date.getUTCMonth();
+});
+
+var utcMonths = utcMonth.range;
+
+var utcYear = newInterval(function(date) {
+ date.setUTCMonth(0, 1);
+ date.setUTCHours(0, 0, 0, 0);
+}, function(date, step) {
+ date.setUTCFullYear(date.getUTCFullYear() + step);
+}, function(start, end) {
+ return end.getUTCFullYear() - start.getUTCFullYear();
+}, function(date) {
+ return date.getUTCFullYear();
+});
+
+// An optimized implementation for this simple case.
+utcYear.every = function(k) {
+ return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
+ date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
+ date.setUTCMonth(0, 1);
+ date.setUTCHours(0, 0, 0, 0);
+ }, function(date, step) {
+ date.setUTCFullYear(date.getUTCFullYear() + step * k);
+ });
+};
+
+var utcYears = utcYear.range;
+
+function localDate(d) {
+ if (0 <= d.y && d.y < 100) {
+ var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
+ date.setFullYear(d.y);
+ return date;
+ }
+ return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
+}
+
+function utcDate(d) {
+ if (0 <= d.y && d.y < 100) {
+ var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
+ date.setUTCFullYear(d.y);
+ return date;
+ }
+ return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
+}
+
+function newYear(y) {
+ return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};
+}
+
+function formatLocale$1(locale) {
+ var locale_dateTime = locale.dateTime,
+ locale_date = locale.date,
+ locale_time = locale.time,
+ locale_periods = locale.periods,
+ locale_weekdays = locale.days,
+ locale_shortWeekdays = locale.shortDays,
+ locale_months = locale.months,
+ locale_shortMonths = locale.shortMonths;
+
+ var periodRe = formatRe(locale_periods),
+ periodLookup = formatLookup(locale_periods),
+ weekdayRe = formatRe(locale_weekdays),
+ weekdayLookup = formatLookup(locale_weekdays),
+ shortWeekdayRe = formatRe(locale_shortWeekdays),
+ shortWeekdayLookup = formatLookup(locale_shortWeekdays),
+ monthRe = formatRe(locale_months),
+ monthLookup = formatLookup(locale_months),
+ shortMonthRe = formatRe(locale_shortMonths),
+ shortMonthLookup = formatLookup(locale_shortMonths);
+
+ var formats = {
+ "a": formatShortWeekday,
+ "A": formatWeekday,
+ "b": formatShortMonth,
+ "B": formatMonth,
+ "c": null,
+ "d": formatDayOfMonth,
+ "e": formatDayOfMonth,
+ "H": formatHour24,
+ "I": formatHour12,
+ "j": formatDayOfYear,
+ "L": formatMilliseconds,
+ "m": formatMonthNumber,
+ "M": formatMinutes,
+ "p": formatPeriod,
+ "S": formatSeconds,
+ "U": formatWeekNumberSunday,
+ "w": formatWeekdayNumber,
+ "W": formatWeekNumberMonday,
+ "x": null,
+ "X": null,
+ "y": formatYear,
+ "Y": formatFullYear,
+ "Z": formatZone,
+ "%": formatLiteralPercent
+ };
+
+ var utcFormats = {
+ "a": formatUTCShortWeekday,
+ "A": formatUTCWeekday,
+ "b": formatUTCShortMonth,
+ "B": formatUTCMonth,
+ "c": null,
+ "d": formatUTCDayOfMonth,
+ "e": formatUTCDayOfMonth,
+ "H": formatUTCHour24,
+ "I": formatUTCHour12,
+ "j": formatUTCDayOfYear,
+ "L": formatUTCMilliseconds,
+ "m": formatUTCMonthNumber,
+ "M": formatUTCMinutes,
+ "p": formatUTCPeriod,
+ "S": formatUTCSeconds,
+ "U": formatUTCWeekNumberSunday,
+ "w": formatUTCWeekdayNumber,
+ "W": formatUTCWeekNumberMonday,
+ "x": null,
+ "X": null,
+ "y": formatUTCYear,
+ "Y": formatUTCFullYear,
+ "Z": formatUTCZone,
+ "%": formatLiteralPercent
+ };
+
+ var parses = {
+ "a": parseShortWeekday,
+ "A": parseWeekday,
+ "b": parseShortMonth,
+ "B": parseMonth,
+ "c": parseLocaleDateTime,
+ "d": parseDayOfMonth,
+ "e": parseDayOfMonth,
+ "H": parseHour24,
+ "I": parseHour24,
+ "j": parseDayOfYear,
+ "L": parseMilliseconds,
+ "m": parseMonthNumber,
+ "M": parseMinutes,
+ "p": parsePeriod,
+ "S": parseSeconds,
+ "U": parseWeekNumberSunday,
+ "w": parseWeekdayNumber,
+ "W": parseWeekNumberMonday,
+ "x": parseLocaleDate,
+ "X": parseLocaleTime,
+ "y": parseYear,
+ "Y": parseFullYear,
+ "Z": parseZone,
+ "%": parseLiteralPercent
+ };
+
+ // These recursive directive definitions must be deferred.
+ formats.x = newFormat(locale_date, formats);
+ formats.X = newFormat(locale_time, formats);
+ formats.c = newFormat(locale_dateTime, formats);
+ utcFormats.x = newFormat(locale_date, utcFormats);
+ utcFormats.X = newFormat(locale_time, utcFormats);
+ utcFormats.c = newFormat(locale_dateTime, utcFormats);
+
+ function newFormat(specifier, formats) {
+ return function(date) {
+ var string = [],
+ i = -1,
+ j = 0,
+ n = specifier.length,
+ c,
+ pad,
+ format;
+
+ if (!(date instanceof Date)) date = new Date(+date);
+
+ while (++i < n) {
+ if (specifier.charCodeAt(i) === 37) {
+ string.push(specifier.slice(j, i));
+ if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
+ else pad = c === "e" ? " " : "0";
+ if (format = formats[c]) c = format(date, pad);
+ string.push(c);
+ j = i + 1;
+ }
+ }
+
+ string.push(specifier.slice(j, i));
+ return string.join("");
+ };
+ }
+
+ function newParse(specifier, newDate) {
+ return function(string) {
+ var d = newYear(1900),
+ i = parseSpecifier(d, specifier, string += "", 0);
+ if (i != string.length) return null;
+
+ // The am-pm flag is 0 for AM, and 1 for PM.
+ if ("p" in d) d.H = d.H % 12 + d.p * 12;
+
+ // Convert day-of-week and week-of-year to day-of-year.
+ if ("W" in d || "U" in d) {
+ if (!("w" in d)) d.w = "W" in d ? 1 : 0;
+ var day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();
+ d.m = 0;
+ d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7;
+ }
+
+ // If a time zone is specified, all fields are interpreted as UTC and then
+ // offset according to the specified time zone.
+ if ("Z" in d) {
+ d.H += d.Z / 100 | 0;
+ d.M += d.Z % 100;
+ return utcDate(d);
+ }
+
+ // Otherwise, all fields are in local time.
+ return newDate(d);
+ };
+ }
+
+ function parseSpecifier(d, specifier, string, j) {
+ var i = 0,
+ n = specifier.length,
+ m = string.length,
+ c,
+ parse;
+
+ while (i < n) {
+ if (j >= m) return -1;
+ c = specifier.charCodeAt(i++);
+ if (c === 37) {
+ c = specifier.charAt(i++);
+ parse = parses[c in pads ? specifier.charAt(i++) : c];
+ if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
+ } else if (c != string.charCodeAt(j++)) {
+ return -1;
+ }
+ }
+
+ return j;
+ }
+
+ function parsePeriod(d, string, i) {
+ var n = periodRe.exec(string.slice(i));
+ return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseShortWeekday(d, string, i) {
+ var n = shortWeekdayRe.exec(string.slice(i));
+ return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseWeekday(d, string, i) {
+ var n = weekdayRe.exec(string.slice(i));
+ return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseShortMonth(d, string, i) {
+ var n = shortMonthRe.exec(string.slice(i));
+ return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseMonth(d, string, i) {
+ var n = monthRe.exec(string.slice(i));
+ return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
+ }
+
+ function parseLocaleDateTime(d, string, i) {
+ return parseSpecifier(d, locale_dateTime, string, i);
+ }
+
+ function parseLocaleDate(d, string, i) {
+ return parseSpecifier(d, locale_date, string, i);
+ }
+
+ function parseLocaleTime(d, string, i) {
+ return parseSpecifier(d, locale_time, string, i);
+ }
+
+ function formatShortWeekday(d) {
+ return locale_shortWeekdays[d.getDay()];
+ }
+
+ function formatWeekday(d) {
+ return locale_weekdays[d.getDay()];
+ }
+
+ function formatShortMonth(d) {
+ return locale_shortMonths[d.getMonth()];
+ }
+
+ function formatMonth(d) {
+ return locale_months[d.getMonth()];
+ }
+
+ function formatPeriod(d) {
+ return locale_periods[+(d.getHours() >= 12)];
+ }
+
+ function formatUTCShortWeekday(d) {
+ return locale_shortWeekdays[d.getUTCDay()];
+ }
+
+ function formatUTCWeekday(d) {
+ return locale_weekdays[d.getUTCDay()];
+ }
+
+ function formatUTCShortMonth(d) {
+ return locale_shortMonths[d.getUTCMonth()];
+ }
+
+ function formatUTCMonth(d) {
+ return locale_months[d.getUTCMonth()];
+ }
+
+ function formatUTCPeriod(d) {
+ return locale_periods[+(d.getUTCHours() >= 12)];
+ }
+
+ return {
+ format: function(specifier) {
+ var f = newFormat(specifier += "", formats);
+ f.toString = function() { return specifier; };
+ return f;
+ },
+ parse: function(specifier) {
+ var p = newParse(specifier += "", localDate);
+ p.toString = function() { return specifier; };
+ return p;
+ },
+ utcFormat: function(specifier) {
+ var f = newFormat(specifier += "", utcFormats);
+ f.toString = function() { return specifier; };
+ return f;
+ },
+ utcParse: function(specifier) {
+ var p = newParse(specifier, utcDate);
+ p.toString = function() { return specifier; };
+ return p;
+ }
+ };
+}
+
+var pads = {"-": "", "_": " ", "0": "0"};
+var numberRe = /^\s*\d+/;
+var percentRe = /^%/;
+var requoteRe = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
+
+function pad(value, fill, width) {
+ var sign = value < 0 ? "-" : "",
+ string = (sign ? -value : value) + "",
+ length = string.length;
+ return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
+}
+
+function requote(s) {
+ return s.replace(requoteRe, "\\$&");
+}
+
+function formatRe(names) {
+ return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
+}
+
+function formatLookup(names) {
+ var map = {}, i = -1, n = names.length;
+ while (++i < n) map[names[i].toLowerCase()] = i;
+ return map;
+}
+
+function parseWeekdayNumber(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 1));
+ return n ? (d.w = +n[0], i + n[0].length) : -1;
+}
+
+function parseWeekNumberSunday(d, string, i) {
+ var n = numberRe.exec(string.slice(i));
+ return n ? (d.U = +n[0], i + n[0].length) : -1;
+}
+
+function parseWeekNumberMonday(d, string, i) {
+ var n = numberRe.exec(string.slice(i));
+ return n ? (d.W = +n[0], i + n[0].length) : -1;
+}
+
+function parseFullYear(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 4));
+ return n ? (d.y = +n[0], i + n[0].length) : -1;
+}
+
+function parseYear(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
+}
+
+function parseZone(d, string, i) {
+ var n = /^(Z)|([+-]\d\d)(?:\:?(\d\d))?/.exec(string.slice(i, i + 6));
+ return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
+}
+
+function parseMonthNumber(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
+}
+
+function parseDayOfMonth(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.d = +n[0], i + n[0].length) : -1;
+}
+
+function parseDayOfYear(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 3));
+ return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
+}
+
+function parseHour24(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.H = +n[0], i + n[0].length) : -1;
+}
+
+function parseMinutes(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.M = +n[0], i + n[0].length) : -1;
+}
+
+function parseSeconds(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 2));
+ return n ? (d.S = +n[0], i + n[0].length) : -1;
+}
+
+function parseMilliseconds(d, string, i) {
+ var n = numberRe.exec(string.slice(i, i + 3));
+ return n ? (d.L = +n[0], i + n[0].length) : -1;
+}
+
+function parseLiteralPercent(d, string, i) {
+ var n = percentRe.exec(string.slice(i, i + 1));
+ return n ? i + n[0].length : -1;
+}
+
+function formatDayOfMonth(d, p) {
+ return pad(d.getDate(), p, 2);
+}
+
+function formatHour24(d, p) {
+ return pad(d.getHours(), p, 2);
+}
+
+function formatHour12(d, p) {
+ return pad(d.getHours() % 12 || 12, p, 2);
+}
+
+function formatDayOfYear(d, p) {
+ return pad(1 + day.count(year(d), d), p, 3);
+}
+
+function formatMilliseconds(d, p) {
+ return pad(d.getMilliseconds(), p, 3);
+}
+
+function formatMonthNumber(d, p) {
+ return pad(d.getMonth() + 1, p, 2);
+}
+
+function formatMinutes(d, p) {
+ return pad(d.getMinutes(), p, 2);
+}
+
+function formatSeconds(d, p) {
+ return pad(d.getSeconds(), p, 2);
+}
+
+function formatWeekNumberSunday(d, p) {
+ return pad(sunday.count(year(d), d), p, 2);
+}
+
+function formatWeekdayNumber(d) {
+ return d.getDay();
+}
+
+function formatWeekNumberMonday(d, p) {
+ return pad(monday.count(year(d), d), p, 2);
+}
+
+function formatYear(d, p) {
+ return pad(d.getFullYear() % 100, p, 2);
+}
+
+function formatFullYear(d, p) {
+ return pad(d.getFullYear() % 10000, p, 4);
+}
+
+function formatZone(d) {
+ var z = d.getTimezoneOffset();
+ return (z > 0 ? "-" : (z *= -1, "+"))
+ + pad(z / 60 | 0, "0", 2)
+ + pad(z % 60, "0", 2);
+}
+
+function formatUTCDayOfMonth(d, p) {
+ return pad(d.getUTCDate(), p, 2);
+}
+
+function formatUTCHour24(d, p) {
+ return pad(d.getUTCHours(), p, 2);
+}
+
+function formatUTCHour12(d, p) {
+ return pad(d.getUTCHours() % 12 || 12, p, 2);
+}
+
+function formatUTCDayOfYear(d, p) {
+ return pad(1 + utcDay.count(utcYear(d), d), p, 3);
+}
+
+function formatUTCMilliseconds(d, p) {
+ return pad(d.getUTCMilliseconds(), p, 3);
+}
+
+function formatUTCMonthNumber(d, p) {
+ return pad(d.getUTCMonth() + 1, p, 2);
+}
+
+function formatUTCMinutes(d, p) {
+ return pad(d.getUTCMinutes(), p, 2);
+}
+
+function formatUTCSeconds(d, p) {
+ return pad(d.getUTCSeconds(), p, 2);
+}
+
+function formatUTCWeekNumberSunday(d, p) {
+ return pad(utcSunday.count(utcYear(d), d), p, 2);
+}
+
+function formatUTCWeekdayNumber(d) {
+ return d.getUTCDay();
+}
+
+function formatUTCWeekNumberMonday(d, p) {
+ return pad(utcMonday.count(utcYear(d), d), p, 2);
+}
+
+function formatUTCYear(d, p) {
+ return pad(d.getUTCFullYear() % 100, p, 2);
+}
+
+function formatUTCFullYear(d, p) {
+ return pad(d.getUTCFullYear() % 10000, p, 4);
+}
+
+function formatUTCZone() {
+ return "+0000";
+}
+
+function formatLiteralPercent() {
+ return "%";
+}
+
+var locale$2;
+var timeFormat;
+var timeParse;
+var utcFormat;
+var utcParse;
+
+defaultLocale$1({
+ dateTime: "%x, %X",
+ date: "%-m/%-d/%Y",
+ time: "%-I:%M:%S %p",
+ periods: ["AM", "PM"],
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+});
+
+function defaultLocale$1(definition) {
+ locale$2 = formatLocale$1(definition);
+ timeFormat = locale$2.format;
+ timeParse = locale$2.parse;
+ utcFormat = locale$2.utcFormat;
+ utcParse = locale$2.utcParse;
+ return locale$2;
+}
+
+var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ";
+
+function formatIsoNative(date) {
+ return date.toISOString();
+}
+
+var formatIso = Date.prototype.toISOString
+ ? formatIsoNative
+ : utcFormat(isoSpecifier);
+
+function parseIsoNative(string) {
+ var date = new Date(string);
+ return isNaN(date) ? null : date;
+}
+
+var parseIso = +new Date("2000-01-01T00:00:00.000Z")
+ ? parseIsoNative
+ : utcParse(isoSpecifier);
+
+var durationSecond = 1000;
+var durationMinute = durationSecond * 60;
+var durationHour = durationMinute * 60;
+var durationDay = durationHour * 24;
+var durationWeek = durationDay * 7;
+var durationMonth = durationDay * 30;
+var durationYear = durationDay * 365;
+
+function date$1(t) {
+ return new Date(t);
+}
+
+function number$2(t) {
+ return t instanceof Date ? +t : +new Date(+t);
+}
+
+function calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format) {
+ var scale = continuous(deinterpolateLinear, reinterpolate),
+ invert = scale.invert,
+ domain = scale.domain;
+
+ var formatMillisecond = format(".%L"),
+ formatSecond = format(":%S"),
+ formatMinute = format("%I:%M"),
+ formatHour = format("%I %p"),
+ formatDay = format("%a %d"),
+ formatWeek = format("%b %d"),
+ formatMonth = format("%B"),
+ formatYear = format("%Y");
+
+ var tickIntervals = [
+ [second$$1, 1, durationSecond],
+ [second$$1, 5, 5 * durationSecond],
+ [second$$1, 15, 15 * durationSecond],
+ [second$$1, 30, 30 * durationSecond],
+ [minute$$1, 1, durationMinute],
+ [minute$$1, 5, 5 * durationMinute],
+ [minute$$1, 15, 15 * durationMinute],
+ [minute$$1, 30, 30 * durationMinute],
+ [ hour$$1, 1, durationHour ],
+ [ hour$$1, 3, 3 * durationHour ],
+ [ hour$$1, 6, 6 * durationHour ],
+ [ hour$$1, 12, 12 * durationHour ],
+ [ day$$1, 1, durationDay ],
+ [ day$$1, 2, 2 * durationDay ],
+ [ week, 1, durationWeek ],
+ [ month$$1, 1, durationMonth ],
+ [ month$$1, 3, 3 * durationMonth ],
+ [ year$$1, 1, durationYear ]
+ ];
+
+ function tickFormat(date$$1) {
+ return (second$$1(date$$1) < date$$1 ? formatMillisecond
+ : minute$$1(date$$1) < date$$1 ? formatSecond
+ : hour$$1(date$$1) < date$$1 ? formatMinute
+ : day$$1(date$$1) < date$$1 ? formatHour
+ : month$$1(date$$1) < date$$1 ? (week(date$$1) < date$$1 ? formatDay : formatWeek)
+ : year$$1(date$$1) < date$$1 ? formatMonth
+ : formatYear)(date$$1);
+ }
+
+ function tickInterval(interval$$1, start, stop, step) {
+ if (interval$$1 == null) interval$$1 = 10;
+
+ // If a desired tick count is specified, pick a reasonable tick interval
+ // based on the extent of the domain and a rough estimate of tick size.
+ // Otherwise, assume interval is already a time interval and use it.
+ if (typeof interval$$1 === "number") {
+ var target = Math.abs(stop - start) / interval$$1,
+ i = bisector(function(i) { return i[2]; }).right(tickIntervals, target);
+ if (i === tickIntervals.length) {
+ step = tickStep(start / durationYear, stop / durationYear, interval$$1);
+ interval$$1 = year$$1;
+ } else if (i) {
+ i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
+ step = i[1];
+ interval$$1 = i[0];
+ } else {
+ step = tickStep(start, stop, interval$$1);
+ interval$$1 = millisecond$$1;
+ }
+ }
+
+ return step == null ? interval$$1 : interval$$1.every(step);
+ }
+
+ scale.invert = function(y) {
+ return new Date(invert(y));
+ };
+
+ scale.domain = function(_) {
+ return arguments.length ? domain(map$3.call(_, number$2)) : domain().map(date$1);
+ };
+
+ scale.ticks = function(interval$$1, step) {
+ var d = domain(),
+ t0 = d[0],
+ t1 = d[d.length - 1],
+ r = t1 < t0,
+ t;
+ if (r) t = t0, t0 = t1, t1 = t;
+ t = tickInterval(interval$$1, t0, t1, step);
+ t = t ? t.range(t0, t1 + 1) : []; // inclusive stop
+ return r ? t.reverse() : t;
+ };
+
+ scale.tickFormat = function(count, specifier) {
+ return specifier == null ? tickFormat : format(specifier);
+ };
+
+ scale.nice = function(interval$$1, step) {
+ var d = domain();
+ return (interval$$1 = tickInterval(interval$$1, d[0], d[d.length - 1], step))
+ ? domain(nice(d, interval$$1))
+ : scale;
+ };
+
+ scale.copy = function() {
+ return copy(scale, calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format));
+ };
+
+ return scale;
+}
+
+var time = function() {
+ return calendar(year, month, sunday, day, hour, minute, second, millisecond, timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]);
+};
+
+var utcTime = function() {
+ return calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]);
+};
+
+var colors = function(s) {
+ return s.match(/.{6}/g).map(function(x) {
+ return "#" + x;
+ });
+};
+
+var category10 = colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf");
+
+var category20b = colors("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6");
+
+var category20c = colors("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9");
+
+var category20 = colors("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5");
+
+var cubehelix$3 = cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0));
+
+var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
+
+var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
+
+var rainbow = cubehelix();
+
+var rainbow$1 = function(t) {
+ if (t < 0 || t > 1) t -= Math.floor(t);
+ var ts = Math.abs(t - 0.5);
+ rainbow.h = 360 * t - 100;
+ rainbow.s = 1.5 - 1.5 * ts;
+ rainbow.l = 0.8 - 0.9 * ts;
+ return rainbow + "";
+};
+
+function ramp(range) {
+ var n = range.length;
+ return function(t) {
+ return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];
+ };
+}
+
+var viridis = ramp(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"));
+
+var magma = ramp(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf"));
+
+var inferno = ramp(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4"));
+
+var plasma = ramp(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));
+
+function sequential(interpolator) {
+ var x0 = 0,
+ x1 = 1,
+ clamp = false;
+
+ function scale(x) {
+ var t = (x - x0) / (x1 - x0);
+ return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);
+ }
+
+ scale.domain = function(_) {
+ return arguments.length ? (x0 = +_[0], x1 = +_[1], scale) : [x0, x1];
+ };
+
+ scale.clamp = function(_) {
+ return arguments.length ? (clamp = !!_, scale) : clamp;
+ };
+
+ scale.interpolator = function(_) {
+ return arguments.length ? (interpolator = _, scale) : interpolator;
+ };
+
+ scale.copy = function() {
+ return sequential(interpolator).domain([x0, x1]).clamp(clamp);
+ };
+
+ return linearish(scale);
+}
+
+var constant$10 = function(x) {
+ return function constant() {
+ return x;
+ };
+};
+
+var abs$1 = Math.abs;
+var atan2$1 = Math.atan2;
+var cos$2 = Math.cos;
+var max$2 = Math.max;
+var min$1 = Math.min;
+var sin$2 = Math.sin;
+var sqrt$2 = Math.sqrt;
+
+var epsilon$3 = 1e-12;
+var pi$4 = Math.PI;
+var halfPi$3 = pi$4 / 2;
+var tau$4 = 2 * pi$4;
+
+function acos$1(x) {
+ return x > 1 ? 0 : x < -1 ? pi$4 : Math.acos(x);
+}
+
+function asin$1(x) {
+ return x >= 1 ? halfPi$3 : x <= -1 ? -halfPi$3 : Math.asin(x);
+}
+
+function arcInnerRadius(d) {
+ return d.innerRadius;
+}
+
+function arcOuterRadius(d) {
+ return d.outerRadius;
+}
+
+function arcStartAngle(d) {
+ return d.startAngle;
+}
+
+function arcEndAngle(d) {
+ return d.endAngle;
+}
+
+function arcPadAngle(d) {
+ return d && d.padAngle; // Note: optional!
+}
+
+function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
+ var x10 = x1 - x0, y10 = y1 - y0,
+ x32 = x3 - x2, y32 = y3 - y2,
+ t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);
+ return [x0 + t * x10, y0 + t * y10];
+}
+
+// Compute perpendicular offset line of length rc.
+// http://mathworld.wolfram.com/Circle-LineIntersection.html
+function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
+ var x01 = x0 - x1,
+ y01 = y0 - y1,
+ lo = (cw ? rc : -rc) / sqrt$2(x01 * x01 + y01 * y01),
+ ox = lo * y01,
+ oy = -lo * x01,
+ x11 = x0 + ox,
+ y11 = y0 + oy,
+ x10 = x1 + ox,
+ y10 = y1 + oy,
+ x00 = (x11 + x10) / 2,
+ y00 = (y11 + y10) / 2,
+ dx = x10 - x11,
+ dy = y10 - y11,
+ d2 = dx * dx + dy * dy,
+ r = r1 - rc,
+ D = x11 * y10 - x10 * y11,
+ d = (dy < 0 ? -1 : 1) * sqrt$2(max$2(0, r * r * d2 - D * D)),
+ cx0 = (D * dy - dx * d) / d2,
+ cy0 = (-D * dx - dy * d) / d2,
+ cx1 = (D * dy + dx * d) / d2,
+ cy1 = (-D * dx + dy * d) / d2,
+ dx0 = cx0 - x00,
+ dy0 = cy0 - y00,
+ dx1 = cx1 - x00,
+ dy1 = cy1 - y00;
+
+ // Pick the closer of the two intersection points.
+ // TODO Is there a faster way to determine which intersection to use?
+ if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
+
+ return {
+ cx: cx0,
+ cy: cy0,
+ x01: -ox,
+ y01: -oy,
+ x11: cx0 * (r1 / r - 1),
+ y11: cy0 * (r1 / r - 1)
+ };
+}
+
+var arc = function() {
+ var innerRadius = arcInnerRadius,
+ outerRadius = arcOuterRadius,
+ cornerRadius = constant$10(0),
+ padRadius = null,
+ startAngle = arcStartAngle,
+ endAngle = arcEndAngle,
+ padAngle = arcPadAngle,
+ context = null;
+
+ function arc() {
+ var buffer,
+ r,
+ r0 = +innerRadius.apply(this, arguments),
+ r1 = +outerRadius.apply(this, arguments),
+ a0 = startAngle.apply(this, arguments) - halfPi$3,
+ a1 = endAngle.apply(this, arguments) - halfPi$3,
+ da = abs$1(a1 - a0),
+ cw = a1 > a0;
+
+ if (!context) context = buffer = path();
+
+ // Ensure that the outer radius is always larger than the inner radius.
+ if (r1 < r0) r = r1, r1 = r0, r0 = r;
+
+ // Is it a point?
+ if (!(r1 > epsilon$3)) context.moveTo(0, 0);
+
+ // Or is it a circle or annulus?
+ else if (da > tau$4 - epsilon$3) {
+ context.moveTo(r1 * cos$2(a0), r1 * sin$2(a0));
+ context.arc(0, 0, r1, a0, a1, !cw);
+ if (r0 > epsilon$3) {
+ context.moveTo(r0 * cos$2(a1), r0 * sin$2(a1));
+ context.arc(0, 0, r0, a1, a0, cw);
+ }
+ }
+
+ // Or is it a circular or annular sector?
+ else {
+ var a01 = a0,
+ a11 = a1,
+ a00 = a0,
+ a10 = a1,
+ da0 = da,
+ da1 = da,
+ ap = padAngle.apply(this, arguments) / 2,
+ rp = (ap > epsilon$3) && (padRadius ? +padRadius.apply(this, arguments) : sqrt$2(r0 * r0 + r1 * r1)),
+ rc = min$1(abs$1(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
+ rc0 = rc,
+ rc1 = rc,
+ t0,
+ t1;
+
+ // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
+ if (rp > epsilon$3) {
+ var p0 = asin$1(rp / r0 * sin$2(ap)),
+ p1 = asin$1(rp / r1 * sin$2(ap));
+ if ((da0 -= p0 * 2) > epsilon$3) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
+ else da0 = 0, a00 = a10 = (a0 + a1) / 2;
+ if ((da1 -= p1 * 2) > epsilon$3) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
+ else da1 = 0, a01 = a11 = (a0 + a1) / 2;
+ }
+
+ var x01 = r1 * cos$2(a01),
+ y01 = r1 * sin$2(a01),
+ x10 = r0 * cos$2(a10),
+ y10 = r0 * sin$2(a10);
+
+ // Apply rounded corners?
+ if (rc > epsilon$3) {
+ var x11 = r1 * cos$2(a11),
+ y11 = r1 * sin$2(a11),
+ x00 = r0 * cos$2(a00),
+ y00 = r0 * sin$2(a00);
+
+ // Restrict the corner radius according to the sector angle.
+ if (da < pi$4) {
+ var oc = da0 > epsilon$3 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],
+ ax = x01 - oc[0],
+ ay = y01 - oc[1],
+ bx = x11 - oc[0],
+ by = y11 - oc[1],
+ kc = 1 / sin$2(acos$1((ax * bx + ay * by) / (sqrt$2(ax * ax + ay * ay) * sqrt$2(bx * bx + by * by))) / 2),
+ lc = sqrt$2(oc[0] * oc[0] + oc[1] * oc[1]);
+ rc0 = min$1(rc, (r0 - lc) / (kc - 1));
+ rc1 = min$1(rc, (r1 - lc) / (kc + 1));
+ }
+ }
+
+ // Is the sector collapsed to a line?
+ if (!(da1 > epsilon$3)) context.moveTo(x01, y01);
+
+ // Does the sector’s outer ring have rounded corners?
+ else if (rc1 > epsilon$3) {
+ t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
+ t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
+
+ context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
+
+ // Have the corners merged?
+ if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
+
+ // Otherwise, draw the two corners and the ring.
+ else {
+ context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
+ context.arc(0, 0, r1, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
+ context.arc(t1.cx, t1.cy, rc1, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
+ }
+ }
+
+ // Or is the outer ring just a circular arc?
+ else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
+
+ // Is there no inner ring, and it’s a circular sector?
+ // Or perhaps it’s an annular sector collapsed due to padding?
+ if (!(r0 > epsilon$3) || !(da0 > epsilon$3)) context.lineTo(x10, y10);
+
+ // Does the sector’s inner ring (or point) have rounded corners?
+ else if (rc0 > epsilon$3) {
+ t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
+ t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
+
+ context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
+
+ // Have the corners merged?
+ if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
+
+ // Otherwise, draw the two corners and the ring.
+ else {
+ context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
+ context.arc(0, 0, r0, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), cw);
+ context.arc(t1.cx, t1.cy, rc0, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
+ }
+ }
+
+ // Or is the inner ring just a circular arc?
+ else context.arc(0, 0, r0, a10, a00, cw);
+ }
+
+ context.closePath();
+
+ if (buffer) return context = null, buffer + "" || null;
+ }
+
+ arc.centroid = function() {
+ var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
+ a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$4 / 2;
+ return [cos$2(a) * r, sin$2(a) * r];
+ };
+
+ arc.innerRadius = function(_) {
+ return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$10(+_), arc) : innerRadius;
+ };
+
+ arc.outerRadius = function(_) {
+ return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$10(+_), arc) : outerRadius;
+ };
+
+ arc.cornerRadius = function(_) {
+ return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$10(+_), arc) : cornerRadius;
+ };
+
+ arc.padRadius = function(_) {
+ return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$10(+_), arc) : padRadius;
+ };
+
+ arc.startAngle = function(_) {
+ return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$10(+_), arc) : startAngle;
+ };
+
+ arc.endAngle = function(_) {
+ return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$10(+_), arc) : endAngle;
+ };
+
+ arc.padAngle = function(_) {
+ return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$10(+_), arc) : padAngle;
+ };
+
+ arc.context = function(_) {
+ return arguments.length ? ((context = _ == null ? null : _), arc) : context;
+ };
+
+ return arc;
+};
+
+function Linear(context) {
+ this._context = context;
+}
+
+Linear.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
+ case 1: this._point = 2; // proceed
+ default: this._context.lineTo(x, y); break;
+ }
+ }
+};
+
+var curveLinear = function(context) {
+ return new Linear(context);
+};
+
+function x$3(p) {
+ return p[0];
+}
+
+function y$3(p) {
+ return p[1];
+}
+
+var line = function() {
+ var x = x$3,
+ y = y$3,
+ defined = constant$10(true),
+ context = null,
+ curve = curveLinear,
+ output = null;
+
+ function line(data) {
+ var i,
+ n = data.length,
+ d,
+ defined0 = false,
+ buffer;
+
+ if (context == null) output = curve(buffer = path());
+
+ for (i = 0; i <= n; ++i) {
+ if (!(i < n && defined(d = data[i], i, data)) === defined0) {
+ if (defined0 = !defined0) output.lineStart();
+ else output.lineEnd();
+ }
+ if (defined0) output.point(+x(d, i, data), +y(d, i, data));
+ }
+
+ if (buffer) return output = null, buffer + "" || null;
+ }
+
+ line.x = function(_) {
+ return arguments.length ? (x = typeof _ === "function" ? _ : constant$10(+_), line) : x;
+ };
+
+ line.y = function(_) {
+ return arguments.length ? (y = typeof _ === "function" ? _ : constant$10(+_), line) : y;
+ };
+
+ line.defined = function(_) {
+ return arguments.length ? (defined = typeof _ === "function" ? _ : constant$10(!!_), line) : defined;
+ };
+
+ line.curve = function(_) {
+ return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
+ };
+
+ line.context = function(_) {
+ return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
+ };
+
+ return line;
+};
+
+var area$1 = function() {
+ var x0 = x$3,
+ x1 = null,
+ y0 = constant$10(0),
+ y1 = y$3,
+ defined = constant$10(true),
+ context = null,
+ curve = curveLinear,
+ output = null;
+
+ function area(data) {
+ var i,
+ j,
+ k,
+ n = data.length,
+ d,
+ defined0 = false,
+ buffer,
+ x0z = new Array(n),
+ y0z = new Array(n);
+
+ if (context == null) output = curve(buffer = path());
+
+ for (i = 0; i <= n; ++i) {
+ if (!(i < n && defined(d = data[i], i, data)) === defined0) {
+ if (defined0 = !defined0) {
+ j = i;
+ output.areaStart();
+ output.lineStart();
+ } else {
+ output.lineEnd();
+ output.lineStart();
+ for (k = i - 1; k >= j; --k) {
+ output.point(x0z[k], y0z[k]);
+ }
+ output.lineEnd();
+ output.areaEnd();
+ }
+ }
+ if (defined0) {
+ x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);
+ output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);
+ }
+ }
+
+ if (buffer) return output = null, buffer + "" || null;
+ }
+
+ function arealine() {
+ return line().defined(defined).curve(curve).context(context);
+ }
+
+ area.x = function(_) {
+ return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$10(+_), x1 = null, area) : x0;
+ };
+
+ area.x0 = function(_) {
+ return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$10(+_), area) : x0;
+ };
+
+ area.x1 = function(_) {
+ return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$10(+_), area) : x1;
+ };
+
+ area.y = function(_) {
+ return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$10(+_), y1 = null, area) : y0;
+ };
+
+ area.y0 = function(_) {
+ return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$10(+_), area) : y0;
+ };
+
+ area.y1 = function(_) {
+ return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$10(+_), area) : y1;
+ };
+
+ area.lineX0 =
+ area.lineY0 = function() {
+ return arealine().x(x0).y(y0);
+ };
+
+ area.lineY1 = function() {
+ return arealine().x(x0).y(y1);
+ };
+
+ area.lineX1 = function() {
+ return arealine().x(x1).y(y0);
+ };
+
+ area.defined = function(_) {
+ return arguments.length ? (defined = typeof _ === "function" ? _ : constant$10(!!_), area) : defined;
+ };
+
+ area.curve = function(_) {
+ return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;
+ };
+
+ area.context = function(_) {
+ return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;
+ };
+
+ return area;
+};
+
+var descending$1 = function(a, b) {
+ return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
+};
+
+var identity$7 = function(d) {
+ return d;
+};
+
+var pie = function() {
+ var value = identity$7,
+ sortValues = descending$1,
+ sort = null,
+ startAngle = constant$10(0),
+ endAngle = constant$10(tau$4),
+ padAngle = constant$10(0);
+
+ function pie(data) {
+ var i,
+ n = data.length,
+ j,
+ k,
+ sum = 0,
+ index = new Array(n),
+ arcs = new Array(n),
+ a0 = +startAngle.apply(this, arguments),
+ da = Math.min(tau$4, Math.max(-tau$4, endAngle.apply(this, arguments) - a0)),
+ a1,
+ p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),
+ pa = p * (da < 0 ? -1 : 1),
+ v;
+
+ for (i = 0; i < n; ++i) {
+ if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {
+ sum += v;
+ }
+ }
+
+ // Optionally sort the arcs by previously-computed values or by data.
+ if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });
+ else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });
+
+ // Compute the arcs! They are stored in the original data's order.
+ for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {
+ j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {
+ data: data[j],
+ index: i,
+ value: v,
+ startAngle: a0,
+ endAngle: a1,
+ padAngle: p
+ };
+ }
+
+ return arcs;
+ }
+
+ pie.value = function(_) {
+ return arguments.length ? (value = typeof _ === "function" ? _ : constant$10(+_), pie) : value;
+ };
+
+ pie.sortValues = function(_) {
+ return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;
+ };
+
+ pie.sort = function(_) {
+ return arguments.length ? (sort = _, sortValues = null, pie) : sort;
+ };
+
+ pie.startAngle = function(_) {
+ return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$10(+_), pie) : startAngle;
+ };
+
+ pie.endAngle = function(_) {
+ return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$10(+_), pie) : endAngle;
+ };
+
+ pie.padAngle = function(_) {
+ return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$10(+_), pie) : padAngle;
+ };
+
+ return pie;
+};
+
+var curveRadialLinear = curveRadial(curveLinear);
+
+function Radial(curve) {
+ this._curve = curve;
+}
+
+Radial.prototype = {
+ areaStart: function() {
+ this._curve.areaStart();
+ },
+ areaEnd: function() {
+ this._curve.areaEnd();
+ },
+ lineStart: function() {
+ this._curve.lineStart();
+ },
+ lineEnd: function() {
+ this._curve.lineEnd();
+ },
+ point: function(a, r) {
+ this._curve.point(r * Math.sin(a), r * -Math.cos(a));
+ }
+};
+
+function curveRadial(curve) {
+
+ function radial(context) {
+ return new Radial(curve(context));
+ }
+
+ radial._curve = curve;
+
+ return radial;
+}
+
+function radialLine(l) {
+ var c = l.curve;
+
+ l.angle = l.x, delete l.x;
+ l.radius = l.y, delete l.y;
+
+ l.curve = function(_) {
+ return arguments.length ? c(curveRadial(_)) : c()._curve;
+ };
+
+ return l;
+}
+
+var radialLine$1 = function() {
+ return radialLine(line().curve(curveRadialLinear));
+};
+
+var radialArea = function() {
+ var a = area$1().curve(curveRadialLinear),
+ c = a.curve,
+ x0 = a.lineX0,
+ x1 = a.lineX1,
+ y0 = a.lineY0,
+ y1 = a.lineY1;
+
+ a.angle = a.x, delete a.x;
+ a.startAngle = a.x0, delete a.x0;
+ a.endAngle = a.x1, delete a.x1;
+ a.radius = a.y, delete a.y;
+ a.innerRadius = a.y0, delete a.y0;
+ a.outerRadius = a.y1, delete a.y1;
+ a.lineStartAngle = function() { return radialLine(x0()); }, delete a.lineX0;
+ a.lineEndAngle = function() { return radialLine(x1()); }, delete a.lineX1;
+ a.lineInnerRadius = function() { return radialLine(y0()); }, delete a.lineY0;
+ a.lineOuterRadius = function() { return radialLine(y1()); }, delete a.lineY1;
+
+ a.curve = function(_) {
+ return arguments.length ? c(curveRadial(_)) : c()._curve;
+ };
+
+ return a;
+};
+
+var slice$5 = Array.prototype.slice;
+
+var radialPoint = function(x, y) {
+ return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
+};
+
+function linkSource(d) {
+ return d.source;
+}
+
+function linkTarget(d) {
+ return d.target;
+}
+
+function link$2(curve) {
+ var source = linkSource,
+ target = linkTarget,
+ x = x$3,
+ y = y$3,
+ context = null;
+
+ function link() {
+ var buffer, argv = slice$5.call(arguments), s = source.apply(this, argv), t = target.apply(this, argv);
+ if (!context) context = buffer = path();
+ curve(context, +x.apply(this, (argv[0] = s, argv)), +y.apply(this, argv), +x.apply(this, (argv[0] = t, argv)), +y.apply(this, argv));
+ if (buffer) return context = null, buffer + "" || null;
+ }
+
+ link.source = function(_) {
+ return arguments.length ? (source = _, link) : source;
+ };
+
+ link.target = function(_) {
+ return arguments.length ? (target = _, link) : target;
+ };
+
+ link.x = function(_) {
+ return arguments.length ? (x = typeof _ === "function" ? _ : constant$10(+_), link) : x;
+ };
+
+ link.y = function(_) {
+ return arguments.length ? (y = typeof _ === "function" ? _ : constant$10(+_), link) : y;
+ };
+
+ link.context = function(_) {
+ return arguments.length ? ((context = _ == null ? null : _), link) : context;
+ };
+
+ return link;
+}
+
+function curveHorizontal(context, x0, y0, x1, y1) {
+ context.moveTo(x0, y0);
+ context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);
+}
+
+function curveVertical(context, x0, y0, x1, y1) {
+ context.moveTo(x0, y0);
+ context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);
+}
+
+function curveRadial$1(context, x0, y0, x1, y1) {
+ var p0 = radialPoint(x0, y0),
+ p1 = radialPoint(x0, y0 = (y0 + y1) / 2),
+ p2 = radialPoint(x1, y0),
+ p3 = radialPoint(x1, y1);
+ context.moveTo(p0[0], p0[1]);
+ context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
+}
+
+function linkHorizontal() {
+ return link$2(curveHorizontal);
+}
+
+function linkVertical() {
+ return link$2(curveVertical);
+}
+
+function linkRadial() {
+ var l = link$2(curveRadial$1);
+ l.angle = l.x, delete l.x;
+ l.radius = l.y, delete l.y;
+ return l;
+}
+
+var circle$2 = {
+ draw: function(context, size) {
+ var r = Math.sqrt(size / pi$4);
+ context.moveTo(r, 0);
+ context.arc(0, 0, r, 0, tau$4);
+ }
+};
+
+var cross$2 = {
+ draw: function(context, size) {
+ var r = Math.sqrt(size / 5) / 2;
+ context.moveTo(-3 * r, -r);
+ context.lineTo(-r, -r);
+ context.lineTo(-r, -3 * r);
+ context.lineTo(r, -3 * r);
+ context.lineTo(r, -r);
+ context.lineTo(3 * r, -r);
+ context.lineTo(3 * r, r);
+ context.lineTo(r, r);
+ context.lineTo(r, 3 * r);
+ context.lineTo(-r, 3 * r);
+ context.lineTo(-r, r);
+ context.lineTo(-3 * r, r);
+ context.closePath();
+ }
+};
+
+var tan30 = Math.sqrt(1 / 3);
+var tan30_2 = tan30 * 2;
+
+var diamond = {
+ draw: function(context, size) {
+ var y = Math.sqrt(size / tan30_2),
+ x = y * tan30;
+ context.moveTo(0, -y);
+ context.lineTo(x, 0);
+ context.lineTo(0, y);
+ context.lineTo(-x, 0);
+ context.closePath();
+ }
+};
+
+var ka = 0.89081309152928522810;
+var kr = Math.sin(pi$4 / 10) / Math.sin(7 * pi$4 / 10);
+var kx = Math.sin(tau$4 / 10) * kr;
+var ky = -Math.cos(tau$4 / 10) * kr;
+
+var star = {
+ draw: function(context, size) {
+ var r = Math.sqrt(size * ka),
+ x = kx * r,
+ y = ky * r;
+ context.moveTo(0, -r);
+ context.lineTo(x, y);
+ for (var i = 1; i < 5; ++i) {
+ var a = tau$4 * i / 5,
+ c = Math.cos(a),
+ s = Math.sin(a);
+ context.lineTo(s * r, -c * r);
+ context.lineTo(c * x - s * y, s * x + c * y);
+ }
+ context.closePath();
+ }
+};
+
+var square = {
+ draw: function(context, size) {
+ var w = Math.sqrt(size),
+ x = -w / 2;
+ context.rect(x, x, w, w);
+ }
+};
+
+var sqrt3 = Math.sqrt(3);
+
+var triangle = {
+ draw: function(context, size) {
+ var y = -Math.sqrt(size / (sqrt3 * 3));
+ context.moveTo(0, y * 2);
+ context.lineTo(-sqrt3 * y, -y);
+ context.lineTo(sqrt3 * y, -y);
+ context.closePath();
+ }
+};
+
+var c = -0.5;
+var s = Math.sqrt(3) / 2;
+var k = 1 / Math.sqrt(12);
+var a = (k / 2 + 1) * 3;
+
+var wye = {
+ draw: function(context, size) {
+ var r = Math.sqrt(size / a),
+ x0 = r / 2,
+ y0 = r * k,
+ x1 = x0,
+ y1 = r * k + r,
+ x2 = -x1,
+ y2 = y1;
+ context.moveTo(x0, y0);
+ context.lineTo(x1, y1);
+ context.lineTo(x2, y2);
+ context.lineTo(c * x0 - s * y0, s * x0 + c * y0);
+ context.lineTo(c * x1 - s * y1, s * x1 + c * y1);
+ context.lineTo(c * x2 - s * y2, s * x2 + c * y2);
+ context.lineTo(c * x0 + s * y0, c * y0 - s * x0);
+ context.lineTo(c * x1 + s * y1, c * y1 - s * x1);
+ context.lineTo(c * x2 + s * y2, c * y2 - s * x2);
+ context.closePath();
+ }
+};
+
+var symbols = [
+ circle$2,
+ cross$2,
+ diamond,
+ square,
+ star,
+ triangle,
+ wye
+];
+
+var symbol = function() {
+ var type = constant$10(circle$2),
+ size = constant$10(64),
+ context = null;
+
+ function symbol() {
+ var buffer;
+ if (!context) context = buffer = path();
+ type.apply(this, arguments).draw(context, +size.apply(this, arguments));
+ if (buffer) return context = null, buffer + "" || null;
+ }
+
+ symbol.type = function(_) {
+ return arguments.length ? (type = typeof _ === "function" ? _ : constant$10(_), symbol) : type;
+ };
+
+ symbol.size = function(_) {
+ return arguments.length ? (size = typeof _ === "function" ? _ : constant$10(+_), symbol) : size;
+ };
+
+ symbol.context = function(_) {
+ return arguments.length ? (context = _ == null ? null : _, symbol) : context;
+ };
+
+ return symbol;
+};
+
+var noop$2 = function() {};
+
+function point$2(that, x, y) {
+ that._context.bezierCurveTo(
+ (2 * that._x0 + that._x1) / 3,
+ (2 * that._y0 + that._y1) / 3,
+ (that._x0 + 2 * that._x1) / 3,
+ (that._y0 + 2 * that._y1) / 3,
+ (that._x0 + 4 * that._x1 + x) / 6,
+ (that._y0 + 4 * that._y1 + y) / 6
+ );
+}
+
+function Basis(context) {
+ this._context = context;
+}
+
+Basis.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x0 = this._x1 =
+ this._y0 = this._y1 = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ switch (this._point) {
+ case 3: point$2(this, this._x1, this._y1); // proceed
+ case 2: this._context.lineTo(this._x1, this._y1); break;
+ }
+ if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
+ case 1: this._point = 2; break;
+ case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed
+ default: point$2(this, x, y); break;
+ }
+ this._x0 = this._x1, this._x1 = x;
+ this._y0 = this._y1, this._y1 = y;
+ }
+};
+
+var basis$2 = function(context) {
+ return new Basis(context);
+};
+
+function BasisClosed(context) {
+ this._context = context;
+}
+
+BasisClosed.prototype = {
+ areaStart: noop$2,
+ areaEnd: noop$2,
+ lineStart: function() {
+ this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =
+ this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ switch (this._point) {
+ case 1: {
+ this._context.moveTo(this._x2, this._y2);
+ this._context.closePath();
+ break;
+ }
+ case 2: {
+ this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);
+ this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);
+ this._context.closePath();
+ break;
+ }
+ case 3: {
+ this.point(this._x2, this._y2);
+ this.point(this._x3, this._y3);
+ this.point(this._x4, this._y4);
+ break;
+ }
+ }
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; this._x2 = x, this._y2 = y; break;
+ case 1: this._point = 2; this._x3 = x, this._y3 = y; break;
+ case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;
+ default: point$2(this, x, y); break;
+ }
+ this._x0 = this._x1, this._x1 = x;
+ this._y0 = this._y1, this._y1 = y;
+ }
+};
+
+var basisClosed$1 = function(context) {
+ return new BasisClosed(context);
+};
+
+function BasisOpen(context) {
+ this._context = context;
+}
+
+BasisOpen.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x0 = this._x1 =
+ this._y0 = this._y1 = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; break;
+ case 1: this._point = 2; break;
+ case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;
+ case 3: this._point = 4; // proceed
+ default: point$2(this, x, y); break;
+ }
+ this._x0 = this._x1, this._x1 = x;
+ this._y0 = this._y1, this._y1 = y;
+ }
+};
+
+var basisOpen = function(context) {
+ return new BasisOpen(context);
+};
+
+function Bundle(context, beta) {
+ this._basis = new Basis(context);
+ this._beta = beta;
+}
+
+Bundle.prototype = {
+ lineStart: function() {
+ this._x = [];
+ this._y = [];
+ this._basis.lineStart();
+ },
+ lineEnd: function() {
+ var x = this._x,
+ y = this._y,
+ j = x.length - 1;
+
+ if (j > 0) {
+ var x0 = x[0],
+ y0 = y[0],
+ dx = x[j] - x0,
+ dy = y[j] - y0,
+ i = -1,
+ t;
+
+ while (++i <= j) {
+ t = i / j;
+ this._basis.point(
+ this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),
+ this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)
+ );
+ }
+ }
+
+ this._x = this._y = null;
+ this._basis.lineEnd();
+ },
+ point: function(x, y) {
+ this._x.push(+x);
+ this._y.push(+y);
+ }
+};
+
+var bundle = ((function custom(beta) {
+
+ function bundle(context) {
+ return beta === 1 ? new Basis(context) : new Bundle(context, beta);
+ }
+
+ bundle.beta = function(beta) {
+ return custom(+beta);
+ };
+
+ return bundle;
+}))(0.85);
+
+function point$3(that, x, y) {
+ that._context.bezierCurveTo(
+ that._x1 + that._k * (that._x2 - that._x0),
+ that._y1 + that._k * (that._y2 - that._y0),
+ that._x2 + that._k * (that._x1 - x),
+ that._y2 + that._k * (that._y1 - y),
+ that._x2,
+ that._y2
+ );
+}
+
+function Cardinal(context, tension) {
+ this._context = context;
+ this._k = (1 - tension) / 6;
+}
+
+Cardinal.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x0 = this._x1 = this._x2 =
+ this._y0 = this._y1 = this._y2 = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ switch (this._point) {
+ case 2: this._context.lineTo(this._x2, this._y2); break;
+ case 3: point$3(this, this._x1, this._y1); break;
+ }
+ if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
+ case 1: this._point = 2; this._x1 = x, this._y1 = y; break;
+ case 2: this._point = 3; // proceed
+ default: point$3(this, x, y); break;
+ }
+ this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
+ this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
+ }
+};
+
+var cardinal = ((function custom(tension) {
+
+ function cardinal(context) {
+ return new Cardinal(context, tension);
+ }
+
+ cardinal.tension = function(tension) {
+ return custom(+tension);
+ };
+
+ return cardinal;
+}))(0);
+
+function CardinalClosed(context, tension) {
+ this._context = context;
+ this._k = (1 - tension) / 6;
+}
+
+CardinalClosed.prototype = {
+ areaStart: noop$2,
+ areaEnd: noop$2,
+ lineStart: function() {
+ this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
+ this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ switch (this._point) {
+ case 1: {
+ this._context.moveTo(this._x3, this._y3);
+ this._context.closePath();
+ break;
+ }
+ case 2: {
+ this._context.lineTo(this._x3, this._y3);
+ this._context.closePath();
+ break;
+ }
+ case 3: {
+ this.point(this._x3, this._y3);
+ this.point(this._x4, this._y4);
+ this.point(this._x5, this._y5);
+ break;
+ }
+ }
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
+ case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
+ case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
+ default: point$3(this, x, y); break;
+ }
+ this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
+ this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
+ }
+};
+
+var cardinalClosed = ((function custom(tension) {
+
+ function cardinal$$1(context) {
+ return new CardinalClosed(context, tension);
+ }
+
+ cardinal$$1.tension = function(tension) {
+ return custom(+tension);
+ };
+
+ return cardinal$$1;
+}))(0);
+
+function CardinalOpen(context, tension) {
+ this._context = context;
+ this._k = (1 - tension) / 6;
+}
+
+CardinalOpen.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x0 = this._x1 = this._x2 =
+ this._y0 = this._y1 = this._y2 = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; break;
+ case 1: this._point = 2; break;
+ case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
+ case 3: this._point = 4; // proceed
+ default: point$3(this, x, y); break;
+ }
+ this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
+ this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
+ }
+};
+
+var cardinalOpen = ((function custom(tension) {
+
+ function cardinal$$1(context) {
+ return new CardinalOpen(context, tension);
+ }
+
+ cardinal$$1.tension = function(tension) {
+ return custom(+tension);
+ };
+
+ return cardinal$$1;
+}))(0);
+
+function point$4(that, x, y) {
+ var x1 = that._x1,
+ y1 = that._y1,
+ x2 = that._x2,
+ y2 = that._y2;
+
+ if (that._l01_a > epsilon$3) {
+ var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,
+ n = 3 * that._l01_a * (that._l01_a + that._l12_a);
+ x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;
+ y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;
+ }
+
+ if (that._l23_a > epsilon$3) {
+ var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,
+ m = 3 * that._l23_a * (that._l23_a + that._l12_a);
+ x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;
+ y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;
+ }
+
+ that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);
+}
+
+function CatmullRom(context, alpha) {
+ this._context = context;
+ this._alpha = alpha;
+}
+
+CatmullRom.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x0 = this._x1 = this._x2 =
+ this._y0 = this._y1 = this._y2 = NaN;
+ this._l01_a = this._l12_a = this._l23_a =
+ this._l01_2a = this._l12_2a = this._l23_2a =
+ this._point = 0;
+ },
+ lineEnd: function() {
+ switch (this._point) {
+ case 2: this._context.lineTo(this._x2, this._y2); break;
+ case 3: this.point(this._x2, this._y2); break;
+ }
+ if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+
+ if (this._point) {
+ var x23 = this._x2 - x,
+ y23 = this._y2 - y;
+ this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
+ }
+
+ switch (this._point) {
+ case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
+ case 1: this._point = 2; break;
+ case 2: this._point = 3; // proceed
+ default: point$4(this, x, y); break;
+ }
+
+ this._l01_a = this._l12_a, this._l12_a = this._l23_a;
+ this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
+ this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
+ this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
+ }
+};
+
+var catmullRom = ((function custom(alpha) {
+
+ function catmullRom(context) {
+ return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);
+ }
+
+ catmullRom.alpha = function(alpha) {
+ return custom(+alpha);
+ };
+
+ return catmullRom;
+}))(0.5);
+
+function CatmullRomClosed(context, alpha) {
+ this._context = context;
+ this._alpha = alpha;
+}
+
+CatmullRomClosed.prototype = {
+ areaStart: noop$2,
+ areaEnd: noop$2,
+ lineStart: function() {
+ this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
+ this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
+ this._l01_a = this._l12_a = this._l23_a =
+ this._l01_2a = this._l12_2a = this._l23_2a =
+ this._point = 0;
+ },
+ lineEnd: function() {
+ switch (this._point) {
+ case 1: {
+ this._context.moveTo(this._x3, this._y3);
+ this._context.closePath();
+ break;
+ }
+ case 2: {
+ this._context.lineTo(this._x3, this._y3);
+ this._context.closePath();
+ break;
+ }
+ case 3: {
+ this.point(this._x3, this._y3);
+ this.point(this._x4, this._y4);
+ this.point(this._x5, this._y5);
+ break;
+ }
+ }
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+
+ if (this._point) {
+ var x23 = this._x2 - x,
+ y23 = this._y2 - y;
+ this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
+ }
+
+ switch (this._point) {
+ case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
+ case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
+ case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
+ default: point$4(this, x, y); break;
+ }
+
+ this._l01_a = this._l12_a, this._l12_a = this._l23_a;
+ this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
+ this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
+ this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
+ }
+};
+
+var catmullRomClosed = ((function custom(alpha) {
+
+ function catmullRom$$1(context) {
+ return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);
+ }
+
+ catmullRom$$1.alpha = function(alpha) {
+ return custom(+alpha);
+ };
+
+ return catmullRom$$1;
+}))(0.5);
+
+function CatmullRomOpen(context, alpha) {
+ this._context = context;
+ this._alpha = alpha;
+}
+
+CatmullRomOpen.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x0 = this._x1 = this._x2 =
+ this._y0 = this._y1 = this._y2 = NaN;
+ this._l01_a = this._l12_a = this._l23_a =
+ this._l01_2a = this._l12_2a = this._l23_2a =
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+
+ if (this._point) {
+ var x23 = this._x2 - x,
+ y23 = this._y2 - y;
+ this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
+ }
+
+ switch (this._point) {
+ case 0: this._point = 1; break;
+ case 1: this._point = 2; break;
+ case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
+ case 3: this._point = 4; // proceed
+ default: point$4(this, x, y); break;
+ }
+
+ this._l01_a = this._l12_a, this._l12_a = this._l23_a;
+ this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
+ this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
+ this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
+ }
+};
+
+var catmullRomOpen = ((function custom(alpha) {
+
+ function catmullRom$$1(context) {
+ return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);
+ }
+
+ catmullRom$$1.alpha = function(alpha) {
+ return custom(+alpha);
+ };
+
+ return catmullRom$$1;
+}))(0.5);
+
+function LinearClosed(context) {
+ this._context = context;
+}
+
+LinearClosed.prototype = {
+ areaStart: noop$2,
+ areaEnd: noop$2,
+ lineStart: function() {
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (this._point) this._context.closePath();
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ if (this._point) this._context.lineTo(x, y);
+ else this._point = 1, this._context.moveTo(x, y);
+ }
+};
+
+var linearClosed = function(context) {
+ return new LinearClosed(context);
+};
+
+function sign$1(x) {
+ return x < 0 ? -1 : 1;
+}
+
+// Calculate the slopes of the tangents (Hermite-type interpolation) based on
+// the following paper: Steffen, M. 1990. A Simple Method for Monotonic
+// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.
+// NOV(II), P. 443, 1990.
+function slope3(that, x2, y2) {
+ var h0 = that._x1 - that._x0,
+ h1 = x2 - that._x1,
+ s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
+ s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
+ p = (s0 * h1 + s1 * h0) / (h0 + h1);
+ return (sign$1(s0) + sign$1(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
+}
+
+// Calculate a one-sided slope.
+function slope2(that, t) {
+ var h = that._x1 - that._x0;
+ return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;
+}
+
+// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
+// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
+// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
+function point$5(that, t0, t1) {
+ var x0 = that._x0,
+ y0 = that._y0,
+ x1 = that._x1,
+ y1 = that._y1,
+ dx = (x1 - x0) / 3;
+ that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
+}
+
+function MonotoneX(context) {
+ this._context = context;
+}
+
+MonotoneX.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x0 = this._x1 =
+ this._y0 = this._y1 =
+ this._t0 = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ switch (this._point) {
+ case 2: this._context.lineTo(this._x1, this._y1); break;
+ case 3: point$5(this, this._t0, slope2(this, this._t0)); break;
+ }
+ if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
+ this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ var t1 = NaN;
+
+ x = +x, y = +y;
+ if (x === this._x1 && y === this._y1) return; // Ignore coincident points.
+ switch (this._point) {
+ case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
+ case 1: this._point = 2; break;
+ case 2: this._point = 3; point$5(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
+ default: point$5(this, this._t0, t1 = slope3(this, x, y)); break;
+ }
+
+ this._x0 = this._x1, this._x1 = x;
+ this._y0 = this._y1, this._y1 = y;
+ this._t0 = t1;
+ }
+};
+
+function MonotoneY(context) {
+ this._context = new ReflectContext(context);
+}
+
+(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {
+ MonotoneX.prototype.point.call(this, y, x);
+};
+
+function ReflectContext(context) {
+ this._context = context;
+}
+
+ReflectContext.prototype = {
+ moveTo: function(x, y) { this._context.moveTo(y, x); },
+ closePath: function() { this._context.closePath(); },
+ lineTo: function(x, y) { this._context.lineTo(y, x); },
+ bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }
+};
+
+function monotoneX(context) {
+ return new MonotoneX(context);
+}
+
+function monotoneY(context) {
+ return new MonotoneY(context);
+}
+
+function Natural(context) {
+ this._context = context;
+}
+
+Natural.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x = [];
+ this._y = [];
+ },
+ lineEnd: function() {
+ var x = this._x,
+ y = this._y,
+ n = x.length;
+
+ if (n) {
+ this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);
+ if (n === 2) {
+ this._context.lineTo(x[1], y[1]);
+ } else {
+ var px = controlPoints(x),
+ py = controlPoints(y);
+ for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
+ this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
+ }
+ }
+ }
+
+ if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();
+ this._line = 1 - this._line;
+ this._x = this._y = null;
+ },
+ point: function(x, y) {
+ this._x.push(+x);
+ this._y.push(+y);
+ }
+};
+
+// See https://www.particleincell.com/2012/bezier-splines/ for derivation.
+function controlPoints(x) {
+ var i,
+ n = x.length - 1,
+ m,
+ a = new Array(n),
+ b = new Array(n),
+ r = new Array(n);
+ a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
+ for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
+ a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
+ for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
+ a[n - 1] = r[n - 1] / b[n - 1];
+ for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
+ b[n - 1] = (x[n] + a[n - 1]) / 2;
+ for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
+ return [a, b];
+}
+
+var natural = function(context) {
+ return new Natural(context);
+};
+
+function Step(context, t) {
+ this._context = context;
+ this._t = t;
+}
+
+Step.prototype = {
+ areaStart: function() {
+ this._line = 0;
+ },
+ areaEnd: function() {
+ this._line = NaN;
+ },
+ lineStart: function() {
+ this._x = this._y = NaN;
+ this._point = 0;
+ },
+ lineEnd: function() {
+ if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);
+ if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
+ if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
+ },
+ point: function(x, y) {
+ x = +x, y = +y;
+ switch (this._point) {
+ case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
+ case 1: this._point = 2; // proceed
+ default: {
+ if (this._t <= 0) {
+ this._context.lineTo(this._x, y);
+ this._context.lineTo(x, y);
+ } else {
+ var x1 = this._x * (1 - this._t) + x * this._t;
+ this._context.lineTo(x1, this._y);
+ this._context.lineTo(x1, y);
+ }
+ break;
+ }
+ }
+ this._x = x, this._y = y;
+ }
+};
+
+var step = function(context) {
+ return new Step(context, 0.5);
+};
+
+function stepBefore(context) {
+ return new Step(context, 0);
+}
+
+function stepAfter(context) {
+ return new Step(context, 1);
+}
+
+var none$1 = function(series, order) {
+ if (!((n = series.length) > 1)) return;
+ for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
+ s0 = s1, s1 = series[order[i]];
+ for (j = 0; j < m; ++j) {
+ s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];
+ }
+ }
+};
+
+var none$2 = function(series) {
+ var n = series.length, o = new Array(n);
+ while (--n >= 0) o[n] = n;
+ return o;
+};
+
+function stackValue(d, key) {
+ return d[key];
+}
+
+var stack = function() {
+ var keys = constant$10([]),
+ order = none$2,
+ offset = none$1,
+ value = stackValue;
+
+ function stack(data) {
+ var kz = keys.apply(this, arguments),
+ i,
+ m = data.length,
+ n = kz.length,
+ sz = new Array(n),
+ oz;
+
+ for (i = 0; i < n; ++i) {
+ for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {
+ si[j] = sij = [0, +value(data[j], ki, j, data)];
+ sij.data = data[j];
+ }
+ si.key = ki;
+ }
+
+ for (i = 0, oz = order(sz); i < n; ++i) {
+ sz[oz[i]].index = i;
+ }
+
+ offset(sz, oz);
+ return sz;
+ }
+
+ stack.keys = function(_) {
+ return arguments.length ? (keys = typeof _ === "function" ? _ : constant$10(slice$5.call(_)), stack) : keys;
+ };
+
+ stack.value = function(_) {
+ return arguments.length ? (value = typeof _ === "function" ? _ : constant$10(+_), stack) : value;
+ };
+
+ stack.order = function(_) {
+ return arguments.length ? (order = _ == null ? none$2 : typeof _ === "function" ? _ : constant$10(slice$5.call(_)), stack) : order;
+ };
+
+ stack.offset = function(_) {
+ return arguments.length ? (offset = _ == null ? none$1 : _, stack) : offset;
+ };
+
+ return stack;
+};
+
+var expand = function(series, order) {
+ if (!((n = series.length) > 0)) return;
+ for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {
+ for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;
+ if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;
+ }
+ none$1(series, order);
+};
+
+var diverging = function(series, order) {
+ if (!((n = series.length) > 1)) return;
+ for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {
+ for (yp = yn = 0, i = 0; i < n; ++i) {
+ if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {
+ d[0] = yp, d[1] = yp += dy;
+ } else if (dy < 0) {
+ d[1] = yn, d[0] = yn += dy;
+ } else {
+ d[0] = yp;
+ }
+ }
+ }
+};
+
+var silhouette = function(series, order) {
+ if (!((n = series.length) > 0)) return;
+ for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {
+ for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;
+ s0[j][1] += s0[j][0] = -y / 2;
+ }
+ none$1(series, order);
+};
+
+var wiggle = function(series, order) {
+ if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;
+ for (var y = 0, j = 1, s0, m, n; j < m; ++j) {
+ for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {
+ var si = series[order[i]],
+ sij0 = si[j][1] || 0,
+ sij1 = si[j - 1][1] || 0,
+ s3 = (sij0 - sij1) / 2;
+ for (var k = 0; k < i; ++k) {
+ var sk = series[order[k]],
+ skj0 = sk[j][1] || 0,
+ skj1 = sk[j - 1][1] || 0;
+ s3 += skj0 - skj1;
+ }
+ s1 += sij0, s2 += s3 * sij0;
+ }
+ s0[j - 1][1] += s0[j - 1][0] = y;
+ if (s1) y -= s2 / s1;
+ }
+ s0[j - 1][1] += s0[j - 1][0] = y;
+ none$1(series, order);
+};
+
+var ascending$2 = function(series) {
+ var sums = series.map(sum$2);
+ return none$2(series).sort(function(a, b) { return sums[a] - sums[b]; });
+};
+
+function sum$2(series) {
+ var s = 0, i = -1, n = series.length, v;
+ while (++i < n) if (v = +series[i][1]) s += v;
+ return s;
+}
+
+var descending$2 = function(series) {
+ return ascending$2(series).reverse();
+};
+
+var insideOut = function(series) {
+ var n = series.length,
+ i,
+ j,
+ sums = series.map(sum$2),
+ order = none$2(series).sort(function(a, b) { return sums[b] - sums[a]; }),
+ top = 0,
+ bottom = 0,
+ tops = [],
+ bottoms = [];
+
+ for (i = 0; i < n; ++i) {
+ j = order[i];
+ if (top < bottom) {
+ top += sums[j];
+ tops.push(j);
+ } else {
+ bottom += sums[j];
+ bottoms.push(j);
+ }
+ }
+
+ return bottoms.reverse().concat(tops);
+};
+
+var reverse = function(series) {
+ return none$2(series).reverse();
+};
+
+var constant$11 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+function x$4(d) {
+ return d[0];
+}
+
+function y$4(d) {
+ return d[1];
+}
+
+function RedBlackTree() {
+ this._ = null; // root node
+}
+
+function RedBlackNode(node) {
+ node.U = // parent node
+ node.C = // color - true for red, false for black
+ node.L = // left node
+ node.R = // right node
+ node.P = // previous node
+ node.N = null; // next node
+}
+
+RedBlackTree.prototype = {
+ constructor: RedBlackTree,
+
+ insert: function(after, node) {
+ var parent, grandpa, uncle;
+
+ if (after) {
+ node.P = after;
+ node.N = after.N;
+ if (after.N) after.N.P = node;
+ after.N = node;
+ if (after.R) {
+ after = after.R;
+ while (after.L) after = after.L;
+ after.L = node;
+ } else {
+ after.R = node;
+ }
+ parent = after;
+ } else if (this._) {
+ after = RedBlackFirst(this._);
+ node.P = null;
+ node.N = after;
+ after.P = after.L = node;
+ parent = after;
+ } else {
+ node.P = node.N = null;
+ this._ = node;
+ parent = null;
+ }
+ node.L = node.R = null;
+ node.U = parent;
+ node.C = true;
+
+ after = node;
+ while (parent && parent.C) {
+ grandpa = parent.U;
+ if (parent === grandpa.L) {
+ uncle = grandpa.R;
+ if (uncle && uncle.C) {
+ parent.C = uncle.C = false;
+ grandpa.C = true;
+ after = grandpa;
+ } else {
+ if (after === parent.R) {
+ RedBlackRotateLeft(this, parent);
+ after = parent;
+ parent = after.U;
+ }
+ parent.C = false;
+ grandpa.C = true;
+ RedBlackRotateRight(this, grandpa);
+ }
+ } else {
+ uncle = grandpa.L;
+ if (uncle && uncle.C) {
+ parent.C = uncle.C = false;
+ grandpa.C = true;
+ after = grandpa;
+ } else {
+ if (after === parent.L) {
+ RedBlackRotateRight(this, parent);
+ after = parent;
+ parent = after.U;
+ }
+ parent.C = false;
+ grandpa.C = true;
+ RedBlackRotateLeft(this, grandpa);
+ }
+ }
+ parent = after.U;
+ }
+ this._.C = false;
+ },
+
+ remove: function(node) {
+ if (node.N) node.N.P = node.P;
+ if (node.P) node.P.N = node.N;
+ node.N = node.P = null;
+
+ var parent = node.U,
+ sibling,
+ left = node.L,
+ right = node.R,
+ next,
+ red;
+
+ if (!left) next = right;
+ else if (!right) next = left;
+ else next = RedBlackFirst(right);
+
+ if (parent) {
+ if (parent.L === node) parent.L = next;
+ else parent.R = next;
+ } else {
+ this._ = next;
+ }
+
+ if (left && right) {
+ red = next.C;
+ next.C = node.C;
+ next.L = left;
+ left.U = next;
+ if (next !== right) {
+ parent = next.U;
+ next.U = node.U;
+ node = next.R;
+ parent.L = node;
+ next.R = right;
+ right.U = next;
+ } else {
+ next.U = parent;
+ parent = next;
+ node = next.R;
+ }
+ } else {
+ red = node.C;
+ node = next;
+ }
+
+ if (node) node.U = parent;
+ if (red) return;
+ if (node && node.C) { node.C = false; return; }
+
+ do {
+ if (node === this._) break;
+ if (node === parent.L) {
+ sibling = parent.R;
+ if (sibling.C) {
+ sibling.C = false;
+ parent.C = true;
+ RedBlackRotateLeft(this, parent);
+ sibling = parent.R;
+ }
+ if ((sibling.L && sibling.L.C)
+ || (sibling.R && sibling.R.C)) {
+ if (!sibling.R || !sibling.R.C) {
+ sibling.L.C = false;
+ sibling.C = true;
+ RedBlackRotateRight(this, sibling);
+ sibling = parent.R;
+ }
+ sibling.C = parent.C;
+ parent.C = sibling.R.C = false;
+ RedBlackRotateLeft(this, parent);
+ node = this._;
+ break;
+ }
+ } else {
+ sibling = parent.L;
+ if (sibling.C) {
+ sibling.C = false;
+ parent.C = true;
+ RedBlackRotateRight(this, parent);
+ sibling = parent.L;
+ }
+ if ((sibling.L && sibling.L.C)
+ || (sibling.R && sibling.R.C)) {
+ if (!sibling.L || !sibling.L.C) {
+ sibling.R.C = false;
+ sibling.C = true;
+ RedBlackRotateLeft(this, sibling);
+ sibling = parent.L;
+ }
+ sibling.C = parent.C;
+ parent.C = sibling.L.C = false;
+ RedBlackRotateRight(this, parent);
+ node = this._;
+ break;
+ }
+ }
+ sibling.C = true;
+ node = parent;
+ parent = parent.U;
+ } while (!node.C);
+
+ if (node) node.C = false;
+ }
+};
+
+function RedBlackRotateLeft(tree, node) {
+ var p = node,
+ q = node.R,
+ parent = p.U;
+
+ if (parent) {
+ if (parent.L === p) parent.L = q;
+ else parent.R = q;
+ } else {
+ tree._ = q;
+ }
+
+ q.U = parent;
+ p.U = q;
+ p.R = q.L;
+ if (p.R) p.R.U = p;
+ q.L = p;
+}
+
+function RedBlackRotateRight(tree, node) {
+ var p = node,
+ q = node.L,
+ parent = p.U;
+
+ if (parent) {
+ if (parent.L === p) parent.L = q;
+ else parent.R = q;
+ } else {
+ tree._ = q;
+ }
+
+ q.U = parent;
+ p.U = q;
+ p.L = q.R;
+ if (p.L) p.L.U = p;
+ q.R = p;
+}
+
+function RedBlackFirst(node) {
+ while (node.L) node = node.L;
+ return node;
+}
+
+function createEdge(left, right, v0, v1) {
+ var edge = [null, null],
+ index = edges.push(edge) - 1;
+ edge.left = left;
+ edge.right = right;
+ if (v0) setEdgeEnd(edge, left, right, v0);
+ if (v1) setEdgeEnd(edge, right, left, v1);
+ cells[left.index].halfedges.push(index);
+ cells[right.index].halfedges.push(index);
+ return edge;
+}
+
+function createBorderEdge(left, v0, v1) {
+ var edge = [v0, v1];
+ edge.left = left;
+ return edge;
+}
+
+function setEdgeEnd(edge, left, right, vertex) {
+ if (!edge[0] && !edge[1]) {
+ edge[0] = vertex;
+ edge.left = left;
+ edge.right = right;
+ } else if (edge.left === right) {
+ edge[1] = vertex;
+ } else {
+ edge[0] = vertex;
+ }
+}
+
+// Liang–Barsky line clipping.
+function clipEdge(edge, x0, y0, x1, y1) {
+ var a = edge[0],
+ b = edge[1],
+ ax = a[0],
+ ay = a[1],
+ bx = b[0],
+ by = b[1],
+ 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) && !(t1 < 1)) return true; // TODO Better check?
+
+ if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy];
+ if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy];
+ return true;
+}
+
+function connectEdge(edge, x0, y0, x1, y1) {
+ var v1 = edge[1];
+ if (v1) return true;
+
+ var v0 = edge[0],
+ left = edge.left,
+ right = edge.right,
+ lx = left[0],
+ ly = left[1],
+ rx = right[0],
+ ry = right[1],
+ fx = (lx + rx) / 2,
+ fy = (ly + ry) / 2,
+ fm,
+ fb;
+
+ if (ry === ly) {
+ if (fx < x0 || fx >= x1) return;
+ if (lx > rx) {
+ if (!v0) v0 = [fx, y0];
+ else if (v0[1] >= y1) return;
+ v1 = [fx, y1];
+ } else {
+ if (!v0) v0 = [fx, y1];
+ else if (v0[1] < y0) return;
+ v1 = [fx, y0];
+ }
+ } else {
+ fm = (lx - rx) / (ry - ly);
+ fb = fy - fm * fx;
+ if (fm < -1 || fm > 1) {
+ if (lx > rx) {
+ if (!v0) v0 = [(y0 - fb) / fm, y0];
+ else if (v0[1] >= y1) return;
+ v1 = [(y1 - fb) / fm, y1];
+ } else {
+ if (!v0) v0 = [(y1 - fb) / fm, y1];
+ else if (v0[1] < y0) return;
+ v1 = [(y0 - fb) / fm, y0];
+ }
+ } else {
+ if (ly < ry) {
+ if (!v0) v0 = [x0, fm * x0 + fb];
+ else if (v0[0] >= x1) return;
+ v1 = [x1, fm * x1 + fb];
+ } else {
+ if (!v0) v0 = [x1, fm * x1 + fb];
+ else if (v0[0] < x0) return;
+ v1 = [x0, fm * x0 + fb];
+ }
+ }
+ }
+
+ edge[0] = v0;
+ edge[1] = v1;
+ return true;
+}
+
+function clipEdges(x0, y0, x1, y1) {
+ var i = edges.length,
+ edge;
+
+ while (i--) {
+ if (!connectEdge(edge = edges[i], x0, y0, x1, y1)
+ || !clipEdge(edge, x0, y0, x1, y1)
+ || !(Math.abs(edge[0][0] - edge[1][0]) > epsilon$4
+ || Math.abs(edge[0][1] - edge[1][1]) > epsilon$4)) {
+ delete edges[i];
+ }
+ }
+}
+
+function createCell(site) {
+ return cells[site.index] = {
+ site: site,
+ halfedges: []
+ };
+}
+
+function cellHalfedgeAngle(cell, edge) {
+ var site = cell.site,
+ va = edge.left,
+ vb = edge.right;
+ if (site === vb) vb = va, va = site;
+ if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]);
+ if (site === va) va = edge[1], vb = edge[0];
+ else va = edge[0], vb = edge[1];
+ return Math.atan2(va[0] - vb[0], vb[1] - va[1]);
+}
+
+function cellHalfedgeStart(cell, edge) {
+ return edge[+(edge.left !== cell.site)];
+}
+
+function cellHalfedgeEnd(cell, edge) {
+ return edge[+(edge.left === cell.site)];
+}
+
+function sortCellHalfedges() {
+ for (var i = 0, n = cells.length, cell, halfedges, j, m; i < n; ++i) {
+ if ((cell = cells[i]) && (m = (halfedges = cell.halfedges).length)) {
+ var index = new Array(m),
+ array = new Array(m);
+ for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, edges[halfedges[j]]);
+ index.sort(function(i, j) { return array[j] - array[i]; });
+ for (j = 0; j < m; ++j) array[j] = halfedges[index[j]];
+ for (j = 0; j < m; ++j) halfedges[j] = array[j];
+ }
+ }
+}
+
+function clipCells(x0, y0, x1, y1) {
+ var nCells = cells.length,
+ iCell,
+ cell,
+ site,
+ iHalfedge,
+ halfedges,
+ nHalfedges,
+ start,
+ startX,
+ startY,
+ end,
+ endX,
+ endY,
+ cover = true;
+
+ for (iCell = 0; iCell < nCells; ++iCell) {
+ if (cell = cells[iCell]) {
+ site = cell.site;
+ halfedges = cell.halfedges;
+ iHalfedge = halfedges.length;
+
+ // Remove any dangling clipped edges.
+ while (iHalfedge--) {
+ if (!edges[halfedges[iHalfedge]]) {
+ halfedges.splice(iHalfedge, 1);
+ }
+ }
+
+ // Insert any border edges as necessary.
+ iHalfedge = 0, nHalfedges = halfedges.length;
+ while (iHalfedge < nHalfedges) {
+ end = cellHalfedgeEnd(cell, edges[halfedges[iHalfedge]]), endX = end[0], endY = end[1];
+ start = cellHalfedgeStart(cell, edges[halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1];
+ if (Math.abs(endX - startX) > epsilon$4 || Math.abs(endY - startY) > epsilon$4) {
+ halfedges.splice(iHalfedge, 0, edges.push(createBorderEdge(site, end,
+ Math.abs(endX - x0) < epsilon$4 && y1 - endY > epsilon$4 ? [x0, Math.abs(startX - x0) < epsilon$4 ? startY : y1]
+ : Math.abs(endY - y1) < epsilon$4 && x1 - endX > epsilon$4 ? [Math.abs(startY - y1) < epsilon$4 ? startX : x1, y1]
+ : Math.abs(endX - x1) < epsilon$4 && endY - y0 > epsilon$4 ? [x1, Math.abs(startX - x1) < epsilon$4 ? startY : y0]
+ : Math.abs(endY - y0) < epsilon$4 && endX - x0 > epsilon$4 ? [Math.abs(startY - y0) < epsilon$4 ? startX : x0, y0]
+ : null)) - 1);
+ ++nHalfedges;
+ }
+ }
+
+ if (nHalfedges) cover = false;
+ }
+ }
+
+ // If there weren’t any edges, have the closest site cover the extent.
+ // It doesn’t matter which corner of the extent we measure!
+ if (cover) {
+ var dx, dy, d2, dc = Infinity;
+
+ for (iCell = 0, cover = null; iCell < nCells; ++iCell) {
+ if (cell = cells[iCell]) {
+ site = cell.site;
+ dx = site[0] - x0;
+ dy = site[1] - y0;
+ d2 = dx * dx + dy * dy;
+ if (d2 < dc) dc = d2, cover = cell;
+ }
+ }
+
+ if (cover) {
+ var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0];
+ cover.halfedges.push(
+ edges.push(createBorderEdge(site = cover.site, v00, v01)) - 1,
+ edges.push(createBorderEdge(site, v01, v11)) - 1,
+ edges.push(createBorderEdge(site, v11, v10)) - 1,
+ edges.push(createBorderEdge(site, v10, v00)) - 1
+ );
+ }
+ }
+
+ // Lastly delete any cells with no edges; these were entirely clipped.
+ for (iCell = 0; iCell < nCells; ++iCell) {
+ if (cell = cells[iCell]) {
+ if (!cell.halfedges.length) {
+ delete cells[iCell];
+ }
+ }
+ }
+}
+
+var circlePool = [];
+
+var firstCircle;
+
+function Circle() {
+ RedBlackNode(this);
+ this.x =
+ this.y =
+ this.arc =
+ this.site =
+ this.cy = null;
+}
+
+function attachCircle(arc) {
+ var lArc = arc.P,
+ rArc = arc.N;
+
+ if (!lArc || !rArc) return;
+
+ var lSite = lArc.site,
+ cSite = arc.site,
+ rSite = rArc.site;
+
+ if (lSite === rSite) return;
+
+ var bx = cSite[0],
+ by = cSite[1],
+ ax = lSite[0] - bx,
+ ay = lSite[1] - by,
+ cx = rSite[0] - bx,
+ cy = rSite[1] - by;
+
+ var d = 2 * (ax * cy - ay * cx);
+ if (d >= -epsilon2$2) return;
+
+ var ha = ax * ax + ay * ay,
+ hc = cx * cx + cy * cy,
+ x = (cy * ha - ay * hc) / d,
+ y = (ax * hc - cx * ha) / d;
+
+ var circle = circlePool.pop() || new Circle;
+ circle.arc = arc;
+ circle.site = cSite;
+ circle.x = x + bx;
+ circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom
+
+ arc.circle = circle;
+
+ var before = null,
+ node = circles._;
+
+ while (node) {
+ if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) {
+ if (node.L) node = node.L;
+ else { before = node.P; break; }
+ } else {
+ if (node.R) node = node.R;
+ else { before = node; break; }
+ }
+ }
+
+ circles.insert(before, circle);
+ if (!before) firstCircle = circle;
+}
+
+function detachCircle(arc) {
+ var circle = arc.circle;
+ if (circle) {
+ if (!circle.P) firstCircle = circle.N;
+ circles.remove(circle);
+ circlePool.push(circle);
+ RedBlackNode(circle);
+ arc.circle = null;
+ }
+}
+
+var beachPool = [];
+
+function Beach() {
+ RedBlackNode(this);
+ this.edge =
+ this.site =
+ this.circle = null;
+}
+
+function createBeach(site) {
+ var beach = beachPool.pop() || new Beach;
+ beach.site = site;
+ return beach;
+}
+
+function detachBeach(beach) {
+ detachCircle(beach);
+ beaches.remove(beach);
+ beachPool.push(beach);
+ RedBlackNode(beach);
+}
+
+function removeBeach(beach) {
+ var circle = beach.circle,
+ x = circle.x,
+ y = circle.cy,
+ vertex = [x, y],
+ previous = beach.P,
+ next = beach.N,
+ disappearing = [beach];
+
+ detachBeach(beach);
+
+ var lArc = previous;
+ while (lArc.circle
+ && Math.abs(x - lArc.circle.x) < epsilon$4
+ && Math.abs(y - lArc.circle.cy) < epsilon$4) {
+ previous = lArc.P;
+ disappearing.unshift(lArc);
+ detachBeach(lArc);
+ lArc = previous;
+ }
+
+ disappearing.unshift(lArc);
+ detachCircle(lArc);
+
+ var rArc = next;
+ while (rArc.circle
+ && Math.abs(x - rArc.circle.x) < epsilon$4
+ && Math.abs(y - rArc.circle.cy) < epsilon$4) {
+ next = rArc.N;
+ disappearing.push(rArc);
+ detachBeach(rArc);
+ rArc = next;
+ }
+
+ disappearing.push(rArc);
+ detachCircle(rArc);
+
+ var nArcs = disappearing.length,
+ iArc;
+ for (iArc = 1; iArc < nArcs; ++iArc) {
+ rArc = disappearing[iArc];
+ lArc = disappearing[iArc - 1];
+ setEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
+ }
+
+ lArc = disappearing[0];
+ rArc = disappearing[nArcs - 1];
+ rArc.edge = createEdge(lArc.site, rArc.site, null, vertex);
+
+ attachCircle(lArc);
+ attachCircle(rArc);
+}
+
+function addBeach(site) {
+ var x = site[0],
+ directrix = site[1],
+ lArc,
+ rArc,
+ dxl,
+ dxr,
+ node = beaches._;
+
+ while (node) {
+ dxl = leftBreakPoint(node, directrix) - x;
+ if (dxl > epsilon$4) node = node.L; else {
+ dxr = x - rightBreakPoint(node, directrix);
+ if (dxr > epsilon$4) {
+ if (!node.R) {
+ lArc = node;
+ break;
+ }
+ node = node.R;
+ } else {
+ if (dxl > -epsilon$4) {
+ lArc = node.P;
+ rArc = node;
+ } else if (dxr > -epsilon$4) {
+ lArc = node;
+ rArc = node.N;
+ } else {
+ lArc = rArc = node;
+ }
+ break;
+ }
+ }
+ }
+
+ createCell(site);
+ var newArc = createBeach(site);
+ beaches.insert(lArc, newArc);
+
+ if (!lArc && !rArc) return;
+
+ if (lArc === rArc) {
+ detachCircle(lArc);
+ rArc = createBeach(lArc.site);
+ beaches.insert(newArc, rArc);
+ newArc.edge = rArc.edge = createEdge(lArc.site, newArc.site);
+ attachCircle(lArc);
+ attachCircle(rArc);
+ return;
+ }
+
+ if (!rArc) { // && lArc
+ newArc.edge = createEdge(lArc.site, newArc.site);
+ return;
+ }
+
+ // else lArc !== rArc
+ detachCircle(lArc);
+ detachCircle(rArc);
+
+ var lSite = lArc.site,
+ ax = lSite[0],
+ ay = lSite[1],
+ bx = site[0] - ax,
+ by = site[1] - ay,
+ rSite = rArc.site,
+ cx = rSite[0] - ax,
+ cy = rSite[1] - ay,
+ d = 2 * (bx * cy - by * cx),
+ hb = bx * bx + by * by,
+ hc = cx * cx + cy * cy,
+ vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay];
+
+ setEdgeEnd(rArc.edge, lSite, rSite, vertex);
+ newArc.edge = createEdge(lSite, site, null, vertex);
+ rArc.edge = createEdge(site, rSite, null, vertex);
+ attachCircle(lArc);
+ attachCircle(rArc);
+}
+
+function leftBreakPoint(arc, directrix) {
+ var site = arc.site,
+ rfocx = site[0],
+ rfocy = site[1],
+ pby2 = rfocy - directrix;
+
+ if (!pby2) return rfocx;
+
+ var lArc = arc.P;
+ if (!lArc) return -Infinity;
+
+ site = lArc.site;
+ var lfocx = site[0],
+ lfocy = site[1],
+ plby2 = lfocy - directrix;
+
+ if (!plby2) return lfocx;
+
+ var hl = lfocx - rfocx,
+ aby2 = 1 / pby2 - 1 / plby2,
+ b = hl / plby2;
+
+ if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
+
+ return (rfocx + lfocx) / 2;
+}
+
+function rightBreakPoint(arc, directrix) {
+ var rArc = arc.N;
+ if (rArc) return leftBreakPoint(rArc, directrix);
+ var site = arc.site;
+ return site[1] === directrix ? site[0] : Infinity;
+}
+
+var epsilon$4 = 1e-6;
+var epsilon2$2 = 1e-12;
+var beaches;
+var cells;
+var circles;
+var edges;
+
+function triangleArea(a, b, c) {
+ return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]);
+}
+
+function lexicographic(a, b) {
+ return b[1] - a[1]
+ || b[0] - a[0];
+}
+
+function Diagram(sites, extent) {
+ var site = sites.sort(lexicographic).pop(),
+ x,
+ y,
+ circle;
+
+ edges = [];
+ cells = new Array(sites.length);
+ beaches = new RedBlackTree;
+ circles = new RedBlackTree;
+
+ while (true) {
+ circle = firstCircle;
+ if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) {
+ if (site[0] !== x || site[1] !== y) {
+ addBeach(site);
+ x = site[0], y = site[1];
+ }
+ site = sites.pop();
+ } else if (circle) {
+ removeBeach(circle.arc);
+ } else {
+ break;
+ }
+ }
+
+ sortCellHalfedges();
+
+ if (extent) {
+ var x0 = +extent[0][0],
+ y0 = +extent[0][1],
+ x1 = +extent[1][0],
+ y1 = +extent[1][1];
+ clipEdges(x0, y0, x1, y1);
+ clipCells(x0, y0, x1, y1);
+ }
+
+ this.edges = edges;
+ this.cells = cells;
+
+ beaches =
+ circles =
+ edges =
+ cells = null;
+}
+
+Diagram.prototype = {
+ constructor: Diagram,
+
+ polygons: function() {
+ var edges = this.edges;
+
+ return this.cells.map(function(cell) {
+ var polygon = cell.halfedges.map(function(i) { return cellHalfedgeStart(cell, edges[i]); });
+ polygon.data = cell.site.data;
+ return polygon;
+ });
+ },
+
+ triangles: function() {
+ var triangles = [],
+ edges = this.edges;
+
+ this.cells.forEach(function(cell, i) {
+ if (!(m = (halfedges = cell.halfedges).length)) return;
+ var site = cell.site,
+ halfedges,
+ j = -1,
+ m,
+ s0,
+ e1 = edges[halfedges[m - 1]],
+ s1 = e1.left === site ? e1.right : e1.left;
+
+ while (++j < m) {
+ s0 = s1;
+ e1 = edges[halfedges[j]];
+ s1 = e1.left === site ? e1.right : e1.left;
+ if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) {
+ triangles.push([site.data, s0.data, s1.data]);
+ }
+ }
+ });
+
+ return triangles;
+ },
+
+ links: function() {
+ return this.edges.filter(function(edge) {
+ return edge.right;
+ }).map(function(edge) {
+ return {
+ source: edge.left.data,
+ target: edge.right.data
+ };
+ });
+ },
+
+ find: function(x, y, radius) {
+ var that = this, i0, i1 = that._found || 0, n = that.cells.length, cell;
+
+ // Use the previously-found cell, or start with an arbitrary one.
+ while (!(cell = that.cells[i1])) if (++i1 >= n) return null;
+ var dx = x - cell.site[0], dy = y - cell.site[1], d2 = dx * dx + dy * dy;
+
+ // Traverse the half-edges to find a closer cell, if any.
+ do {
+ cell = that.cells[i0 = i1], i1 = null;
+ cell.halfedges.forEach(function(e) {
+ var edge = that.edges[e], v = edge.left;
+ if ((v === cell.site || !v) && !(v = edge.right)) return;
+ var vx = x - v[0], vy = y - v[1], v2 = vx * vx + vy * vy;
+ if (v2 < d2) d2 = v2, i1 = v.index;
+ });
+ } while (i1 !== null);
+
+ that._found = i0;
+
+ return radius == null || d2 <= radius * radius ? cell.site : null;
+ }
+};
+
+var voronoi = function() {
+ var x = x$4,
+ y = y$4,
+ extent = null;
+
+ function voronoi(data) {
+ return new Diagram(data.map(function(d, i) {
+ var s = [Math.round(x(d, i, data) / epsilon$4) * epsilon$4, Math.round(y(d, i, data) / epsilon$4) * epsilon$4];
+ s.index = i;
+ s.data = d;
+ return s;
+ }), extent);
+ }
+
+ voronoi.polygons = function(data) {
+ return voronoi(data).polygons();
+ };
+
+ voronoi.links = function(data) {
+ return voronoi(data).links();
+ };
+
+ voronoi.triangles = function(data) {
+ return voronoi(data).triangles();
+ };
+
+ voronoi.x = function(_) {
+ return arguments.length ? (x = typeof _ === "function" ? _ : constant$11(+_), voronoi) : x;
+ };
+
+ voronoi.y = function(_) {
+ return arguments.length ? (y = typeof _ === "function" ? _ : constant$11(+_), voronoi) : y;
+ };
+
+ voronoi.extent = function(_) {
+ return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]];
+ };
+
+ voronoi.size = function(_) {
+ return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]];
+ };
+
+ return voronoi;
+};
+
+var constant$12 = function(x) {
+ return function() {
+ return x;
+ };
+};
+
+function ZoomEvent(target, type, transform) {
+ this.target = target;
+ this.type = type;
+ this.transform = transform;
+}
+
+function Transform(k, x, y) {
+ this.k = k;
+ this.x = x;
+ this.y = y;
+}
+
+Transform.prototype = {
+ constructor: Transform,
+ scale: function(k) {
+ return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
+ },
+ translate: function(x, y) {
+ return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
+ },
+ apply: function(point) {
+ return [point[0] * this.k + this.x, point[1] * this.k + this.y];
+ },
+ applyX: function(x) {
+ return x * this.k + this.x;
+ },
+ applyY: function(y) {
+ return y * this.k + this.y;
+ },
+ invert: function(location) {
+ return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
+ },
+ invertX: function(x) {
+ return (x - this.x) / this.k;
+ },
+ invertY: function(y) {
+ return (y - this.y) / this.k;
+ },
+ rescaleX: function(x) {
+ return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
+ },
+ rescaleY: function(y) {
+ return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
+ },
+ toString: function() {
+ return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
+ }
+};
+
+var identity$8 = new Transform(1, 0, 0);
+
+transform$1.prototype = Transform.prototype;
+
+function transform$1(node) {
+ return node.__zoom || identity$8;
+}
+
+function nopropagation$2() {
+ event.stopImmediatePropagation();
+}
+
+var noevent$2 = function() {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+};
+
+// Ignore right-click, since that should open the context menu.
+function defaultFilter$2() {
+ return !event.button;
+}
+
+function defaultExtent$1() {
+ var e = this, w, h;
+ if (e instanceof SVGElement) {
+ e = e.ownerSVGElement || e;
+ w = e.width.baseVal.value;
+ h = e.height.baseVal.value;
+ } else {
+ w = e.clientWidth;
+ h = e.clientHeight;
+ }
+ return [[0, 0], [w, h]];
+}
+
+function defaultTransform() {
+ return this.__zoom || identity$8;
+}
+
+var zoom = function() {
+ var filter = defaultFilter$2,
+ extent = defaultExtent$1,
+ k0 = 0,
+ k1 = Infinity,
+ x0 = -k1,
+ x1 = k1,
+ y0 = x0,
+ y1 = x1,
+ duration = 250,
+ interpolate = interpolateZoom,
+ gestures = [],
+ listeners = dispatch("start", "zoom", "end"),
+ touchstarting,
+ touchending,
+ touchDelay = 500,
+ wheelDelay = 150,
+ clickDistance2 = 0;
+
+ function zoom(selection$$1) {
+ selection$$1
+ .on("wheel.zoom", wheeled)
+ .on("mousedown.zoom", mousedowned)
+ .on("dblclick.zoom", dblclicked)
+ .on("touchstart.zoom", touchstarted)
+ .on("touchmove.zoom", touchmoved)
+ .on("touchend.zoom touchcancel.zoom", touchended)
+ .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
+ .property("__zoom", defaultTransform);
+ }
+
+ zoom.transform = function(collection, transform$$1) {
+ var selection$$1 = collection.selection ? collection.selection() : collection;
+ selection$$1.property("__zoom", defaultTransform);
+ if (collection !== selection$$1) {
+ schedule(collection, transform$$1);
+ } else {
+ selection$$1.interrupt().each(function() {
+ gesture(this, arguments)
+ .start()
+ .zoom(null, typeof transform$$1 === "function" ? transform$$1.apply(this, arguments) : transform$$1)
+ .end();
+ });
+ }
+ };
+
+ zoom.scaleBy = function(selection$$1, k) {
+ zoom.scaleTo(selection$$1, function() {
+ var k0 = this.__zoom.k,
+ k1 = typeof k === "function" ? k.apply(this, arguments) : k;
+ return k0 * k1;
+ });
+ };
+
+ zoom.scaleTo = function(selection$$1, k) {
+ zoom.transform(selection$$1, function() {
+ var e = extent.apply(this, arguments),
+ t0 = this.__zoom,
+ p0 = centroid(e),
+ p1 = t0.invert(p0),
+ k1 = typeof k === "function" ? k.apply(this, arguments) : k;
+ return constrain(translate(scale(t0, k1), p0, p1), e);
+ });
+ };
+
+ zoom.translateBy = function(selection$$1, x, y) {
+ zoom.transform(selection$$1, function() {
+ return constrain(this.__zoom.translate(
+ typeof x === "function" ? x.apply(this, arguments) : x,
+ typeof y === "function" ? y.apply(this, arguments) : y
+ ), extent.apply(this, arguments));
+ });
+ };
+
+ function scale(transform$$1, k) {
+ k = Math.max(k0, Math.min(k1, k));
+ return k === transform$$1.k ? transform$$1 : new Transform(k, transform$$1.x, transform$$1.y);
+ }
+
+ function translate(transform$$1, p0, p1) {
+ var x = p0[0] - p1[0] * transform$$1.k, y = p0[1] - p1[1] * transform$$1.k;
+ return x === transform$$1.x && y === transform$$1.y ? transform$$1 : new Transform(transform$$1.k, x, y);
+ }
+
+ function constrain(transform$$1, extent) {
+ var dx0 = transform$$1.invertX(extent[0][0]) - x0,
+ dx1 = transform$$1.invertX(extent[1][0]) - x1,
+ dy0 = transform$$1.invertY(extent[0][1]) - y0,
+ dy1 = transform$$1.invertY(extent[1][1]) - y1;
+ return transform$$1.translate(
+ dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
+ dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
+ );
+ }
+
+ function centroid(extent) {
+ return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
+ }
+
+ function schedule(transition$$1, transform$$1, center) {
+ transition$$1
+ .on("start.zoom", function() { gesture(this, arguments).start(); })
+ .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); })
+ .tween("zoom", function() {
+ var that = this,
+ args = arguments,
+ g = gesture(that, args),
+ e = extent.apply(that, args),
+ p = center || centroid(e),
+ w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
+ a = that.__zoom,
+ b = typeof transform$$1 === "function" ? transform$$1.apply(that, args) : transform$$1,
+ i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
+ return function(t) {
+ if (t === 1) t = b; // Avoid rounding error on end.
+ else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
+ g.zoom(null, t);
+ };
+ });
+ }
+
+ function gesture(that, args) {
+ for (var i = 0, n = gestures.length, g; i < n; ++i) {
+ if ((g = gestures[i]).that === that) {
+ return g;
+ }
+ }
+ return new Gesture(that, args);
+ }
+
+ function Gesture(that, args) {
+ this.that = that;
+ this.args = args;
+ this.index = -1;
+ this.active = 0;
+ this.extent = extent.apply(that, args);
+ }
+
+ Gesture.prototype = {
+ start: function() {
+ if (++this.active === 1) {
+ this.index = gestures.push(this) - 1;
+ this.emit("start");
+ }
+ return this;
+ },
+ zoom: function(key, transform$$1) {
+ if (this.mouse && key !== "mouse") this.mouse[1] = transform$$1.invert(this.mouse[0]);
+ if (this.touch0 && key !== "touch") this.touch0[1] = transform$$1.invert(this.touch0[0]);
+ if (this.touch1 && key !== "touch") this.touch1[1] = transform$$1.invert(this.touch1[0]);
+ this.that.__zoom = transform$$1;
+ this.emit("zoom");
+ return this;
+ },
+ end: function() {
+ if (--this.active === 0) {
+ gestures.splice(this.index, 1);
+ this.index = -1;
+ this.emit("end");
+ }
+ return this;
+ },
+ emit: function(type) {
+ customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
+ }
+ };
+
+ function wheeled() {
+ if (!filter.apply(this, arguments)) return;
+ var g = gesture(this, arguments),
+ t = this.__zoom,
+ k = Math.max(k0, Math.min(k1, t.k * Math.pow(2, -event.deltaY * (event.deltaMode ? 120 : 1) / 500))),
+ p = mouse(this);
+
+ // If the mouse is in the same location as before, reuse it.
+ // If there were recent wheel events, reset the wheel idle timeout.
+ if (g.wheel) {
+ if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
+ g.mouse[1] = t.invert(g.mouse[0] = p);
+ }
+ clearTimeout(g.wheel);
+ }
+
+ // If this wheel event won’t trigger a transform change, ignore it.
+ else if (t.k === k) return;
+
+ // Otherwise, capture the mouse point and location at the start.
+ else {
+ g.mouse = [p, t.invert(p)];
+ interrupt(this);
+ g.start();
+ }
+
+ noevent$2();
+ g.wheel = setTimeout(wheelidled, wheelDelay);
+ g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent));
+
+ function wheelidled() {
+ g.wheel = null;
+ g.end();
+ }
+ }
+
+ function mousedowned() {
+ if (touchending || !filter.apply(this, arguments)) return;
+ var g = gesture(this, arguments),
+ v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
+ p = mouse(this),
+ x0 = event.clientX,
+ y0 = event.clientY;
+
+ dragDisable(event.view);
+ nopropagation$2();
+ g.mouse = [p, this.__zoom.invert(p)];
+ interrupt(this);
+ g.start();
+
+ function mousemoved() {
+ noevent$2();
+ if (!g.moved) {
+ var dx = event.clientX - x0, dy = event.clientY - y0;
+ g.moved = dx * dx + dy * dy > clickDistance2;
+ }
+ g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent));
+ }
+
+ function mouseupped() {
+ v.on("mousemove.zoom mouseup.zoom", null);
+ yesdrag(event.view, g.moved);
+ noevent$2();
+ g.end();
+ }
+ }
+
+ function dblclicked() {
+ if (!filter.apply(this, arguments)) return;
+ var t0 = this.__zoom,
+ p0 = mouse(this),
+ p1 = t0.invert(p0),
+ k1 = t0.k * (event.shiftKey ? 0.5 : 2),
+ t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments));
+
+ noevent$2();
+ if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0);
+ else select(this).call(zoom.transform, t1);
+ }
+
+ function touchstarted() {
+ if (!filter.apply(this, arguments)) return;
+ var g = gesture(this, arguments),
+ touches$$1 = event.changedTouches,
+ started,
+ n = touches$$1.length, i, t, p;
+
+ nopropagation$2();
+ for (i = 0; i < n; ++i) {
+ t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
+ p = [p, this.__zoom.invert(p), t.identifier];
+ if (!g.touch0) g.touch0 = p, started = true;
+ else if (!g.touch1) g.touch1 = p;
+ }
+
+ // If this is a dbltap, reroute to the (optional) dblclick.zoom handler.
+ if (touchstarting) {
+ touchstarting = clearTimeout(touchstarting);
+ if (!g.touch1) {
+ g.end();
+ p = select(this).on("dblclick.zoom");
+ if (p) p.apply(this, arguments);
+ return;
+ }
+ }
+
+ if (started) {
+ touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);
+ interrupt(this);
+ g.start();
+ }
+ }
+
+ function touchmoved() {
+ var g = gesture(this, arguments),
+ touches$$1 = event.changedTouches,
+ n = touches$$1.length, i, t, p, l;
+
+ noevent$2();
+ if (touchstarting) touchstarting = clearTimeout(touchstarting);
+ for (i = 0; i < n; ++i) {
+ t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
+ if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
+ else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
+ }
+ t = g.that.__zoom;
+ if (g.touch1) {
+ var p0 = g.touch0[0], l0 = g.touch0[1],
+ p1 = g.touch1[0], l1 = g.touch1[1],
+ dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
+ dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
+ t = scale(t, Math.sqrt(dp / dl));
+ p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
+ l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
+ }
+ else if (g.touch0) p = g.touch0[0], l = g.touch0[1];
+ else return;
+ g.zoom("touch", constrain(translate(t, p, l), g.extent));
+ }
+
+ function touchended() {
+ var g = gesture(this, arguments),
+ touches$$1 = event.changedTouches,
+ n = touches$$1.length, i, t;
+
+ nopropagation$2();
+ if (touchending) clearTimeout(touchending);
+ touchending = setTimeout(function() { touchending = null; }, touchDelay);
+ for (i = 0; i < n; ++i) {
+ t = touches$$1[i];
+ if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;
+ else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
+ }
+ if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
+ if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);
+ else g.end();
+ }
+
+ zoom.filter = function(_) {
+ return arguments.length ? (filter = typeof _ === "function" ? _ : constant$12(!!_), zoom) : filter;
+ };
+
+ zoom.extent = function(_) {
+ return arguments.length ? (extent = typeof _ === "function" ? _ : constant$12([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
+ };
+
+ zoom.scaleExtent = function(_) {
+ return arguments.length ? (k0 = +_[0], k1 = +_[1], zoom) : [k0, k1];
+ };
+
+ zoom.translateExtent = function(_) {
+ return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], zoom) : [[x0, y0], [x1, y1]];
+ };
+
+ zoom.duration = function(_) {
+ return arguments.length ? (duration = +_, zoom) : duration;
+ };
+
+ zoom.interpolate = function(_) {
+ return arguments.length ? (interpolate = _, zoom) : interpolate;
+ };
+
+ zoom.on = function() {
+ var value = listeners.on.apply(listeners, arguments);
+ return value === listeners ? zoom : value;
+ };
+
+ zoom.clickDistance = function(_) {
+ return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
+ };
+
+ return zoom;
+};
+
+
+
+var index$2 = Object.freeze({
+ version: version,
+ bisect: bisectRight,
+ bisectRight: bisectRight,
+ bisectLeft: bisectLeft,
+ ascending: ascending,
+ bisector: bisector,
+ cross: cross,
+ descending: descending,
+ deviation: deviation,
+ extent: extent,
+ histogram: histogram,
+ thresholdFreedmanDiaconis: freedmanDiaconis,
+ thresholdScott: scott,
+ thresholdSturges: sturges,
+ max: max,
+ mean: mean,
+ median: median,
+ merge: merge,
+ min: min,
+ pairs: pairs,
+ permute: permute,
+ quantile: threshold,
+ range: sequence,
+ scan: scan,
+ shuffle: shuffle,
+ sum: sum,
+ ticks: ticks,
+ tickIncrement: tickIncrement,
+ tickStep: tickStep,
+ transpose: transpose,
+ variance: variance,
+ zip: zip,
+ axisTop: axisTop,
+ axisRight: axisRight,
+ axisBottom: axisBottom,
+ axisLeft: axisLeft,
+ brush: brush,
+ brushX: brushX,
+ brushY: brushY,
+ brushSelection: brushSelection,
+ chord: chord,
+ ribbon: ribbon,
+ nest: nest,
+ set: set$2,
+ map: map$1,
+ keys: keys,
+ values: values,
+ entries: entries,
+ color: color,
+ rgb: rgb,
+ hsl: hsl,
+ lab: lab,
+ hcl: hcl,
+ cubehelix: cubehelix,
+ dispatch: dispatch,
+ drag: drag,
+ dragDisable: dragDisable,
+ dragEnable: yesdrag,
+ dsvFormat: dsv,
+ csvParse: csvParse,
+ csvParseRows: csvParseRows,
+ csvFormat: csvFormat,
+ csvFormatRows: csvFormatRows,
+ tsvParse: tsvParse,
+ tsvParseRows: tsvParseRows,
+ tsvFormat: tsvFormat,
+ tsvFormatRows: tsvFormatRows,
+ easeLinear: linear$1,
+ easeQuad: quadInOut,
+ easeQuadIn: quadIn,
+ easeQuadOut: quadOut,
+ easeQuadInOut: quadInOut,
+ easeCubic: cubicInOut,
+ easeCubicIn: cubicIn,
+ easeCubicOut: cubicOut,
+ easeCubicInOut: cubicInOut,
+ easePoly: polyInOut,
+ easePolyIn: polyIn,
+ easePolyOut: polyOut,
+ easePolyInOut: polyInOut,
+ easeSin: sinInOut,
+ easeSinIn: sinIn,
+ easeSinOut: sinOut,
+ easeSinInOut: sinInOut,
+ easeExp: expInOut,
+ easeExpIn: expIn,
+ easeExpOut: expOut,
+ easeExpInOut: expInOut,
+ easeCircle: circleInOut,
+ easeCircleIn: circleIn,
+ easeCircleOut: circleOut,
+ easeCircleInOut: circleInOut,
+ easeBounce: bounceOut,
+ easeBounceIn: bounceIn,
+ easeBounceOut: bounceOut,
+ easeBounceInOut: bounceInOut,
+ easeBack: backInOut,
+ easeBackIn: backIn,
+ easeBackOut: backOut,
+ easeBackInOut: backInOut,
+ easeElastic: elasticOut,
+ easeElasticIn: elasticIn,
+ easeElasticOut: elasticOut,
+ easeElasticInOut: elasticInOut,
+ forceCenter: center$1,
+ forceCollide: collide,
+ forceLink: link,
+ forceManyBody: manyBody,
+ forceSimulation: simulation,
+ forceX: x$2,
+ forceY: y$2,
+ formatDefaultLocale: defaultLocale,
+ get format () { return format; },
+ get formatPrefix () { return formatPrefix; },
+ formatLocale: formatLocale,
+ formatSpecifier: formatSpecifier,
+ precisionFixed: precisionFixed,
+ precisionPrefix: precisionPrefix,
+ precisionRound: precisionRound,
+ geoArea: area,
+ geoBounds: bounds,
+ geoCentroid: d3GeoCentroid,
+ geoCircle: circle,
+ geoClipExtent: extent$1,
+ geoContains: contains,
+ geoDistance: distance,
+ geoGraticule: graticule,
+ geoGraticule10: graticule10,
+ geoInterpolate: interpolate$1,
+ geoLength: d3GeoLength,
+ geoPath: index$4,
+ geoAlbers: albers,
+ geoAlbersUsa: albersUsa,
+ geoAzimuthalEqualArea: azimuthalEqualArea,
+ geoAzimuthalEqualAreaRaw: azimuthalEqualAreaRaw,
+ geoAzimuthalEquidistant: azimuthalEquidistant,
+ geoAzimuthalEquidistantRaw: azimuthalEquidistantRaw,
+ geoConicConformal: conicConformal,
+ geoConicConformalRaw: conicConformalRaw,
+ geoConicEqualArea: conicEqualArea,
+ geoConicEqualAreaRaw: conicEqualAreaRaw,
+ geoConicEquidistant: conicEquidistant,
+ geoConicEquidistantRaw: conicEquidistantRaw,
+ geoEquirectangular: equirectangular,
+ geoEquirectangularRaw: equirectangularRaw,
+ geoGnomonic: gnomonic,
+ geoGnomonicRaw: gnomonicRaw,
+ geoIdentity: identity$5,
+ geoProjection: projection,
+ geoProjectionMutator: projectionMutator,
+ geoMercator: mercator,
+ geoMercatorRaw: mercatorRaw,
+ geoOrthographic: orthographic,
+ geoOrthographicRaw: orthographicRaw,
+ geoStereographic: stereographic,
+ geoStereographicRaw: stereographicRaw,
+ geoTransverseMercator: transverseMercator,
+ geoTransverseMercatorRaw: transverseMercatorRaw,
+ geoRotation: rotation,
+ geoStream: geoStream,
+ geoTransform: transform,
+ cluster: cluster,
+ hierarchy: hierarchy,
+ pack: index$5,
+ packSiblings: siblings,
+ packEnclose: enclose,
+ partition: partition,
+ stratify: stratify,
+ tree: tree,
+ treemap: index$6,
+ treemapBinary: binary,
+ treemapDice: treemapDice,
+ treemapSlice: treemapSlice,
+ treemapSliceDice: sliceDice,
+ treemapSquarify: squarify,
+ treemapResquarify: resquarify,
+ interpolate: interpolateValue,
+ interpolateArray: array$1,
+ interpolateBasis: basis$1,
+ interpolateBasisClosed: basisClosed,
+ interpolateDate: date,
+ interpolateNumber: reinterpolate,
+ interpolateObject: object,
+ interpolateRound: interpolateRound,
+ interpolateString: interpolateString,
+ interpolateTransformCss: interpolateTransformCss,
+ interpolateTransformSvg: interpolateTransformSvg,
+ interpolateZoom: interpolateZoom,
+ interpolateRgb: interpolateRgb,
+ interpolateRgbBasis: rgbBasis,
+ interpolateRgbBasisClosed: rgbBasisClosed,
+ interpolateHsl: hsl$2,
+ interpolateHslLong: hslLong,
+ interpolateLab: lab$1,
+ interpolateHcl: hcl$2,
+ interpolateHclLong: hclLong,
+ interpolateCubehelix: cubehelix$2,
+ interpolateCubehelixLong: cubehelixLong,
+ quantize: quantize,
+ path: path,
+ polygonArea: d3polygonArea,
+ polygonCentroid: d3polygonCentroid,
+ polygonHull: d3polygonHull,
+ polygonContains: contains$1,
+ polygonLength: length$1,
+ quadtree: quadtree,
+ queue: queue,
+ randomUniform: uniform,
+ randomNormal: normal,
+ randomLogNormal: logNormal,
+ randomBates: bates,
+ randomIrwinHall: irwinHall,
+ randomExponential: exponential$1,
+ request: request,
+ html: html,
+ json: json,
+ text: text,
+ xml: xml,
+ csv: csv$1,
+ tsv: tsv$1,
+ scaleBand: band,
+ scalePoint: point$1,
+ scaleIdentity: identity$6,
+ scaleLinear: linear$2,
+ scaleLog: log$1,
+ scaleOrdinal: ordinal,
+ scaleImplicit: implicit,
+ scalePow: pow$1,
+ scaleSqrt: sqrt$1,
+ scaleQuantile: quantile,
+ scaleQuantize: quantize$1,
+ scaleThreshold: threshold$1,
+ scaleTime: time,
+ scaleUtc: utcTime,
+ schemeCategory10: category10,
+ schemeCategory20b: category20b,
+ schemeCategory20c: category20c,
+ schemeCategory20: category20,
+ interpolateCubehelixDefault: cubehelix$3,
+ interpolateRainbow: rainbow$1,
+ interpolateWarm: warm,
+ interpolateCool: cool,
+ interpolateViridis: viridis,
+ interpolateMagma: magma,
+ interpolateInferno: inferno,
+ interpolatePlasma: plasma,
+ scaleSequential: sequential,
+ creator: creator,
+ local: local$1,
+ matcher: matcher$1,
+ mouse: mouse,
+ namespace: namespace,
+ namespaces: namespaces,
+ select: select,
+ selectAll: selectAll,
+ selection: selection,
+ selector: selector,
+ selectorAll: selectorAll,
+ style: styleValue,
+ touch: touch,
+ touches: touches,
+ window: defaultView,
+ get event () { return event; },
+ customEvent: customEvent,
+ arc: arc,
+ area: area$1,
+ line: line,
+ pie: pie,
+ radialArea: radialArea,
+ radialLine: radialLine$1,
+ linkHorizontal: linkHorizontal,
+ linkVertical: linkVertical,
+ linkRadial: linkRadial,
+ symbol: symbol,
+ symbols: symbols,
+ symbolCircle: circle$2,
+ symbolCross: cross$2,
+ symbolDiamond: diamond,
+ symbolSquare: square,
+ symbolStar: star,
+ symbolTriangle: triangle,
+ symbolWye: wye,
+ curveBasisClosed: basisClosed$1,
+ curveBasisOpen: basisOpen,
+ curveBasis: basis$2,
+ curveBundle: bundle,
+ curveCardinalClosed: cardinalClosed,
+ curveCardinalOpen: cardinalOpen,
+ curveCardinal: cardinal,
+ curveCatmullRomClosed: catmullRomClosed,
+ curveCatmullRomOpen: catmullRomOpen,
+ curveCatmullRom: catmullRom,
+ curveLinearClosed: linearClosed,
+ curveLinear: curveLinear,
+ curveMonotoneX: monotoneX,
+ curveMonotoneY: monotoneY,
+ curveNatural: natural,
+ curveStep: step,
+ curveStepAfter: stepAfter,
+ curveStepBefore: stepBefore,
+ stack: stack,
+ stackOffsetExpand: expand,
+ stackOffsetDiverging: diverging,
+ stackOffsetNone: none$1,
+ stackOffsetSilhouette: silhouette,
+ stackOffsetWiggle: wiggle,
+ stackOrderAscending: ascending$2,
+ stackOrderDescending: descending$2,
+ stackOrderInsideOut: insideOut,
+ stackOrderNone: none$2,
+ stackOrderReverse: reverse,
+ timeInterval: newInterval,
+ timeMillisecond: millisecond,
+ timeMilliseconds: milliseconds,
+ utcMillisecond: millisecond,
+ utcMilliseconds: milliseconds,
+ timeSecond: second,
+ timeSeconds: seconds,
+ utcSecond: second,
+ utcSeconds: seconds,
+ timeMinute: minute,
+ timeMinutes: minutes,
+ timeHour: hour,
+ timeHours: hours,
+ timeDay: day,
+ timeDays: days,
+ timeWeek: sunday,
+ timeWeeks: sundays,
+ timeSunday: sunday,
+ timeSundays: sundays,
+ timeMonday: monday,
+ timeMondays: mondays,
+ timeTuesday: tuesday,
+ timeTuesdays: tuesdays,
+ timeWednesday: wednesday,
+ timeWednesdays: wednesdays,
+ timeThursday: thursday,
+ timeThursdays: thursdays,
+ timeFriday: friday,
+ timeFridays: fridays,
+ timeSaturday: saturday,
+ timeSaturdays: saturdays,
+ timeMonth: month,
+ timeMonths: months,
+ timeYear: year,
+ timeYears: years,
+ utcMinute: utcMinute,
+ utcMinutes: utcMinutes,
+ utcHour: utcHour,
+ utcHours: utcHours,
+ utcDay: utcDay,
+ utcDays: utcDays,
+ utcWeek: utcSunday,
+ utcWeeks: utcSundays,
+ utcSunday: utcSunday,
+ utcSundays: utcSundays,
+ utcMonday: utcMonday,
+ utcMondays: utcMondays,
+ utcTuesday: utcTuesday,
+ utcTuesdays: utcTuesdays,
+ utcWednesday: utcWednesday,
+ utcWednesdays: utcWednesdays,
+ utcThursday: utcThursday,
+ utcThursdays: utcThursdays,
+ utcFriday: utcFriday,
+ utcFridays: utcFridays,
+ utcSaturday: utcSaturday,
+ utcSaturdays: utcSaturdays,
+ utcMonth: utcMonth,
+ utcMonths: utcMonths,
+ utcYear: utcYear,
+ utcYears: utcYears,
+ timeFormatDefaultLocale: defaultLocale$1,
+ get timeFormat () { return timeFormat; },
+ get timeParse () { return timeParse; },
+ get utcFormat () { return utcFormat; },
+ get utcParse () { return utcParse; },
+ timeFormatLocale: formatLocale$1,
+ isoFormat: formatIso,
+ isoParse: parseIso,
+ now: now,
+ timer: timer,
+ timerFlush: timerFlush,
+ timeout: timeout$1,
+ interval: interval$1,
+ transition: transition,
+ active: active,
+ interrupt: interrupt,
+ voronoi: voronoi,
+ zoom: zoom,
+ zoomTransform: transform$1,
+ zoomIdentity: identity$8
+});
+
+/*
+ Bypasses features of D3's default projection stream pipeline that are unnecessary:
+ * Antimeridian clipping
+ * Spherical rotation
+ * Resampling
+*/
+function geoRawMercator() {
+ var project = mercatorRaw,
+ k = 512 / Math.PI, // scale
+ x = 0, y = 0, // translate
+ clipExtent = [[0, 0], [0, 0]];
+
+
+ function projection$$1(point) {
+ point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
+ return [point[0] * k + x, y - point[1] * k];
+ }
+
+
+ projection$$1.invert = function(point) {
+ point = project.invert((point[0] - x) / k, (y - point[1]) / k);
+ return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
+ };
+
+
+ projection$$1.scale = function(_) {
+ if (!arguments.length) return k;
+ k = +_;
+ return projection$$1;
+ };
+
+
+ projection$$1.translate = function(_) {
+ if (!arguments.length) return [x, y];
+ x = +_[0];
+ y = +_[1];
+ return projection$$1;
+ };
+
+
+ projection$$1.clipExtent = function(_) {
+ if (!arguments.length) return clipExtent;
+ clipExtent = _;
+ return projection$$1;
+ };
+
+
+ projection$$1.transform = function(_) {
+ if (!arguments.length) return identity$8.translate(x, y).scale(k);
+ x = +_.x;
+ y = +_.y;
+ k = +_.k;
+ return projection$$1;
+ };
+
+
+ projection$$1.stream = transform({
+ point: function(x, y) {
+ x = projection$$1([x, y]);
+ this.stream.point(x[0], x[1]);
+ }
+ }).stream;
+
+
+ return projection$$1;
+}
+
+function osmChangeset() {
+ if (!(this instanceof osmChangeset)) {
+ return (new osmChangeset()).initialize(arguments);
+ } else if (arguments.length) {
+ this.initialize(arguments);
+ }
+}
+
+
+osmEntity$$1.changeset = osmChangeset;
+
+osmChangeset.prototype = Object.create(osmEntity$$1.prototype);
+
+lodash.extend(osmChangeset.prototype, {
+
+ type: 'changeset',
+
+
+ extent: function() {
+ return new geoExtent$$1();
+ },
+
+
+ geometry: function() {
+ return 'changeset';
+ },
+
+
+ asJXON: function() {
+ return {
+ osm: {
+ changeset: {
+ tag: lodash.map(this.tags, function(value, key) {
+ return { '@k': key, '@v': value };
+ }),
+ '@version': 0.6,
+ '@generator': 'iD'
+ }
+ }
+ };
+ },
+
+
+ // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
+ // XML. Returns a string.
+ osmChangeJXON: function(changes) {
+ var changeset_id = this.id;
+
+ function nest(x, order) {
+ var groups = {};
+ for (var i = 0; i < x.length; i++) {
+ var tagName = Object.keys(x[i])[0];
+ if (!groups[tagName]) groups[tagName] = [];
+ groups[tagName].push(x[i][tagName]);
+ }
+ var ordered = {};
+ order.forEach(function(o) {
+ if (groups[o]) ordered[o] = groups[o];
+ });
+ return ordered;
+ }
+
+
+ // sort relations in a changeset by dependencies
+ function sort(changes) {
+
+ // find a referenced relation in the current changeset
+ function resolve(item) {
+ return lodash.find(relations, function(relation) {
+ return item.keyAttributes.type === 'relation'
+ && item.keyAttributes.ref === relation['@id'];
+ });
+ }
+
+ // a new item is an item that has not been already processed
+ function isNew(item) {
+ return !sorted[ item['@id'] ] && !lodash.find(processing, function(proc) {
+ return proc['@id'] === item['@id'];
+ });
+ }
+
+ var processing = [],
+ sorted = {},
+ relations = changes.relation;
+
+ if (!relations) return changes;
+
+ for (var i = 0; i < relations.length; i++) {
+ var relation = relations[i];
+
+ // skip relation if already sorted
+ if (!sorted[relation['@id']]) {
+ processing.push(relation);
+ }
+
+ while (processing.length > 0) {
+ var next = processing[0],
+ deps = lodash.filter(lodash.compact(next.member.map(resolve)), isNew);
+ if (deps.length === 0) {
+ sorted[next['@id']] = next;
+ processing.shift();
+ } else {
+ processing = deps.concat(processing);
+ }
+ }
+ }
+
+ changes.relation = lodash.values(sorted);
+ return changes;
+ }
+
+ function rep(entity) {
+ return entity.asJXON(changeset_id);
+ }
+
+ return {
+ osmChange: {
+ '@version': 0.6,
+ '@generator': 'iD',
+ 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
+ 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
+ 'delete': lodash.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), { '@if-unused': true })
+ }
+ };
+ },
+
+
+ asGeoJSON: function() {
+ return {};
+ }
+
+});
+
+function osmNode() {
+ if (!(this instanceof osmNode)) {
+ return (new osmNode()).initialize(arguments);
+ } else if (arguments.length) {
+ this.initialize(arguments);
+ }
+}
+
+osmEntity$$1.node = osmNode;
+
+osmNode.prototype = Object.create(osmEntity$$1.prototype);
+
+lodash.extend(osmNode.prototype, {
+
+ type: 'node',
+
+
+ extent: function() {
+ return new geoExtent$$1(this.loc);
+ },
+
+
+ geometry: function(graph) {
+ return graph.transient(this, 'geometry', function() {
+ return graph.isPoi(this) ? 'point' : 'vertex';
+ });
+ },
+
+
+ move: function(loc) {
+ return this.update({loc: loc});
+ },
+
+
+ isDegenerate: function() {
+ return !(
+ Array.isArray(this.loc) && this.loc.length === 2 &&
+ this.loc[0] >= -180 && this.loc[0] <= 180 &&
+ this.loc[1] >= -90 && this.loc[1] <= 90
+ );
+ },
+
+
+ isEndpoint: function(resolver) {
+ return resolver.transient(this, 'isEndpoint', function() {
+ var id = this.id;
+ return resolver.parentWays(this).filter(function(parent) {
+ return !parent.isClosed() && !!parent.affix(id);
+ }).length > 0;
+ });
+ },
+
+
+ isConnected: function(resolver) {
+ return resolver.transient(this, 'isConnected', function() {
+ var parents = resolver.parentWays(this);
+
+ function isLine(entity) {
+ return entity.geometry(resolver) === 'line' &&
+ entity.hasInterestingTags();
+ }
+
+ // vertex is connected to multiple parent lines
+ if (parents.length > 1 && lodash.some(parents, isLine)) {
+ return true;
+
+ } else if (parents.length === 1) {
+ var way = parents[0],
+ nodes = way.nodes.slice();
+ if (way.isClosed()) { nodes.pop(); } // ignore connecting node if closed
+
+ // return true if vertex appears multiple times (way is self intersecting)
+ return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
+ }
+
+ return false;
+ });
+ },
+
+
+ isIntersection: function(resolver) {
+ return resolver.transient(this, 'isIntersection', function() {
+ return resolver.parentWays(this).filter(function(parent) {
+ return (parent.tags.highway ||
+ parent.tags.waterway ||
+ parent.tags.railway ||
+ parent.tags.aeroway) &&
+ parent.geometry(resolver) === 'line';
+ }).length > 1;
+ });
+ },
+
+
+ isHighwayIntersection: function(resolver) {
+ return resolver.transient(this, 'isHighwayIntersection', function() {
+ return resolver.parentWays(this).filter(function(parent) {
+ return parent.tags.highway && parent.geometry(resolver) === 'line';
+ }).length > 1;
+ });
+ },
+
+
+ isOnAddressLine: function(resolver) {
+ return resolver.transient(this, 'isOnAddressLine', function() {
+ return resolver.parentWays(this).filter(function(parent) {
+ return parent.tags.hasOwnProperty('addr:interpolation') &&
+ parent.geometry(resolver) === 'line';
+ }).length > 0;
+ });
+ },
+
+
+ asJXON: function(changeset_id) {
+ var r = {
+ node: {
+ '@id': this.osmId(),
+ '@lon': this.loc[0],
+ '@lat': this.loc[1],
+ '@version': (this.version || 0),
+ tag: lodash.map(this.tags, function(v, k) {
+ return { keyAttributes: { k: k, v: v } };
+ })
+ }
+ };
+ if (changeset_id) r.node['@changeset'] = changeset_id;
+ return r;
+ },
+
+
+ asGeoJSON: function() {
+ return {
+ type: 'Point',
+ coordinates: this.loc
+ };
+ }
+});
+
+/*
+ Order the nodes of a way in reverse order and reverse any direction dependent tags
+ other than `oneway`. (We assume that correcting a backwards oneway is the primary
+ reason for reversing a way.)
+
+ The following transforms are performed:
+
+ Keys:
+ *:right=* ⟺ *:left=*
+ *:forward=* ⟺ *:backward=*
+ direction=up ⟺ direction=down
+ incline=up ⟺ incline=down
+ *=right ⟺ *=left
+
+ Relation members:
+ role=forward ⟺ role=backward
+ role=north ⟺ role=south
+ role=east ⟺ role=west
+
+ In addition, numeric-valued `incline` tags are negated.
+
+ The JOSM implementation was used as a guide, but transformations that were of unclear benefit
+ or adjusted tags that don't seem to be used in practice were omitted.
+
+ Also, each node on the way is examined for its own tags and the following transformations are performed
+ in order to ensure associated nodes (eg a Stop Sign) is also reversed
+
+ Node Keys:
+ direction=forward ⟺ direction=backward
+ direction=left ⟺ direction=right
+ *:forward=* ⟺ *:backward=*
+ *:left=* ⟺ *:right=*
+
+ References:
+ http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
+ http://wiki.openstreetmap.org/wiki/Key:direction#Steps
+ http://wiki.openstreetmap.org/wiki/Key:incline
+ http://wiki.openstreetmap.org/wiki/Route#Members
+ http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
+ http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
+ http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
+ */
+function actionReverse(wayId, options) {
+ var replacements = [
+ [/:right$/, ':left'], [/:left$/, ':right'],
+ [/:forward$/, ':backward'], [/:backward$/, ':forward']
+ ],
+ numeric = /^([+\-]?)(?=[\d.])/,
+ roleReversals = {
+ forward: 'backward',
+ backward: 'forward',
+ north: 'south',
+ south: 'north',
+ east: 'west',
+ west: 'east'
+ };
+
+
+ function reverseKey(key) {
+ for (var i = 0; i < replacements.length; ++i) {
+ var replacement = replacements[i];
+ if (replacement[0].test(key)) {
+ return key.replace(replacement[0], replacement[1]);
+ }
+ }
+ return key;
+ }
+
+
+ function reverseValue(key, value) {
+ if (key === 'incline' && numeric.test(value)) {
+ return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
+ } else if (key === 'incline' || key === 'direction') {
+ return {up: 'down', down: 'up'}[value] || value;
+ } else if (options && options.reverseOneway && key === 'oneway') {
+ return {yes: '-1', '1': '-1', '-1': 'yes'}[value] || value;
+ } else {
+ return {left: 'right', right: 'left'}[value] || value;
+ }
+ }
+
+
+ function reverseDirectionTags(node) {
+ // Update the direction based tags as appropriate then return an updated node
+ return node.update({tags: lodash.transform(node.tags, function(acc, tagValue, tagKey) {
+ // See if this is a direction tag and reverse (or use existing value if not recognised)
+ if (tagKey === 'direction') {
+ acc[tagKey] = {forward: 'backward', backward: 'forward', left: 'right', right: 'left'}[tagValue] || tagValue;
+ } else {
+ // Use the reverseKey method to cater for situations such as traffic_sign:forward=stop
+ // This will pass through other tags unchanged
+ acc[reverseKey(tagKey)] = tagValue;
+ }
+ return acc;
+ }, {})});
+ }
+
+
+ function reverseTagsOnNodes(graph, nodeIds) {
+ // Reverse the direction of appropriate tags attached to the nodes (#3076)
+ return lodash(nodeIds)
+ // Get each node from the graph
+ .map(function(nodeId) { return graph.entity(nodeId);})
+ // Check tags on the node, if there aren't any, we can skip
+ .filter(function(existingNode) { return existingNode.tags !== undefined;})
+ // Get a new version of each node with the appropriate tags reversed
+ .map(function(existingNode) { return reverseDirectionTags(existingNode);})
+ // Chain together consecutive updates to the graph for each updated node and return
+ .reduce(function (accGraph, value) { return accGraph.replace(value); }, graph);
+ }
+
+
+ return function(graph) {
+ var way = graph.entity(wayId),
+ nodes = way.nodes.slice().reverse(),
+ tags = {}, key, role;
+
+ for (key in way.tags) {
+ tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
+ }
+
+ graph.parentRelations(way).forEach(function(relation) {
+ relation.members.forEach(function(member, index) {
+ if (member.id === way.id && (role = roleReversals[member.role])) {
+ relation = relation.updateMember({role: role}, index);
+ graph = graph.replace(relation);
+ }
+ });
+ });
+
+ // Reverse any associated directions on nodes on the way and then replace
+ // the way itself with the reversed node ids and updated way tags
+ return reverseTagsOnNodes(graph, nodes).replace(way.update({nodes: nodes, tags: tags}));
+ };
+}
+
+// For fixing up rendering of multipolygons with tags on the outer member.
+// https://github.com/openstreetmap/iD/issues/613
+function osmIsSimpleMultipolygonOuterMember(entity, graph) {
+ if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0)
+ return false;
+
+ var parents = graph.parentRelations(entity);
+ if (parents.length !== 1)
+ return false;
+
+ var parent = parents[0];
+ if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
+ return false;
+
+ var members = parent.members, member;
+ for (var i = 0; i < members.length; i++) {
+ member = members[i];
+ if (member.id === entity.id && member.role && member.role !== 'outer')
+ return false; // Not outer member
+ if (member.id !== entity.id && (!member.role || member.role === 'outer'))
+ return false; // Not a simple multipolygon
+ }
+
+ return parent;
+}
+
+
+function osmSimpleMultipolygonOuterMember(entity, graph) {
+ if (entity.type !== 'way')
+ return false;
+
+ var parents = graph.parentRelations(entity);
+ if (parents.length !== 1)
+ return false;
+
+ var parent = parents[0];
+ if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
+ return false;
+
+ var members = parent.members, member, outerMember;
+ for (var i = 0; i < members.length; i++) {
+ member = members[i];
+ if (!member.role || member.role === 'outer') {
+ if (outerMember)
+ return false; // Not a simple multipolygon
+ outerMember = member;
+ }
+ }
+
+ if (!outerMember)
+ return false;
+
+ var outerEntity = graph.hasEntity(outerMember.id);
+ if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length)
+ return false;
+
+ return outerEntity;
+}
+
+
+// Join `array` into sequences of connecting ways.
+//
+// Segments which share identical start/end nodes will, as much as possible,
+// be connected with each other.
+//
+// The return value is a nested array. Each constituent array contains elements
+// of `array` which have been determined to connect. Each consitituent array
+// also has a `nodes` property whose value is an ordered array of member nodes,
+// with appropriate order reversal and start/end coordinate de-duplication.
+//
+// Members of `array` must have, at minimum, `type` and `id` properties.
+// Thus either an array of `osmWay`s or a relation member array may be
+// used.
+//
+// If an member has a `tags` property, its tags will be reversed via
+// `actionReverse` in the output.
+//
+// Incomplete members (those for which `graph.hasEntity(element.id)` returns
+// false) and non-way members are ignored.
+//
+function osmJoinWays(array, graph) {
+ var joined = [], member, current, nodes, first, last, i, how, what;
+
+ array = array.filter(function(member) {
+ return member.type === 'way' && graph.hasEntity(member.id);
+ });
+
+ function resolve(member) {
+ return graph.childNodes(graph.entity(member.id));
+ }
+
+ function reverse(member) {
+ return member.tags ? actionReverse(member.id, { reverseOneway: true })(graph).entity(member.id) : member;
+ }
+
+ while (array.length) {
+ member = array.shift();
+ current = [member];
+ current.nodes = nodes = resolve(member).slice();
+ joined.push(current);
+
+ while (array.length && lodash.first(nodes) !== lodash.last(nodes)) {
+ first = lodash.first(nodes);
+ last = lodash.last(nodes);
+
+ for (i = 0; i < array.length; i++) {
+ member = array[i];
+ what = resolve(member);
+
+ if (last === lodash.first(what)) {
+ how = nodes.push;
+ what = what.slice(1);
+ break;
+ } else if (last === lodash.last(what)) {
+ how = nodes.push;
+ what = what.slice(0, -1).reverse();
+ member = reverse(member);
+ break;
+ } else if (first === lodash.last(what)) {
+ how = nodes.unshift;
+ what = what.slice(0, -1);
+ break;
+ } else if (first === lodash.first(what)) {
+ how = nodes.unshift;
+ what = what.slice(1).reverse();
+ member = reverse(member);
+ break;
+ } else {
+ what = how = null;
+ }
+ }
+
+ if (!what)
+ break; // No more joinable ways.
+
+ how.apply(current, [member]);
+ how.apply(nodes, what);
+
+ array.splice(i, 1);
+ }
+ }
+
+ return joined;
+}
+
+function osmRelation() {
+ if (!(this instanceof osmRelation)) {
+ return (new osmRelation()).initialize(arguments);
+ } else if (arguments.length) {
+ this.initialize(arguments);
+ }
+}
+
+
+osmEntity$$1.relation = osmRelation;
+
+osmRelation.prototype = Object.create(osmEntity$$1.prototype);
+
+
+osmRelation.creationOrder = function(a, b) {
+ var aId = parseInt(osmEntity$$1.id.toOSM(a.id), 10);
+ var bId = parseInt(osmEntity$$1.id.toOSM(b.id), 10);
+
+ if (aId < 0 || bId < 0) return aId - bId;
+ return bId - aId;
+};
+
+
+lodash.extend(osmRelation.prototype, {
+ type: 'relation',
+ members: [],
+
+
+ copy: function(resolver, copies) {
+ if (copies[this.id])
+ return copies[this.id];
+
+ var copy = osmEntity$$1.prototype.copy.call(this, resolver, copies);
+
+ var members = this.members.map(function(member) {
+ return lodash.extend({}, member, { id: resolver.entity(member.id).copy(resolver, copies).id });
+ });
+
+ copy = copy.update({members: members});
+ copies[this.id] = copy;
+
+ return copy;
+ },
+
+
+ extent: function(resolver, memo) {
+ return resolver.transient(this, 'extent', function() {
+ if (memo && memo[this.id]) return geoExtent$$1();
+ memo = memo || {};
+ memo[this.id] = true;
+
+ var extent$$1 = geoExtent$$1();
+ for (var i = 0; i < this.members.length; i++) {
+ var member = resolver.hasEntity(this.members[i].id);
+ if (member) {
+ extent$$1._extend(member.extent(resolver, memo));
+ }
+ }
+ return extent$$1;
+ });
+ },
+
+
+ geometry: function(graph) {
+ return graph.transient(this, 'geometry', function() {
+ return this.isMultipolygon() ? 'area' : 'relation';
+ });
+ },
+
+
+ isDegenerate: function() {
+ return this.members.length === 0;
+ },
+
+
+ // Return an array of members, each extended with an 'index' property whose value
+ // is the member index.
+ indexedMembers: function() {
+ var result = new Array(this.members.length);
+ for (var i = 0; i < this.members.length; i++) {
+ result[i] = lodash.extend({}, this.members[i], {index: i});
+ }
+ return result;
+ },
+
+
+ // Return the first member with the given role. A copy of the member object
+ // is returned, extended with an 'index' property whose value is the member index.
+ memberByRole: function(role) {
+ for (var i = 0; i < this.members.length; i++) {
+ if (this.members[i].role === role) {
+ return lodash.extend({}, this.members[i], {index: i});
+ }
+ }
+ },
+
+
+ // Return the first member with the given id. A copy of the member object
+ // is returned, extended with an 'index' property whose value is the member index.
+ memberById: function(id) {
+ for (var i = 0; i < this.members.length; i++) {
+ if (this.members[i].id === id) {
+ return lodash.extend({}, this.members[i], {index: i});
+ }
+ }
+ },
+
+
+ // Return the first member with the given id and role. A copy of the member object
+ // is returned, extended with an 'index' property whose value is the member index.
+ memberByIdAndRole: function(id, role) {
+ for (var i = 0; i < this.members.length; i++) {
+ if (this.members[i].id === id && this.members[i].role === role) {
+ return lodash.extend({}, this.members[i], {index: i});
+ }
+ }
+ },
+
+
+ addMember: function(member, index) {
+ var members = this.members.slice();
+ members.splice(index === undefined ? members.length : index, 0, member);
+ return this.update({members: members});
+ },
+
+
+ updateMember: function(member, index) {
+ var members = this.members.slice();
+ members.splice(index, 1, lodash.extend({}, members[index], member));
+ return this.update({members: members});
+ },
+
+
+ removeMember: function(index) {
+ var members = this.members.slice();
+ members.splice(index, 1);
+ return this.update({members: members});
+ },
+
+
+ removeMembersWithID: function(id) {
+ var members = lodash.reject(this.members, function(m) { return m.id === id; });
+ return this.update({members: members});
+ },
+
+
+ // Wherever a member appears with id `needle.id`, replace it with a member
+ // with id `replacement.id`, type `replacement.type`, and the original role,
+ // unless a member already exists with that id and role. Return an updated
+ // relation.
+ replaceMember: function(needle, replacement) {
+ if (!this.memberById(needle.id))
+ return this;
+
+ var members = [];
+
+ for (var i = 0; i < this.members.length; i++) {
+ var member = this.members[i];
+ if (member.id !== needle.id) {
+ members.push(member);
+ } else if (!this.memberByIdAndRole(replacement.id, member.role)) {
+ members.push({id: replacement.id, type: replacement.type, role: member.role});
+ }
+ }
+
+ return this.update({members: members});
+ },
+
+
+ asJXON: function(changeset_id) {
+ var r = {
+ relation: {
+ '@id': this.osmId(),
+ '@version': this.version || 0,
+ member: lodash.map(this.members, function(member) {
+ return {
+ keyAttributes: {
+ type: member.type,
+ role: member.role,
+ ref: osmEntity$$1.id.toOSM(member.id)
+ }
+ };
+ }),
+ tag: lodash.map(this.tags, function(v, k) {
+ return { keyAttributes: { k: k, v: v } };
+ })
+ }
+ };
+ if (changeset_id) r.relation['@changeset'] = changeset_id;
+ return r;
+ },
+
+
+ asGeoJSON: function(resolver) {
+ return resolver.transient(this, 'GeoJSON', function () {
+ if (this.isMultipolygon()) {
+ return {
+ type: 'MultiPolygon',
+ coordinates: this.multipolygon(resolver)
+ };
+ } else {
+ return {
+ type: 'FeatureCollection',
+ properties: this.tags,
+ features: this.members.map(function (member) {
+ return lodash.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
+ })
+ };
+ }
+ });
+ },
+
+
+ area: function(resolver) {
+ return resolver.transient(this, 'area', function() {
+ return area(this.asGeoJSON(resolver));
+ });
+ },
+
+
+ isMultipolygon: function() {
+ return this.tags.type === 'multipolygon';
+ },
+
+
+ isComplete: function(resolver) {
+ for (var i = 0; i < this.members.length; i++) {
+ if (!resolver.hasEntity(this.members[i].id)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+
+ isRestriction: function() {
+ return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
+ },
+
+
+ // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
+ // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
+ //
+ // This corresponds to the structure needed for rendering a multipolygon path using a
+ // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
+ //
+ // In the case of invalid geometries, this function will still return a result which
+ // includes the nodes of all way members, but some Nds may be unclosed and some inner
+ // rings not matched with the intended outer ring.
+ //
+ multipolygon: function(resolver) {
+ var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); }),
+ inners = this.members.filter(function(m) { return 'inner' === m.role; });
+
+ outers = osmJoinWays(outers, resolver);
+ inners = osmJoinWays(inners, resolver);
+
+ outers = outers.map(function(outer) { return lodash.map(outer.nodes, 'loc'); });
+ inners = inners.map(function(inner) { return lodash.map(inner.nodes, 'loc'); });
+
+ var result = outers.map(function(o) {
+ // Heuristic for detecting counterclockwise winding order. Assumes
+ // that OpenStreetMap polygons are not hemisphere-spanning.
+ return [area({ type: 'Polygon', coordinates: [o] }) > 2 * Math.PI ? o.reverse() : o];
+ });
+
+ function findOuter(inner) {
+ var o, outer;
+
+ for (o = 0; o < outers.length; o++) {
+ outer = outers[o];
+ if (geoPolygonContainsPolygon(outer, inner))
+ return o;
+ }
+
+ for (o = 0; o < outers.length; o++) {
+ outer = outers[o];
+ if (geoPolygonIntersectsPolygon(outer, inner, false))
+ return o;
+ }
+ }
+
+ for (var i = 0; i < inners.length; i++) {
+ var inner = inners[i];
+
+ if (area({ type: 'Polygon', coordinates: [inner] }) < 2 * Math.PI) {
+ inner = inner.reverse();
+ }
+
+ var o = findOuter(inners[i]);
+ if (o !== undefined)
+ result[o].push(inners[i]);
+ else
+ result.push([inners[i]]); // Invalid geometry
+ }
+
+ return result;
+ }
+});
+
+function osmLanes(entity) {
+ if (entity.type !== 'way') return null;
+ if (!entity.tags.highway) return null;
+
+ var tags = entity.tags;
+ var isOneWay = entity.isOneWay();
+ var laneCount = getLaneCount(tags, isOneWay);
+ var maxspeed = parseMaxspeed(tags);
+
+ var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
+ var forward = laneDirections.forward;
+ var backward = laneDirections.backward;
+ var bothways = laneDirections.bothways;
+
+ // parse the piped string 'x|y|z' format
+ var turnLanes = {};
+ turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
+ turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
+ turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
+
+ var maxspeedLanes = {};
+ maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
+ maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
+ maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
+
+ var psvLanes = {};
+ psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
+ psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
+ psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
+
+ var busLanes = {};
+ busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
+ busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
+ busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
+
+ var taxiLanes = {};
+ taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
+ taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
+ taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
+
+ var hovLanes = {};
+ hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
+ hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
+ hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
+
+ var hgvLanes = {};
+ hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
+ hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
+ hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
+
+ var bicyclewayLanes = {};
+ bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
+ bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
+ bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
+
+ var lanesObj = {
+ forward: [],
+ backward: [],
+ unspecified: []
+ };
+
+ // map forward/backward/unspecified of each lane type to lanesObj
+ mapToLanesObj(lanesObj, turnLanes, 'turnLane');
+ mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
+ mapToLanesObj(lanesObj, psvLanes, 'psv');
+ mapToLanesObj(lanesObj, busLanes, 'bus');
+ mapToLanesObj(lanesObj, taxiLanes, 'taxi');
+ mapToLanesObj(lanesObj, hovLanes, 'hov');
+ mapToLanesObj(lanesObj, hgvLanes, 'hgv');
+ mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
+
+ return {
+ metadata: {
+ count: laneCount,
+ oneway: isOneWay,
+ forward: forward,
+ backward: backward,
+ bothways: bothways,
+ turnLanes: turnLanes,
+ maxspeed: maxspeed,
+ maxspeedLanes: maxspeedLanes,
+ psvLanes: psvLanes,
+ busLanes: busLanes,
+ taxiLanes: taxiLanes,
+ hovLanes: hovLanes,
+ hgvLanes: hgvLanes,
+ bicyclewayLanes: bicyclewayLanes
+ },
+ lanes: lanesObj
+ };
+}
+
+
+function getLaneCount(tags, isOneWay) {
+ var count;
+ if (tags.lanes) {
+ count = parseInt(tags.lanes, 10);
+ if (count > 0) {
+ return count;
+ }
+ }
+
+
+ switch (tags.highway) {
+ case 'trunk':
+ case 'motorway':
+ count = isOneWay ? 2 : 4;
+ break;
+ default:
+ count = isOneWay ? 1 : 2;
+ break;
+ }
+
+ return count;
+}
+
+
+function parseMaxspeed(tags) {
+ var maxspeed = tags.maxspeed;
+ if (lodash.isNumber(maxspeed)) return maxspeed;
+ if (lodash.isString(maxspeed)) {
+ maxspeed = maxspeed.match(/^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/g);
+ if (!maxspeed) return;
+ return parseInt(maxspeed, 10);
+ }
+}
+
+
+function parseLaneDirections(tags, isOneWay, laneCount) {
+ var forward = parseInt(tags['lanes:forward'], 10);
+ var backward = parseInt(tags['lanes:backward'], 10);
+ var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
+
+ if (parseInt(tags.oneway, 10) === -1) {
+ forward = 0;
+ bothways = 0;
+ backward = laneCount;
+ }
+ else if (isOneWay) {
+ forward = laneCount;
+ bothways = 0;
+ backward = 0;
+ }
+ else if (lodash.isNaN(forward) && lodash.isNaN(backward)) {
+ backward = Math.floor((laneCount - bothways) / 2);
+ forward = laneCount - bothways - backward;
+ }
+ else if (lodash.isNaN(forward)) {
+ if (backward > laneCount - bothways) {
+ backward = laneCount - bothways;
+ }
+ forward = laneCount - bothways - backward;
+ }
+ else if (lodash.isNaN(backward)) {
+ if (forward > laneCount - bothways) {
+ forward = laneCount - bothways;
+ }
+ backward = laneCount - bothways - forward;
+ }
+ return {
+ forward: forward,
+ backward: backward,
+ bothways: bothways
+ };
+}
+
+
+function parseTurnLanes(tag){
+ if (!tag) return;
+
+ var validValues = [
+ 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right',
+ 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'
+ ];
+
+ return tag.split('|')
+ .map(function (s) {
+ if (s === '') s = 'none';
+ return s.split(';')
+ .map(function (d) {
+ return validValues.indexOf(d) === -1 ? 'unknown': d;
+ });
+ });
+}
+
+
+function parseMaxspeedLanes(tag, maxspeed) {
+ if (!tag) return;
+
+ return tag.split('|')
+ .map(function (s) {
+ if (s === 'none') return s;
+ var m = parseInt(s, 10);
+ if (s === '' || m === maxspeed) return null;
+ return lodash.isNaN(m) ? 'unknown': m;
+ });
+}
+
+
+function parseMiscLanes(tag) {
+ if (!tag) return;
+
+ var validValues = [
+ 'yes', 'no', 'designated'
+ ];
+
+ return tag.split('|')
+ .map(function (s) {
+ if (s === '') s = 'no';
+ return validValues.indexOf(s) === -1 ? 'unknown': s;
+ });
+}
+
+
+function parseBicycleWay(tag) {
+ if (!tag) return;
+
+ var validValues = [
+ 'yes', 'no', 'designated', 'lane'
+ ];
+
+ return tag.split('|')
+ .map(function (s) {
+ if (s === '') s = 'no';
+ return validValues.indexOf(s) === -1 ? 'unknown': s;
+ });
+}
+
+
+function mapToLanesObj(lanesObj, data, key) {
+ if (data.forward) data.forward.forEach(function(l, i) {
+ if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
+ lanesObj.forward[i][key] = l;
+ });
+ if (data.backward) data.backward.forEach(function(l, i) {
+ if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
+ lanesObj.backward[i][key] = l;
+ });
+ if (data.unspecified) data.unspecified.forEach(function(l, i) {
+ if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
+ lanesObj.unspecified[i][key] = l;
+ });
+}
+
+var translations = Object.create(null);
+
+var currentLocale = 'en';
+var textDirection = 'ltr';
+
+function setLocale(_) {
+ if (translations[_] !== undefined) {
+ currentLocale = _;
+ } else if (translations[_.split('-')[0]]) {
+ currentLocale = _.split('-')[0];
+ }
+}
+
+function addTranslation(id, value) {
+ translations[id] = value;
+}
+
+/**
+ * Given a string identifier, try to find that string in the current
+ * language, and return it.
+ *
+ * @param {string} s string identifier
+ * @returns {string?} locale string
+ */
+function t(s, o, loc) {
+ loc = loc || currentLocale;
+
+ var path = s
+ .split('.')
+ .map(function(s) { return s.replace('<TX_DOT>', '.'); })
+ .reverse();
+
+ var rep = translations[loc];
+
+ while (rep !== undefined && path.length) rep = rep[path.pop()];
+
+ if (rep !== undefined) {
+ if (o) for (var k in o) rep = rep.replace('{' + k + '}', o[k]);
+ return rep;
+ }
+
+ if (loc !== 'en') {
+ return t(s, o, 'en');
+ }
+
+ if (o && 'default' in o) {
+ return o.default;
+ }
+
+ var missing = 'Missing ' + loc + ' translation: ' + s;
+ if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line
+
+ return missing;
+}
+
+/**
+ * Given string 'ltr' or 'rtl', save that setting
+ *
+ * @param {string} s ltr or rtl
+ */
+
+function setTextDirection(dir) {
+ textDirection = dir;
+}
+
+var detected;
+
+function utilDetect(force) {
+ if (detected && !force) return detected;
+ detected = {};
+
+ var ua = navigator.userAgent,
+ m = null;
+
+ m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
+ if (m !== null) {
+ detected.browser = m[1];
+ detected.version = m[2];
+ }
+ if (!detected.browser) {
+ m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
+ if (m !== null) {
+ detected.browser = 'msie';
+ detected.version = m[1];
+ }
+ }
+ if (!detected.browser) {
+ m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
+ if (m !== null) {
+ detected.browser = 'Opera';
+ detected.version = m[2];
+ }
+ }
+ if (!detected.browser) {
+ m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
+ if (m !== null) {
+ detected.browser = m[1];
+ detected.version = m[2];
+ m = ua.match(/version\/([\.\d]+)/i);
+ if (m !== null) detected.version = m[1];
+ }
+ }
+ if (!detected.browser) {
+ detected.browser = navigator.appName;
+ detected.version = navigator.appVersion;
+ }
+
+ // keep major.minor version only..
+ detected.version = detected.version.split(/\W/).slice(0,2).join('.');
+
+ if (detected.browser.toLowerCase() === 'msie') {
+ detected.ie = true;
+ detected.browser = 'Internet Explorer';
+ detected.support = parseFloat(detected.version) >= 11;
+ } else {
+ detected.ie = false;
+ detected.support = true;
+ }
+
+ // Added due to incomplete svg style support. See #715
+ detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
+
+ detected.locale = (navigator.language || navigator.userLanguage || 'en-US');
+ detected.language = detected.locale.split('-')[0];
+
+ // Search `navigator.languages` for a better locale.. Prefer the first language,
+ // unless the second language is a culture-specific version of the first one, see #3842
+ if (navigator.languages && navigator.languages.length > 0) {
+ var code0 = navigator.languages[0],
+ parts0 = code0.split('-');
+
+ detected.locale = code0;
+ detected.language = parts0[0];
+
+ if (navigator.languages.length > 1 && parts0.length === 1) {
+ var code1 = navigator.languages[1],
+ parts1 = code1.split('-');
+
+ if (parts1[0] === parts0[0]) {
+ detected.locale = code1;
+ }
+ }
+ }
+
+ // Loaded locale is stored in currentLocale
+ // return that instead (except in the situation where 'en' might override 'en-US')
+ var loadedLocale = currentLocale || 'en';
+ if (loadedLocale !== 'en') {
+ detected.locale = loadedLocale;
+ detected.language = detected.locale.split('-')[0];
+ }
+
+ // detect text direction
+ var q = utilStringQs(window.location.hash.substring(1));
+ var lang = dataLocales[detected.locale];
+ if ((lang && lang.rtl) || q.hasOwnProperty('rtl')) {
+ detected.textDirection = 'rtl';
+ } else {
+ detected.textDirection = 'ltr';
+ }
+ setTextDirection(detected.textDirection);
+
+ // detect host
+ var loc = window.top.location;
+ var origin = loc.origin;
+ if (!origin) { // for unpatched IE11
+ origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port: '');
+ }
+
+ detected.host = origin + loc.pathname;
+
+ detected.filedrop = (window.FileReader && 'ondrop' in window);
+
+ function nav(x) {
+ return navigator.userAgent.indexOf(x) !== -1;
+ }
+
+ if (nav('Win')) {
+ detected.os = 'win';
+ detected.platform = 'Windows';
+ }
+ else if (nav('Mac')) {
+ detected.os = 'mac';
+ detected.platform = 'Macintosh';
+ }
+ else if (nav('X11') || nav('Linux')) {
+ detected.os = 'linux';
+ detected.platform = 'Linux';
+ }
+ else {
+ detected.os = 'win';
+ detected.platform = 'Unknown';
+ }
+
+ return detected;
+}
+
+var remove$1 = removeDiacritics;
+
+var replacementList = [
+ {
+ base: ' ',
+ chars: "\u00A0",
+ }, {
+ base: '0',
+ chars: "\u07C0",
+ }, {
+ base: 'A',
+ chars: "\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F",
+ }, {
+ base: 'AA',
+ chars: "\uA732",
+ }, {
+ base: 'AE',
+ chars: "\u00C6\u01FC\u01E2",
+ }, {
+ base: 'AO',
+ chars: "\uA734",
+ }, {
+ base: 'AU',
+ chars: "\uA736",
+ }, {
+ base: 'AV',
+ chars: "\uA738\uA73A",
+ }, {
+ base: 'AY',
+ chars: "\uA73C",
+ }, {
+ base: 'B',
+ chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181",
+ }, {
+ base: 'C',
+ chars: "\u24b8\uff23\uA73E\u1E08\u0106\u0043\u0108\u010A\u010C\u00C7\u0187\u023B",
+ }, {
+ base: 'D',
+ chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779",
+ }, {
+ base: 'Dh',
+ chars: "\u00D0",
+ }, {
+ base: 'DZ',
+ chars: "\u01F1\u01C4",
+ }, {
+ base: 'Dz',
+ chars: "\u01F2\u01C5",
+ }, {
+ base: 'E',
+ chars: "\u025B\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E\u1D07",
+ }, {
+ base: 'F',
+ chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B",
+ }, {
+ base: 'G',
+ chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262",
+ }, {
+ base: 'H',
+ chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D",
+ }, {
+ base: 'I',
+ chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197",
+ }, {
+ base: 'J',
+ chars: "\u24BF\uFF2A\u0134\u0248\u0237",
+ }, {
+ base: 'K',
+ chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2",
+ }, {
+ base: 'L',
+ chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780",
+ }, {
+ base: 'LJ',
+ chars: "\u01C7",
+ }, {
+ base: 'Lj',
+ chars: "\u01C8",
+ }, {
+ base: 'M',
+ chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB",
+ }, {
+ base: 'N',
+ chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E",
+ }, {
+ base: 'NJ',
+ chars: "\u01CA",
+ }, {
+ base: 'Nj',
+ chars: "\u01CB",
+ }, {
+ base: 'O',
+ chars: "\u24C4\uFF2F\xD2\xD3\xD4\u1ED2\u1ED0\u1ED6\u1ED4\xD5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\xD6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\xD8\u01FE\u0186\u019F\uA74A\uA74C",
+ }, {
+ base: 'OE',
+ chars: "\u0152",
+ }, {
+ base: 'OI',
+ chars: "\u01A2",
+ }, {
+ base: 'OO',
+ chars: "\uA74E",
+ }, {
+ base: 'OU',
+ chars: "\u0222",
+ }, {
+ base: 'P',
+ chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754",
+ }, {
+ base: 'Q',
+ chars: "\u24C6\uFF31\uA756\uA758\u024A",
+ }, {
+ base: 'R',
+ chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782",
+ }, {
+ base: 'S',
+ chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784",
+ }, {
+ base: 'T',
+ chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786",
+ }, {
+ base: 'Th',
+ chars: "\u00DE",
+ }, {
+ base: 'TZ',
+ chars: "\uA728",
+ }, {
+ base: 'U',
+ chars: "\u24CA\uFF35\xD9\xDA\xDB\u0168\u1E78\u016A\u1E7A\u016C\xDC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244",
+ }, {
+ base: 'V',
+ chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245",
+ }, {
+ base: 'VY',
+ chars: "\uA760",
+ }, {
+ base: 'W',
+ chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72",
+ }, {
+ base: 'X',
+ chars: "\u24CD\uFF38\u1E8A\u1E8C",
+ }, {
+ base: 'Y',
+ chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE",
+ }, {
+ base: 'Z',
+ chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762",
+ }, {
+ base: 'a',
+ chars: "\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250\u0251",
+ }, {
+ base: 'aa',
+ chars: "\uA733",
+ }, {
+ base: 'ae',
+ chars: "\u00E6\u01FD\u01E3",
+ }, {
+ base: 'ao',
+ chars: "\uA735",
+ }, {
+ base: 'au',
+ chars: "\uA737",
+ }, {
+ base: 'av',
+ chars: "\uA739\uA73B",
+ }, {
+ base: 'ay',
+ chars: "\uA73D",
+ }, {
+ base: 'b',
+ chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182",
+ }, {
+ base: 'c',
+ chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184",
+ }, {
+ base: 'd',
+ chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA",
+ }, {
+ base: 'dh',
+ chars: "\u00F0",
+ }, {
+ base: 'dz',
+ chars: "\u01F3\u01C6",
+ }, {
+ base: 'e',
+ chars: "\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u01DD",
+ }, {
+ base: 'f',
+ chars: "\u24D5\uFF46\u1E1F\u0192",
+ }, {
+ base: 'ff',
+ chars: "\uFB00",
+ }, {
+ base: 'fi',
+ chars: "\uFB01",
+ }, {
+ base: 'fl',
+ chars: "\uFB02",
+ }, {
+ base: 'ffi',
+ chars: "\uFB03",
+ }, {
+ base: 'ffl',
+ chars: "\uFB04",
+ }, {
+ base: 'g',
+ chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79",
+ }, {
+ base: 'h',
+ chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265",
+ }, {
+ base: 'hv',
+ chars: "\u0195",
+ }, {
+ base: 'i',
+ chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131",
+ }, {
+ base: 'j',
+ chars: "\u24D9\uFF4A\u0135\u01F0\u0249",
+ }, {
+ base: 'k',
+ chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3",
+ }, {
+ base: 'l',
+ chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D",
+ }, {
+ base: 'lj',
+ chars: "\u01C9",
+ }, {
+ base: 'm',
+ chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F",
+ }, {
+ base: 'n',
+ chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509",
+ }, {
+ base: 'nj',
+ chars: "\u01CC",
+ }, {
+ base: 'o',
+ chars: "\u24DE\uFF4F\xF2\xF3\xF4\u1ED3\u1ED1\u1ED7\u1ED5\xF5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\xF6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\xF8\u01FF\uA74B\uA74D\u0275\u0254\u1D11",
+ }, {
+ base: 'oe',
+ chars: "\u0153",
+ }, {
+ base: 'oi',
+ chars: "\u01A3",
+ }, {
+ base: 'oo',
+ chars: "\uA74F",
+ }, {
+ base: 'ou',
+ chars: "\u0223",
+ }, {
+ base: 'p',
+ chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1",
+ }, {
+ base: 'q',
+ chars: "\u24E0\uFF51\u024B\uA757\uA759",
+ }, {
+ base: 'r',
+ chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783",
+ }, {
+ base: 's',
+ chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282",
+ }, {
+ base: 'ss',
+ chars: "\xDF",
+ }, {
+ base: 't',
+ chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787",
+ }, {
+ base: 'th',
+ chars: "\u00FE",
+ }, {
+ base: 'tz',
+ chars: "\uA729",
+ }, {
+ base: 'u',
+ chars: "\u24E4\uFF55\xF9\xFA\xFB\u0169\u1E79\u016B\u1E7B\u016D\xFC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289",
+ }, {
+ base: 'v',
+ chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C",
+ }, {
+ base: 'vy',
+ chars: "\uA761",
+ }, {
+ base: 'w',
+ chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73",
+ }, {
+ base: 'x',
+ chars: "\u24E7\uFF58\u1E8B\u1E8D",
+ }, {
+ base: 'y',
+ chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF",
+ }, {
+ base: 'z',
+ chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763",
+ }
+];
+
+var diacriticsMap = {};
+for (var i = 0; i < replacementList.length; i += 1) {
+ var chars = replacementList[i].chars;
+ for (var j = 0; j < chars.length; j += 1) {
+ diacriticsMap[chars[j]] = replacementList[i].base;
+ }
+}
+
+function removeDiacritics(str) {
+ return str.replace(/[^\u0000-\u007e]/g, function(c) {
+ return diacriticsMap[c] || c;
+ });
+}
+
+// see https://github.com/openstreetmap/iD/pull/3707
+// https://gist.github.com/mapmeld/556b09ddec07a2044c76e1ef45f01c60
+
+var chars$1 = {
+ // madda above alef
+ 1570: { initial: 'آ', isolated: 'ﺁ', medial: 'ﺁ', final: 'ﺂ' },
+
+ // hamza above and below alef
+ 1571: { initial: 'أ', isolated: 'ﺃ', medial: '', final: 'ﺄ' },
+ // 1572 is ؤ
+ 1573: { initial: 'إ', isolated: 'ﺇ', medial: '', final: 'ﺈ' },
+ // 1574 is ئ
+ 1575: { initial: 'ا', isolated: 'ا', medial: '', final: 'ﺎ' },
+ 1576: { initial: 'ﺑ', isolated: 'ﺏ', medial: 'ﺒ', final: 'ﺐ' },
+
+ // 1577 ة
+ 1577: { initial: '', isolated: 'ة', medial: '', final: 'ﺔ' },
+
+ 1578: { initial: 'ﺗ', isolated: 'ﺕ', medial: 'ﺘ', final: 'ﺖ' },
+ 1579: { initial: 'ﺛ', isolated: 'ﺙ', medial: 'ﺜ', final: 'ﺚ' },
+ 1580: { initial: 'ﺟ', isolated: 'ﺝ', medial: 'ﺠ', final: 'ﺞ' },
+ 1581: { initial: 'ﺣ', isolated: 'ﺡ', medial: 'ﺤ', final: 'ﺢ' },
+ 1582: { initial: 'ﺧ', isolated: 'ﺥ', medial: 'ﺨ', final: 'ﺦ' },
+ 1583: { initial: 'ﺩ', isolated: 'ﺩ', medial: '', final: 'ﺪ' },
+ 1584: { initial: 'ﺫ', isolated: 'ﺫ', medial: '', final: 'ﺬ' },
+ 1585: { initial: 'ﺭ', isolated: 'ﺭ', medial: '', final: 'ﺮ' },
+ 1586: { initial: 'ﺯ', isolated: 'ﺯ', medial: '', final: 'ﺰ' },
+ 1688: { initial: 'ﮊ', isolated: 'ﮊ', medial: '', final: 'ﮋ' },
+ 1587: { initial: 'ﺳ', isolated: 'ﺱ', medial: 'ﺴ', final: 'ﺲ' },
+ 1588: { initial: 'ﺷ', isolated: 'ﺵ', medial: 'ﺸ', final: 'ﺶ' },
+ 1589: { initial: 'ﺻ', isolated: 'ﺹ', medial: 'ﺼ', final: 'ﺺ' },
+ 1590: { initial: 'ﺿ', isolated: 'ﺽ', medial: 'ﻀ', final: 'ﺾ' },
+ 1591: { initial: 'ﻃ', isolated: 'ﻁ', medial: 'ﻄ', final: 'ﻂ' },
+ 1592: { initial: 'ﻇ', isolated: 'ﻅ', medial: 'ﻈ', final: 'ﻆ' },
+ 1593: { initial: 'ﻋ', isolated: 'ﻉ', medial: 'ﻌ', final: 'ﻊ' },
+ 1594: { initial: 'ﻏ', isolated: 'ﻍ', medial: 'ﻐ', final: 'ﻎ' },
+
+ // 1595 ػ - may be very rare
+
+ 1601: { initial: 'ﻓ', isolated: 'ﻑ', medial: 'ﻔ', final: 'ﻒ' },
+ 1602: { initial: 'ﻗ', isolated: 'ﻕ', medial: 'ﻘ', final: 'ﻖ' },
+ 1604: { initial: 'ﻟ', isolated: 'ﻝ', medial: 'ﻠ', final: 'ﻞ' },
+ 1605: { initial: 'ﻣ', isolated: 'ﻡ', medial: 'ﻤ', final: 'ﻢ' },
+ 1606: { initial: 'ﻧ', isolated: 'ﻥ', medial: 'ﻨ', final: 'ﻦ' },
+ 1607: { initial: 'ﻫ', isolated: 'ﻩ', medial: 'ﻬ', final: 'ﻪ' },
+ 1608: { initial: 'ﻭ', isolated: 'ﻭ', medial: '', final: 'ﻮ' },
+
+ // 1609 ى
+ 1609: { initial: 'ﯨ', isolated: 'ﻯ', medial: 'ﯩ', final: 'ﻰ' },
+ // 1610 ي
+ 1610: { initial: 'ﻳ', isolated: 'ﻱ', medial: 'ﻴ', final: 'ﻲ' },
+
+ // short vowel sounds / tashkil markings
+
+ 1662: { initial: 'ﭘ', isolated: 'ﭖ', medial: 'ﭙ', final: 'ﭗ' },
+
+ 1670: { initial: 'ﭼ', isolated: 'ﭺ', medial: 'ﭽ', final: 'ﭻ' },
+ 1603: { initial: 'ﻛ', isolated: 'ﻙ', medial: 'ﻜ', final: 'ﻚ' },
+ 1705: { initial: 'ﻛ', isolated: 'ﮎ', medial: 'ﻜ', final: 'ﮏ' },
+ 1711: { initial: 'ﮔ', isolated: 'ﮒ', medial: 'ﮕ', final: 'ﮓ' },
+ 1740: { initial: 'ﻳ', isolated: 'ﻯ', medial: 'ﻴ', final: 'ﻰ' },
+ 5000: { initial: 'ﻻ', isolated: 'ﻻ', medial: '', final: 'ﻼ' }
+};
+
+var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0780-\u07BF]/;
+
+function fixRTLTextForSvg(inputText) {
+ var context = true;
+ var ret = '';
+ var rtlBuffer = [];
+ var arabicRegex = /[\u0600-\u06FF]/g;
+ var arabicTashkil = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED]/;
+ var thaanaVowel = /[\u07A6-\u07B0]/;
+ var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/;
+
+ if (!arabicRegex.test(inputText)) {
+ // Hebrew or Thaana RTL script
+ for (var n = 0; n < inputText.length; n++) {
+ var c = inputText[n];
+ if ((thaanaVowel.test(c) || hebrewSign.test(c)) && rtlBuffer.length) {
+ rtlBuffer[rtlBuffer.length - 1] += c;
+ } else if (rtlRegex.test(c)) {
+ rtlBuffer.push(c);
+ } else if (c === ' ' && rtlBuffer.length) {
+ // whitespace within RTL text
+ rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
+ } else {
+ // non-RTL character
+ ret += rtlBuffer.reverse().join('') + c;
+ rtlBuffer = [];
+ }
+ }
+ } else {
+ for (var i = 0, l = inputText.length; i < l; i++) {
+ var code = inputText[i].charCodeAt(0);
+ var nextCode = inputText[i + 1] ? inputText[i + 1].charCodeAt(0) : 0;
+
+ if (!chars$1[code]) {
+ if (code === 32 && rtlBuffer.length) {
+ // whitespace
+ rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
+ } else if (arabicTashkil.test(inputText[i]) && rtlBuffer.length) {
+ // tashkil mark
+ rtlBuffer[rtlBuffer.length - 1] += inputText[i];
+ } else {
+ // non-RTL character
+ ret += rtlBuffer.reverse().join('') + inputText[i];
+ rtlBuffer = [];
+ }
+ continue;
+ }
+ if (context) {
+ if (i === l - 1 || nextCode === 32) {
+ rtlBuffer.push(chars$1[code].isolated);
+ } else {
+ // special case for لا
+ if (code === 1604 && nextCode === 1575) {
+ rtlBuffer.push(chars$1[5000].initial);
+ i++;
+ context = true;
+ continue;
+ }
+ rtlBuffer.push(chars$1[code].initial);
+ }
+ } else {
+ if (i === l - 1 || nextCode === 32){
+ rtlBuffer.push(chars$1[code].final);
+ } else {
+ // special case for ﻼ
+ if (code === 1604 && nextCode === 1575){
+ rtlBuffer.push(chars$1[5000].final);
+ i++;
+ context = true;
+ continue;
+ }
+ if (chars$1[code].medial === ''){
+ rtlBuffer.push(chars$1[code].final);
+ } else {
+ rtlBuffer.push(chars$1[code].medial);
+ }
+ }
+ }
+ context = (chars$1[code].medial === '') || nextCode === 32;
+ }
+ }
+ ret += rtlBuffer.reverse().join('');
+ return ret;
+}
+
+function utilTagText(entity) {
+ return entries(entity.tags).map(function(e) {
+ return e.key + '=' + e.value;
+ }).join(', ');
+}
+
+
+function utilEntitySelector(ids) {
+ return ids.length ? '.' + ids.join(',.') : 'nothing';
+}
+
+
+function utilEntityOrMemberSelector(ids, graph) {
+ var s = utilEntitySelector(ids);
+
+ ids.forEach(function(id) {
+ var entity = graph.hasEntity(id);
+ if (entity && entity.type === 'relation') {
+ entity.members.forEach(function(member) {
+ s += ',.' + member.id;
+ });
+ }
+ });
+
+ return s;
+}
+
+
+function utilGetAllNodes(ids, graph) {
+ var seen = {};
+ var nodes = [];
+ ids.forEach(getNodes);
+ return nodes;
+
+ function getNodes(id) {
+ if (seen[id]) return;
+ seen[id] = true;
+
+ var entity = graph.hasEntity(id);
+ if (!entity) return;
+
+ if (entity.type === 'node') {
+ nodes.push(entity);
+ } else if (entity.type === 'way') {
+ entity.nodes.forEach(getNodes);
+ } else {
+ entity.members.map(function(member) { return member.id; }).forEach(getNodes);
+ }
+ }
+}
+
+
+function utilDisplayName(entity) {
+ var localizedNameKey = 'name:' + utilDetect().locale.toLowerCase().split('-')[0],
+ name = entity.tags[localizedNameKey] || entity.tags.name || '',
+ network = entity.tags.cycle_network || entity.tags.network;
+
+ if (!name && entity.tags.ref) {
+ name = entity.tags.ref;
+ if (network) {
+ name = network + ' ' + name;
+ }
+ }
+
+ return name;
+}
+
+
+function utilDisplayNameForPath(entity) {
+ var name = utilDisplayName(entity);
+ var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
+
+ if (!isFirefox && name && rtlRegex.test(name)) {
+ name = fixRTLTextForSvg(name);
+ }
+
+ return name;
+}
+
+
+function utilDisplayType(id) {
+ return {
+ n: t('inspector.node'),
+ w: t('inspector.way'),
+ r: t('inspector.relation')
+ }[id.charAt(0)];
+}
+
+
+function utilStringQs(str) {
+ return str.split('&').reduce(function(obj, pair){
+ var parts = pair.split('=');
+ if (parts.length === 2) {
+ obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
+ }
+ return obj;
+ }, {});
+}
+
+
+function utilQsString(obj, noencode) {
+ function softEncode(s) {
+ // encode everything except special characters used in certain hash parameters:
+ // "/" in map states, ":", ",", {" and "}" in background
+ return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
+ }
+ return Object.keys(obj).sort().map(function(key) {
+ return encodeURIComponent(key) + '=' + (
+ noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
+ }).join('&');
+}
+
+
+function utilPrefixDOMProperty(property) {
+ var prefixes = ['webkit', 'ms', 'moz', 'o'],
+ i = -1,
+ n = prefixes.length,
+ s = document.body;
+
+ if (property in s)
+ return property;
+
+ property = property.substr(0, 1).toUpperCase() + property.substr(1);
+
+ while (++i < n)
+ if (prefixes[i] + property in s)
+ return prefixes[i] + property;
+
+ return false;
+}
+
+
+function utilPrefixCSSProperty(property) {
+ var prefixes = ['webkit', 'ms', 'Moz', 'O'],
+ i = -1,
+ n = prefixes.length,
+ s = document.body.style;
+
+ if (property.toLowerCase() in s)
+ return property.toLowerCase();
+
+ while (++i < n)
+ if (prefixes[i] + property in s)
+ return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
+
+ return false;
+}
+
+
+var transformProperty;
+function utilSetTransform(el, x, y, scale) {
+ var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform'),
+ translate = utilDetect().opera ?
+ 'translate(' + x + 'px,' + y + 'px)' :
+ 'translate3d(' + x + 'px,' + y + 'px,0)';
+ return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
+}
+
+
+// Calculates Levenshtein distance between two strings
+// see: https://en.wikipedia.org/wiki/Levenshtein_distance
+// first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
+function utilEditDistance(a, b) {
+ a = remove$1(a.toLowerCase());
+ b = remove$1(b.toLowerCase());
+ if (a.length === 0) return b.length;
+ if (b.length === 0) return a.length;
+ var matrix = [];
+ for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
+ for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
+ for (i = 1; i <= b.length; i++) {
+ for (j = 1; j <= a.length; j++) {
+ if (b.charAt(i-1) === a.charAt(j-1)) {
+ matrix[i][j] = matrix[i-1][j-1];
+ } else {
+ matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
+ Math.min(matrix[i][j-1] + 1, // insertion
+ matrix[i-1][j] + 1)); // deletion
+ }
+ }
+ }
+ return matrix[b.length][a.length];
+}
+
+
+// a d3.mouse-alike which
+// 1. Only works on HTML elements, not SVG
+// 2. Does not cause style recalculation
+function utilFastMouse(container) {
+ var rect = container.getBoundingClientRect(),
+ rectLeft = rect.left,
+ rectTop = rect.top,
+ clientLeft = +container.clientLeft,
+ clientTop = +container.clientTop;
+ if (textDirection === 'rtl') {
+ rectLeft = 0;
+ }
+ return function(e) {
+ return [
+ e.clientX - rectLeft - clientLeft,
+ e.clientY - rectTop - clientTop];
+ };
+}
+
+
+/* eslint-disable no-proto */
+var utilGetPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
+/* eslint-enable no-proto */
+
+
+function utilAsyncMap(inputs, func, callback) {
+ var remaining = inputs.length,
+ results = [],
+ errors = [];
+
+ inputs.forEach(function(d, i) {
+ func(d, function done(err, data) {
+ errors[i] = err;
+ results[i] = data;
+ remaining--;
+ if (!remaining) callback(errors, results);
+ });
+ });
+}
+
+
+// wraps an index to an interval [0..length-1]
+function utilWrap(index, length) {
+ if (index < 0)
+ index += Math.ceil(-index/length)*length;
+ return index % length;
+}
+
+
+/**
+ * a replacement for functor
+ *
+ * @param {*} value any value
+ * @returns {Function} a function that returns that value or the value if it's a function
+ */
+function utilFunctor(value) {
+ if (typeof value === 'function') return value;
+ return function() {
+ return value;
+ };
+}
+
+
+function utilNoAuto(selection$$1) {
+ return selection$$1
+ .attr('autocomplete', 'off')
+ .attr('autocorrect', 'off')
+ .attr('autocapitalize', 'off')
+ .attr('spellcheck', 'false');
+}
+
+// Like selection.property('value', ...), but avoids no-op value sets,
+// which can result in layout/repaint thrashing in some situations.
+function utilGetSetValue(selection, 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 === 1) {
+ return selection.property('value');
+ }
+
+ return selection.each(d3_selection_value(value));
+}
+
+// Copies a variable number of methods from source to target.
+function utilRebind(target, source) {
+ var i = 1, n = arguments.length, method;
+ while (++i < n) {
+ target[method = arguments[i]] = d3_rebind(target, source, source[method]);
+ }
+ return target;
+}
+
+// Method is assumed to be a standard D3 getter-setter:
+// If passed with no arguments, gets the value.
+// If passed with arguments, sets the value and returns the target.
+function d3_rebind(target, source, method) {
+ return function() {
+ var value = method.apply(source, arguments);
+ return value === source ? target : value;
+ };
+}
+
+// A per-domain session mutex backed by a cookie and dead man's
+// switch. If the session crashes, the mutex will auto-release
+// after 5 seconds.
+
+function utilSessionMutex(name) {
+ var mutex = {},
+ intervalID;
+
+ function renew() {
+ var expires = new Date();
+ expires.setSeconds(expires.getSeconds() + 5);
+ document.cookie = name + '=1; expires=' + expires.toUTCString();
+ }
+
+ mutex.lock = function() {
+ if (intervalID) return true;
+ var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
+ if (cookie) return false;
+ renew();
+ intervalID = window.setInterval(renew, 4000);
+ return true;
+ };
+
+ mutex.unlock = function() {
+ if (!intervalID) return;
+ document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
+ clearInterval(intervalID);
+ intervalID = null;
+ };
+
+ mutex.locked = function() {
+ return !!intervalID;
+ };
+
+ return mutex;
+}
+
+function utilSuggestNames(preset, suggestions) {
+ preset = preset.id.split('/', 2);
+ var k = preset[0],
+ v = preset[1];
+
+ return function(value, callback) {
+ var result = [];
+ if (value && value.length > 2) {
+ if (suggestions[k] && suggestions[k][v]) {
+ for (var sugg in suggestions[k][v]) {
+ var dist = utilEditDistance(value, sugg.substring(0, value.length));
+ if (dist < 3) {
+ result.push({
+ title: sugg,
+ value: sugg,
+ dist: dist
+ });
+ }
+ }
+ }
+ result.sort(function(a, b) {
+ return a.dist - b.dist;
+ });
+ }
+ result = result.slice(0,3);
+ callback(result);
+ };
+}
+
+function utilTriggerEvent(target, type) {
+ target.each(function() {
+ var evt = document.createEvent('HTMLEvents');
+ evt.initEvent(type, true, true);
+ this.dispatchEvent(evt);
+ });
+}
+
+function validationDeprecatedTag() {
+
+ var validation = function(changes) {
+ var warnings = [];
+ for (var i = 0; i < changes.created.length; i++) {
+ var change = changes.created[i],
+ deprecatedTags = change.deprecatedTags();
+
+ if (!lodash.isEmpty(deprecatedTags)) {
+ var tags = utilTagText({ tags: deprecatedTags });
+ warnings.push({
+ id: 'deprecated_tags',
+ message: t('validations.deprecated_tags', { tags: tags }),
+ entity: change
+ });
+ }
+ }
+
+ return warnings;
+ };
+
+
+ return validation;
+}
+
+function validationDisconnectedHighway() {
+
+
+ function isDisconnectedHighway(entity, graph) {
+ if (!entity.tags.highway) return false;
+ if (entity.geometry(graph) !== 'line') return false;
+
+ return graph.childNodes(entity)
+ .every(function(vertex) {
+ var parents = graph.parentWays(vertex);
+ if (parents.length === 1) { // standalone vertex
+ return true;
+ } else { // shared vertex
+ return !vertex.tags.entrance &&
+ parents.filter(function(parent) {
+ return parent.tags.highway && parent !== entity;
+ }).length === 0;
+ }
+ });
+ }
+
+
+ var validation = function(changes, graph) {
+ var warnings = [];
+ for (var i = 0; i < changes.created.length; i++) {
+ var entity = changes.created[i];
+
+ if (isDisconnectedHighway(entity, graph)) {
+ warnings.push({
+ id: 'disconnected_highway',
+ message: t('validations.disconnected_highway'),
+ tooltip: t('validations.disconnected_highway_tooltip'),
+ entity: entity
+ });
+ }
+ }
+
+ return warnings;
+ };
+
+
+ return validation;
+}
+
+function validationManyDeletions() {
+ var threshold = 100;
+
+ var validation = function(changes) {
+ var warnings = [];
+ if (changes.deleted.length > threshold) {
+ warnings.push({
+ id: 'many_deletions',
+ message: t('validations.many_deletions', { n: changes.deleted.length })
+ });
+ }
+
+ return warnings;
+ };
+
+
+ return validation;
+}
+
+function validationMissingTag() {
+
+ // Slightly stricter check than Entity#isUsed (#3091)
+ function hasTags(entity, graph) {
+ return lodash.without(Object.keys(entity.tags), 'area', 'name').length > 0 ||
+ graph.parentRelations(entity).length > 0;
+ }
+
+ var validation = function(changes, graph) {
+ var types = ['point', 'line', 'area', 'relation'],
+ warnings = [];
+
+ for (var i = 0; i < changes.created.length; i++) {
+ var change = changes.created[i],
+ geometry = change.geometry(graph);
+
+ if (types.indexOf(geometry) !== -1 && !hasTags(change, graph)) {
+ warnings.push({
+ id: 'missing_tag',
+ message: t('validations.untagged_' + geometry),
+ tooltip: t('validations.untagged_' + geometry + '_tooltip'),
+ entity: change
+ });
+ }
+ }
+
+ return warnings;
+ };
+
+
+ return validation;
+}
+
+function validationOldMultipolygon() {
+
+
+ return function validation(changes, graph) {
+ var warnings = [];
+ for (var i = 0; i < changes.created.length; i++) {
+ var entity = changes.created[i];
+ var parent = osmIsSimpleMultipolygonOuterMember(entity, graph);
+ if (parent) {
+ warnings.push({
+ id: 'old_multipolygon',
+ message: t('validations.old_multipolygon'),
+ tooltip: t('validations.old_multipolygon_tooltip'),
+ entity: parent
+ });
+ }
+ }
+ return warnings;
+ };
+}
+
+// https://github.com/openstreetmap/josm/blob/mirror/src/org/
+// openstreetmap/josm/data/validation/tests/UnclosedWays.java#L80
+function validationTagSuggestsArea() {
+
+ function tagSuggestsArea(tags) {
+ if (lodash.isEmpty(tags)) return false;
+
+ var presence = ['landuse', 'amenities', 'tourism', 'shop'];
+ for (var i = 0; i < presence.length; i++) {
+ if (tags[presence[i]] !== undefined) {
+ return presence[i] + '=' + tags[presence[i]];
+ }
+ }
+
+ if (tags.building && tags.building === 'yes') return 'building=yes';
+ }
+
+
+ var validation = function(changes, graph) {
+ var warnings = [];
+ for (var i = 0; i < changes.created.length; i++) {
+ var change = changes.created[i],
+ geometry = change.geometry(graph),
+ suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined);
+
+ if (suggestion) {
+ warnings.push({
+ id: 'tag_suggests_area',
+ message: t('validations.tag_suggests_area', { tag: suggestion }),
+ entity: change
+ });
+ }
+ }
+
+ return warnings;
+ };
+
+
+ return validation;
+}
+
+
+
+var Validations = Object.freeze({
+ validationDeprecatedTag: validationDeprecatedTag,
+ validationDisconnectedHighway: validationDisconnectedHighway,
+ validationManyDeletions: validationManyDeletions,
+ validationMissingTag: validationMissingTag,
+ validationOldMultipolygon: validationOldMultipolygon,
+ validationTagSuggestsArea: validationTagSuggestsArea
+});
+
+/*
+ iD.Difference represents the difference between two graphs.
+ It knows how to calculate the set of entities that were
+ created, modified, or deleted, and also contains the logic
+ for recursively extending a difference to the complete set
+ of entities that will require a redraw, taking into account
+ child and parent relationships.
+ */
+function coreDifference(base, head) {
+ var changes = {},
+ difference = {},
+ length = 0;
+
+
+ function changed(h, b) {
+ return h !== b && !lodash.isEqual(lodash.omit(h, 'v'), lodash.omit(b, 'v'));
+ }
+
+
+ lodash.each(head.entities, function(h, id) {
+ var b = base.entities[id];
+ if (changed(h, b)) {
+ changes[id] = {base: b, head: h};
+ length++;
+ }
+ });
+
+
+ lodash.each(base.entities, function(b, id) {
+ var h = head.entities[id];
+ if (!changes[id] && changed(h, b)) {
+ changes[id] = {base: b, head: h};
+ length++;
+ }
+ });
+
+
+ function addParents(parents, result) {
+ for (var i = 0; i < parents.length; i++) {
+ var parent = parents[i];
+
+ if (parent.id in result)
+ continue;
+
+ result[parent.id] = parent;
+ addParents(head.parentRelations(parent), result);
+ }
+ }
+
+
+ difference.length = function() {
+ return length;
+ };
+
+
+ difference.changes = function() {
+ return changes;
+ };
+
+
+ difference.extantIDs = function() {
+ var result = [];
+ lodash.each(changes, function(change, id) {
+ if (change.head) result.push(id);
+ });
+ return result;
+ };
+
+
+ difference.modified = function() {
+ var result = [];
+ lodash.each(changes, function(change) {
+ if (change.base && change.head) result.push(change.head);
+ });
+ return result;
+ };
+
+
+ difference.created = function() {
+ var result = [];
+ lodash.each(changes, function(change) {
+ if (!change.base && change.head) result.push(change.head);
+ });
+ return result;
+ };
+
+
+ difference.deleted = function() {
+ var result = [];
+ lodash.each(changes, function(change) {
+ if (change.base && !change.head) result.push(change.base);
+ });
+ return result;
+ };
+
+
+ difference.summary = function() {
+ var relevant = {};
+
+ function addEntity(entity, graph, changeType) {
+ relevant[entity.id] = {
+ entity: entity,
+ graph: graph,
+ changeType: changeType
+ };
+ }
+
+ function addParents(entity) {
+ var parents = head.parentWays(entity);
+ for (var j = parents.length - 1; j >= 0; j--) {
+ var parent = parents[j];
+ if (!(parent.id in relevant)) addEntity(parent, head, 'modified');
+ }
+ }
+
+ lodash.each(changes, function(change) {
+ if (change.head && change.head.geometry(head) !== 'vertex') {
+ addEntity(change.head, head, change.base ? 'modified' : 'created');
+
+ } else if (change.base && change.base.geometry(base) !== 'vertex') {
+ addEntity(change.base, base, 'deleted');
+
+ } else if (change.base && change.head) { // modified vertex
+ var moved = !lodash.isEqual(change.base.loc, change.head.loc),
+ retagged = !lodash.isEqual(change.base.tags, change.head.tags);
+
+ if (moved) {
+ addParents(change.head);
+ }
+
+ if (retagged || (moved && change.head.hasInterestingTags())) {
+ addEntity(change.head, head, 'modified');
+ }
+
+ } else if (change.head && change.head.hasInterestingTags()) { // created vertex
+ addEntity(change.head, head, 'created');
+
+ } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
+ addEntity(change.base, base, 'deleted');
+ }
+ });
+
+ return values(relevant);
+ };
+
+
+ difference.complete = function(extent$$1) {
+ var result = {}, id, change;
+
+ for (id in changes) {
+ change = changes[id];
+
+ var h = change.head,
+ b = change.base,
+ entity = h || b;
+
+ if (extent$$1 &&
+ (!h || !h.intersects(extent$$1, head)) &&
+ (!b || !b.intersects(extent$$1, base)))
+ continue;
+
+ result[id] = h;
+
+ if (entity.type === 'way') {
+ var nh = h ? h.nodes : [],
+ nb = b ? b.nodes : [],
+ diff, i;
+
+ diff = lodash.difference(nh, nb);
+ for (i = 0; i < diff.length; i++) {
+ result[diff[i]] = head.hasEntity(diff[i]);
+ }
+
+ diff = lodash.difference(nb, nh);
+ for (i = 0; i < diff.length; i++) {
+ result[diff[i]] = head.hasEntity(diff[i]);
+ }
+ }
+
+ addParents(head.parentWays(entity), result);
+ addParents(head.parentRelations(entity), result);
+ }
+
+ return result;
+ };
+
+
+ return difference;
+}
+
+function coreGraph$$1(other, mutable) {
+ if (!(this instanceof coreGraph$$1)) return new coreGraph$$1(other, mutable);
+
+ if (other instanceof coreGraph$$1) {
+ var base = other.base();
+ this.entities = lodash.assign(Object.create(base.entities), other.entities);
+ this._parentWays = lodash.assign(Object.create(base.parentWays), other._parentWays);
+ this._parentRels = lodash.assign(Object.create(base.parentRels), other._parentRels);
+
+ } else {
+ this.entities = Object.create({});
+ this._parentWays = Object.create({});
+ this._parentRels = Object.create({});
+ this.rebase(other || [], [this]);
+ }
+
+ this.transients = {};
+ this._childNodes = {};
+ this.frozen = !mutable;
+}
+
+
+coreGraph$$1.prototype = {
+
+ hasEntity: function(id) {
+ return this.entities[id];
+ },
+
+
+ entity: function(id) {
+ var entity = this.entities[id];
+
+ //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
+ if (!entity) {
+ entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
+ }
+
+ if (!entity) {
+ throw new Error('entity ' + id + ' not found');
+ }
+ return entity;
+ },
+
+
+ transient: function(entity, key, fn) {
+ var id = entity.id,
+ transients = this.transients[id] ||
+ (this.transients[id] = {});
+
+ if (transients[key] !== undefined) {
+ return transients[key];
+ }
+
+ transients[key] = fn.call(entity);
+
+ return transients[key];
+ },
+
+
+ parentWays: function(entity) {
+ var parents = this._parentWays[entity.id],
+ result = [];
+
+ if (parents) {
+ for (var i = 0; i < parents.length; i++) {
+ result.push(this.entity(parents[i]));
+ }
+ }
+ return result;
+ },
+
+
+ isPoi: function(entity) {
+ var parentWays = this._parentWays[entity.id];
+ return !parentWays || parentWays.length === 0;
+ },
+
+
+ isShared: function(entity) {
+ var parentWays = this._parentWays[entity.id];
+ return parentWays && parentWays.length > 1;
+ },
+
+
+ parentRelations: function(entity) {
+ var parents = this._parentRels[entity.id],
+ result = [];
+
+ if (parents) {
+ for (var i = 0; i < parents.length; i++) {
+ result.push(this.entity(parents[i]));
+ }
+ }
+ return result;
+ },
+
+
+ childNodes: function(entity) {
+ if (this._childNodes[entity.id]) return this._childNodes[entity.id];
+ if (!entity.nodes) return [];
+
+ var nodes = [];
+ for (var i = 0; i < entity.nodes.length; i++) {
+ nodes[i] = this.entity(entity.nodes[i]);
+ }
+
+ if (debug) Object.freeze(nodes);
+
+ this._childNodes[entity.id] = nodes;
+ return this._childNodes[entity.id];
+ },
+
+
+ base: function() {
+ return {
+ 'entities': utilGetPrototypeOf(this.entities),
+ 'parentWays': utilGetPrototypeOf(this._parentWays),
+ 'parentRels': utilGetPrototypeOf(this._parentRels)
+ };
+ },
+
+
+ // Unlike other graph methods, rebase mutates in place. This is because it
+ // is used only during the history operation that merges newly downloaded
+ // data into each state. To external consumers, it should appear as if the
+ // graph always contained the newly downloaded data.
+ rebase: function(entities, stack, force) {
+ var base = this.base(),
+ i, j, k, id;
+
+ for (i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+
+ if (!entity.visible || (!force && base.entities[entity.id]))
+ continue;
+
+ // Merging data into the base graph
+ base.entities[entity.id] = entity;
+ this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
+
+ // Restore provisionally-deleted nodes that are discovered to have an extant parent
+ if (entity.type === 'way') {
+ for (j = 0; j < entity.nodes.length; j++) {
+ id = entity.nodes[j];
+ for (k = 1; k < stack.length; k++) {
+ var ents = stack[k].entities;
+ if (ents.hasOwnProperty(id) && ents[id] === undefined) {
+ delete ents[id];
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < stack.length; i++) {
+ stack[i]._updateRebased();
+ }
+ },
+
+
+ _updateRebased: function() {
+ var base = this.base(),
+ i, k, child, id, keys;
+
+ keys = Object.keys(this._parentWays);
+ for (i = 0; i < keys.length; i++) {
+ child = keys[i];
+ if (base.parentWays[child]) {
+ for (k = 0; k < base.parentWays[child].length; k++) {
+ id = base.parentWays[child][k];
+ if (!this.entities.hasOwnProperty(id) && !lodash.includes(this._parentWays[child], id)) {
+ this._parentWays[child].push(id);
+ }
+ }
+ }
+ }
+
+ keys = Object.keys(this._parentRels);
+ for (i = 0; i < keys.length; i++) {
+ child = keys[i];
+ if (base.parentRels[child]) {
+ for (k = 0; k < base.parentRels[child].length; k++) {
+ id = base.parentRels[child][k];
+ if (!this.entities.hasOwnProperty(id) && !lodash.includes(this._parentRels[child], id)) {
+ this._parentRels[child].push(id);
+ }
+ }
+ }
+ }
+
+ this.transients = {};
+
+ // this._childNodes is not updated, under the assumption that
+ // ways are always downloaded with their child nodes.
+ },
+
+
+ // Updates calculated properties (parentWays, parentRels) for the specified change
+ _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
+
+ parentWays = parentWays || this._parentWays;
+ parentRels = parentRels || this._parentRels;
+
+ var type = entity && entity.type || oldentity && oldentity.type,
+ removed, added, ways, rels, i;
+
+
+ if (type === 'way') {
+
+ // Update parentWays
+ if (oldentity && entity) {
+ removed = lodash.difference(oldentity.nodes, entity.nodes);
+ added = lodash.difference(entity.nodes, oldentity.nodes);
+ } else if (oldentity) {
+ removed = oldentity.nodes;
+ added = [];
+ } else if (entity) {
+ removed = [];
+ added = entity.nodes;
+ }
+ for (i = 0; i < removed.length; i++) {
+ parentWays[removed[i]] = lodash.without(parentWays[removed[i]], oldentity.id);
+ }
+ for (i = 0; i < added.length; i++) {
+ ways = lodash.without(parentWays[added[i]], entity.id);
+ ways.push(entity.id);
+ parentWays[added[i]] = ways;
+ }
+
+ } else if (type === 'relation') {
+
+ // Update parentRels
+ if (oldentity && entity) {
+ removed = lodash.difference(oldentity.members, entity.members);
+ added = lodash.difference(entity.members, oldentity);
+ } else if (oldentity) {
+ removed = oldentity.members;
+ added = [];
+ } else if (entity) {
+ removed = [];
+ added = entity.members;
+ }
+ for (i = 0; i < removed.length; i++) {
+ parentRels[removed[i].id] = lodash.without(parentRels[removed[i].id], oldentity.id);
+ }
+ for (i = 0; i < added.length; i++) {
+ rels = lodash.without(parentRels[added[i].id], entity.id);
+ rels.push(entity.id);
+ parentRels[added[i].id] = rels;
+ }
+ }
+ },
+
+
+ replace: function(entity) {
+ if (this.entities[entity.id] === entity)
+ return this;
+
+ return this.update(function() {
+ this._updateCalculated(this.entities[entity.id], entity);
+ this.entities[entity.id] = entity;
+ });
+ },
+
+
+ remove: function(entity) {
+ return this.update(function() {
+ this._updateCalculated(entity, undefined);
+ this.entities[entity.id] = undefined;
+ });
+ },
+
+
+ revert: function(id) {
+ var baseEntity = this.base().entities[id],
+ headEntity = this.entities[id];
+
+ if (headEntity === baseEntity)
+ return this;
+
+ return this.update(function() {
+ this._updateCalculated(headEntity, baseEntity);
+ delete this.entities[id];
+ });
+ },
+
+
+ update: function() {
+ var graph = this.frozen ? coreGraph$$1(this, true) : this;
+
+ for (var i = 0; i < arguments.length; i++) {
+ arguments[i].call(graph, graph);
+ }
+
+ if (this.frozen) graph.frozen = true;
+
+ return graph;
+ },
+
+
+ // Obliterates any existing entities
+ load: function(entities) {
+ var base = this.base();
+ this.entities = Object.create(base.entities);
+
+ for (var i in entities) {
+ this.entities[i] = entities[i];
+ this._updateCalculated(base.entities[i], this.entities[i]);
+ }
+
+ return this;
+ }
+};
+
+var index$10 = partialSort;
+
+// Floyd-Rivest selection algorithm:
+// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right];
+// The k-th element will have the (k - left + 1)th smallest value in [left, right]
+
+function partialSort(arr, k, left, right, compare) {
+ left = left || 0;
+ right = right || (arr.length - 1);
+ compare = compare || defaultCompare;
+
+ while (right > left) {
+ if (right - left > 600) {
+ var n = right - left + 1;
+ var m = k - left + 1;
+ var z = Math.log(n);
+ var s = 0.5 * Math.exp(2 * z / 3);
+ var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
+ var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
+ var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
+ partialSort(arr, k, newLeft, newRight, compare);
+ }
+
+ var t = arr[k];
+ var i = left;
+ var j = right;
+
+ swap(arr, left, k);
+ if (compare(arr[right], t) > 0) swap(arr, left, right);
+
+ while (i < j) {
+ swap(arr, i, j);
+ i++;
+ j--;
+ while (compare(arr[i], t) < 0) i++;
+ while (compare(arr[j], t) > 0) j--;
+ }
+
+ if (compare(arr[left], t) === 0) swap(arr, left, j);
+ else {
+ j++;
+ swap(arr, j, right);
+ }
+
+ if (j <= k) left = j + 1;
+ if (k <= j) right = j - 1;
+ }
+}
+
+function swap(arr, i, j) {
+ var tmp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = tmp;
+}
+
+function defaultCompare(a, b) {
+ return a < b ? -1 : a > b ? 1 : 0;
+}
+
+var index$9 = rbush;
+
+
+
+function rbush(maxEntries, format) {
+ if (!(this instanceof rbush)) return new rbush(maxEntries, format);
+
+ // max entries in a node is 9 by default; min node fill is 40% for best performance
+ this._maxEntries = Math.max(4, maxEntries || 9);
+ this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
+
+ if (format) {
+ this._initFormat(format);
+ }
+
+ this.clear();
+}
+
+rbush.prototype = {
+
+ all: function () {
+ return this._all(this.data, []);
+ },
+
+ search: function (bbox) {
+
+ var node = this.data,
+ result = [],
+ toBBox = this.toBBox;
+
+ if (!intersects$1(bbox, node)) return result;
+
+ var nodesToSearch = [],
+ i, len, child, childBBox;
+
+ while (node) {
+ for (i = 0, len = node.children.length; i < len; i++) {
+
+ child = node.children[i];
+ childBBox = node.leaf ? toBBox(child) : child;
+
+ if (intersects$1(bbox, childBBox)) {
+ if (node.leaf) result.push(child);
+ else if (contains$2(bbox, childBBox)) this._all(child, result);
+ else nodesToSearch.push(child);
+ }
+ }
+ node = nodesToSearch.pop();
+ }
+
+ return result;
+ },
+
+ collides: function (bbox) {
+
+ var node = this.data,
+ toBBox = this.toBBox;
+
+ if (!intersects$1(bbox, node)) return false;
+
+ var nodesToSearch = [],
+ i, len, child, childBBox;
+
+ while (node) {
+ for (i = 0, len = node.children.length; i < len; i++) {
+
+ child = node.children[i];
+ childBBox = node.leaf ? toBBox(child) : child;
+
+ if (intersects$1(bbox, childBBox)) {
+ if (node.leaf || contains$2(bbox, childBBox)) return true;
+ nodesToSearch.push(child);
+ }
+ }
+ node = nodesToSearch.pop();
+ }
+
+ return false;
+ },
+
+ load: function (data) {
+ if (!(data && data.length)) return this;
+
+ if (data.length < this._minEntries) {
+ for (var i = 0, len = data.length; i < len; i++) {
+ this.insert(data[i]);
+ }
+ return this;
+ }
+
+ // recursively build the tree with the given data from stratch using OMT algorithm
+ var node = this._build(data.slice(), 0, data.length - 1, 0);
+
+ if (!this.data.children.length) {
+ // save as is if tree is empty
+ this.data = node;
+
+ } else if (this.data.height === node.height) {
+ // split root if trees have the same height
+ this._splitRoot(this.data, node);
+
+ } else {
+ if (this.data.height < node.height) {
+ // swap trees if inserted one is bigger
+ var tmpNode = this.data;
+ this.data = node;
+ node = tmpNode;
+ }
+
+ // insert the small tree into the large tree at appropriate level
+ this._insert(node, this.data.height - node.height - 1, true);
+ }
+
+ return this;
+ },
+
+ insert: function (item) {
+ if (item) this._insert(item, this.data.height - 1);
+ return this;
+ },
+
+ clear: function () {
+ this.data = createNode([]);
+ return this;
+ },
+
+ remove: function (item, equalsFn) {
+ if (!item) return this;
+
+ var node = this.data,
+ bbox = this.toBBox(item),
+ path = [],
+ indexes = [],
+ i, parent, index, goingUp;
+
+ // depth-first iterative tree traversal
+ while (node || path.length) {
+
+ if (!node) { // go up
+ node = path.pop();
+ parent = path[path.length - 1];
+ i = indexes.pop();
+ goingUp = true;
+ }
+
+ if (node.leaf) { // check current node
+ index = findItem(item, node.children, equalsFn);
+
+ if (index !== -1) {
+ // item found, remove the item and condense tree upwards
+ node.children.splice(index, 1);
+ path.push(node);
+ this._condense(path);
+ return this;
+ }
+ }
+
+ if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down
+ path.push(node);
+ indexes.push(i);
+ i = 0;
+ parent = node;
+ node = node.children[0];
+
+ } else if (parent) { // go right
+ i++;
+ node = parent.children[i];
+ goingUp = false;
+
+ } else node = null; // nothing found
+ }
+
+ return this;
+ },
+
+ toBBox: function (item) { return item; },
+
+ compareMinX: compareNodeMinX,
+ compareMinY: compareNodeMinY,
+
+ toJSON: function () { return this.data; },
+
+ fromJSON: function (data) {
+ this.data = data;
+ return this;
+ },
+
+ _all: function (node, result) {
+ var nodesToSearch = [];
+ while (node) {
+ if (node.leaf) result.push.apply(result, node.children);
+ else nodesToSearch.push.apply(nodesToSearch, node.children);
+
+ node = nodesToSearch.pop();
+ }
+ return result;
+ },
+
+ _build: function (items, left, right, height) {
+
+ var N = right - left + 1,
+ M = this._maxEntries,
+ node;
+
+ if (N <= M) {
+ // reached leaf level; return leaf
+ node = createNode(items.slice(left, right + 1));
+ calcBBox(node, this.toBBox);
+ return node;
+ }
+
+ if (!height) {
+ // target height of the bulk-loaded tree
+ height = Math.ceil(Math.log(N) / Math.log(M));
+
+ // target number of root entries to maximize storage utilization
+ M = Math.ceil(N / Math.pow(M, height - 1));
+ }
+
+ node = createNode([]);
+ node.leaf = false;
+ node.height = height;
+
+ // split the items into M mostly square tiles
+
+ var N2 = Math.ceil(N / M),
+ N1 = N2 * Math.ceil(Math.sqrt(M)),
+ i, j, right2, right3;
+
+ multiSelect(items, left, right, N1, this.compareMinX);
+
+ for (i = left; i <= right; i += N1) {
+
+ right2 = Math.min(i + N1 - 1, right);
+
+ multiSelect(items, i, right2, N2, this.compareMinY);
+
+ for (j = i; j <= right2; j += N2) {
+
+ right3 = Math.min(j + N2 - 1, right2);
+
+ // pack each entry recursively
+ node.children.push(this._build(items, j, right3, height - 1));
+ }
+ }
+
+ calcBBox(node, this.toBBox);
+
+ return node;
+ },
+
+ _chooseSubtree: function (bbox, node, level, path) {
+
+ var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
+
+ while (true) {
+ path.push(node);
+
+ if (node.leaf || path.length - 1 === level) break;
+
+ minArea = minEnlargement = Infinity;
+
+ for (i = 0, len = node.children.length; i < len; i++) {
+ child = node.children[i];
+ area = bboxArea(child);
+ enlargement = enlargedArea(bbox, child) - area;
+
+ // choose entry with the least area enlargement
+ if (enlargement < minEnlargement) {
+ minEnlargement = enlargement;
+ minArea = area < minArea ? area : minArea;
+ targetNode = child;
+
+ } else if (enlargement === minEnlargement) {
+ // otherwise choose one with the smallest area
+ if (area < minArea) {
+ minArea = area;
+ targetNode = child;
+ }
+ }
+ }
+
+ node = targetNode || node.children[0];
+ }
+
+ return node;
+ },
+
+ _insert: function (item, level, isNode) {
+
+ var toBBox = this.toBBox,
+ bbox = isNode ? item : toBBox(item),
+ insertPath = [];
+
+ // find the best node for accommodating the item, saving all nodes along the path too
+ var node = this._chooseSubtree(bbox, this.data, level, insertPath);
+
+ // put the item into the node
+ node.children.push(item);
+ extend$1(node, bbox);
+
+ // split on node overflow; propagate upwards if necessary
+ while (level >= 0) {
+ if (insertPath[level].children.length > this._maxEntries) {
+ this._split(insertPath, level);
+ level--;
+ } else break;
+ }
+
+ // adjust bboxes along the insertion path
+ this._adjustParentBBoxes(bbox, insertPath, level);
+ },
+
+ // split overflowed node into two
+ _split: function (insertPath, level) {
+
+ var node = insertPath[level],
+ M = node.children.length,
+ m = this._minEntries;
+
+ this._chooseSplitAxis(node, m, M);
+
+ var splitIndex = this._chooseSplitIndex(node, m, M);
+
+ var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
+ newNode.height = node.height;
+ newNode.leaf = node.leaf;
+
+ calcBBox(node, this.toBBox);
+ calcBBox(newNode, this.toBBox);
+
+ if (level) insertPath[level - 1].children.push(newNode);
+ else this._splitRoot(node, newNode);
+ },
+
+ _splitRoot: function (node, newNode) {
+ // split root node
+ this.data = createNode([node, newNode]);
+ this.data.height = node.height + 1;
+ this.data.leaf = false;
+ calcBBox(this.data, this.toBBox);
+ },
+
+ _chooseSplitIndex: function (node, m, M) {
+
+ var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
+
+ minOverlap = minArea = Infinity;
+
+ for (i = m; i <= M - m; i++) {
+ bbox1 = distBBox(node, 0, i, this.toBBox);
+ bbox2 = distBBox(node, i, M, this.toBBox);
+
+ overlap = intersectionArea(bbox1, bbox2);
+ area = bboxArea(bbox1) + bboxArea(bbox2);
+
+ // choose distribution with minimum overlap
+ if (overlap < minOverlap) {
+ minOverlap = overlap;
+ index = i;
+
+ minArea = area < minArea ? area : minArea;
+
+ } else if (overlap === minOverlap) {
+ // otherwise choose distribution with minimum area
+ if (area < minArea) {
+ minArea = area;
+ index = i;
+ }
+ }
+ }
+
+ return index;
+ },
+
+ // sorts node children by the best axis for split
+ _chooseSplitAxis: function (node, m, M) {
+
+ var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
+ compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
+ xMargin = this._allDistMargin(node, m, M, compareMinX),
+ yMargin = this._allDistMargin(node, m, M, compareMinY);
+
+ // if total distributions margin value is minimal for x, sort by minX,
+ // otherwise it's already sorted by minY
+ if (xMargin < yMargin) node.children.sort(compareMinX);
+ },
+
+ // total margin of all possible split distributions where each node is at least m full
+ _allDistMargin: function (node, m, M, compare) {
+
+ node.children.sort(compare);
+
+ var toBBox = this.toBBox,
+ leftBBox = distBBox(node, 0, m, toBBox),
+ rightBBox = distBBox(node, M - m, M, toBBox),
+ margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
+ i, child;
+
+ for (i = m; i < M - m; i++) {
+ child = node.children[i];
+ extend$1(leftBBox, node.leaf ? toBBox(child) : child);
+ margin += bboxMargin(leftBBox);
+ }
+
+ for (i = M - m - 1; i >= m; i--) {
+ child = node.children[i];
+ extend$1(rightBBox, node.leaf ? toBBox(child) : child);
+ margin += bboxMargin(rightBBox);
+ }
+
+ return margin;
+ },
+
+ _adjustParentBBoxes: function (bbox, path, level) {
+ // adjust bboxes along the given tree path
+ for (var i = level; i >= 0; i--) {
+ extend$1(path[i], bbox);
+ }
+ },
+
+ _condense: function (path) {
+ // go through the path, removing empty nodes and updating bboxes
+ for (var i = path.length - 1, siblings; i >= 0; i--) {
+ if (path[i].children.length === 0) {
+ if (i > 0) {
+ siblings = path[i - 1].children;
+ siblings.splice(siblings.indexOf(path[i]), 1);
+
+ } else this.clear();
+
+ } else calcBBox(path[i], this.toBBox);
+ }
+ },
+
+ _initFormat: function (format) {
+ // data format (minX, minY, maxX, maxY accessors)
+
+ // uses eval-type function compilation instead of just accepting a toBBox function
+ // because the algorithms are very sensitive to sorting functions performance,
+ // so they should be dead simple and without inner calls
+
+ var compareArr = ['return a', ' - b', ';'];
+
+ this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
+ this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
+
+ this.toBBox = new Function('a',
+ 'return {minX: a' + format[0] +
+ ', minY: a' + format[1] +
+ ', maxX: a' + format[2] +
+ ', maxY: a' + format[3] + '};');
+ }
+};
+
+function findItem(item, items, equalsFn) {
+ if (!equalsFn) return items.indexOf(item);
+
+ for (var i = 0; i < items.length; i++) {
+ if (equalsFn(item, items[i])) return i;
+ }
+ return -1;
+}
+
+// calculate node's bbox from bboxes of its children
+function calcBBox(node, toBBox) {
+ distBBox(node, 0, node.children.length, toBBox, node);
+}
+
+// min bounding rectangle of node children from k to p-1
+function distBBox(node, k, p, toBBox, destNode) {
+ if (!destNode) destNode = createNode(null);
+ destNode.minX = Infinity;
+ destNode.minY = Infinity;
+ destNode.maxX = -Infinity;
+ destNode.maxY = -Infinity;
+
+ for (var i = k, child; i < p; i++) {
+ child = node.children[i];
+ extend$1(destNode, node.leaf ? toBBox(child) : child);
+ }
+
+ return destNode;
+}
+
+function extend$1(a, b) {
+ a.minX = Math.min(a.minX, b.minX);
+ a.minY = Math.min(a.minY, b.minY);
+ a.maxX = Math.max(a.maxX, b.maxX);
+ a.maxY = Math.max(a.maxY, b.maxY);
+ return a;
+}
+
+function compareNodeMinX(a, b) { return a.minX - b.minX; }
+function compareNodeMinY(a, b) { return a.minY - b.minY; }
+
+function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
+function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
+
+function enlargedArea(a, b) {
+ return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
+ (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
+}
+
+function intersectionArea(a, b) {
+ var minX = Math.max(a.minX, b.minX),
+ minY = Math.max(a.minY, b.minY),
+ maxX = Math.min(a.maxX, b.maxX),
+ maxY = Math.min(a.maxY, b.maxY);
+
+ return Math.max(0, maxX - minX) *
+ Math.max(0, maxY - minY);
+}
+
+function contains$2(a, b) {
+ return a.minX <= b.minX &&
+ a.minY <= b.minY &&
+ b.maxX <= a.maxX &&
+ b.maxY <= a.maxY;
+}
+
+function intersects$1(a, b) {
+ return b.minX <= a.maxX &&
+ b.minY <= a.maxY &&
+ b.maxX >= a.minX &&
+ b.maxY >= a.minY;
+}
+
+function createNode(children) {
+ return {
+ children: children,
+ height: 1,
+ leaf: true,
+ minX: Infinity,
+ minY: Infinity,
+ maxX: -Infinity,
+ maxY: -Infinity
+ };
+}
+
+// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
+// combines selection algorithm with binary divide & conquer approach
+
+function multiSelect(arr, left, right, n, compare) {
+ var stack = [left, right],
+ mid;
+
+ while (stack.length) {
+ right = stack.pop();
+ left = stack.pop();
+
+ if (right - left <= n) continue;
+
+ mid = left + Math.ceil((right - left) / n / 2) * n;
+ index$10(arr, mid, left, right, compare);
+
+ stack.push(left, mid, mid, right);
+ }
+}
+
+function coreTree(head) {
+ var rtree = index$9(),
+ bboxes = {},
+ tree = {};
+
+
+ function entityBBox(entity) {
+ var bbox = entity.extent(head).bbox();
+ bbox.id = entity.id;
+ bboxes[entity.id] = bbox;
+ return bbox;
+ }
+
+
+ function updateParents(entity, insertions, memo) {
+ head.parentWays(entity).forEach(function(way) {
+ if (bboxes[way.id]) {
+ rtree.remove(bboxes[way.id]);
+ insertions[way.id] = way;
+ }
+ updateParents(way, insertions, memo);
+ });
+
+ head.parentRelations(entity).forEach(function(relation) {
+ if (memo[entity.id]) return;
+ memo[entity.id] = true;
+ if (bboxes[relation.id]) {
+ rtree.remove(bboxes[relation.id]);
+ insertions[relation.id] = relation;
+ }
+ updateParents(relation, insertions, memo);
+ });
+ }
+
+
+ tree.rebase = function(entities, force) {
+ var insertions = {};
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+
+ if (!entity.visible)
+ continue;
+
+ if (head.entities.hasOwnProperty(entity.id) || bboxes[entity.id]) {
+ if (!force) {
+ continue;
+ } else if (bboxes[entity.id]) {
+ rtree.remove(bboxes[entity.id]);
+ }
+ }
+
+ insertions[entity.id] = entity;
+ updateParents(entity, insertions, {});
+ }
+
+ rtree.load(lodash.map(insertions, entityBBox));
+
+ return tree;
+ };
+
+
+ tree.intersects = function(extent, graph) {
+ if (graph !== head) {
+ var diff = coreDifference(head, graph),
+ insertions = {};
+
+ head = graph;
+
+ diff.deleted().forEach(function(entity) {
+ rtree.remove(bboxes[entity.id]);
+ delete bboxes[entity.id];
+ });
+
+ diff.modified().forEach(function(entity) {
+ rtree.remove(bboxes[entity.id]);
+ insertions[entity.id] = entity;
+ updateParents(entity, insertions, {});
+ });
+
+ diff.created().forEach(function(entity) {
+ insertions[entity.id] = entity;
+ });
+
+ rtree.load(lodash.map(insertions, entityBBox));
+ }
+
+ return rtree.search(extent.bbox()).map(function(bbox) {
+ return head.entity(bbox.id);
+ });
+ };
+
+
+ return tree;
+}
+
+/*
+ * This code is licensed under the MIT license.
+ *
+ * Copyright © 2013, iD authors.
+ *
+ * Portions copyright © 2011, Keith Cirkel
+ * See https://github.com/keithamus/jwerty
+ *
+ */
+function d3keybinding(namespace$$1) {
+ var bindings = [];
+
+
+ function testBindings(isCapturing) {
+ var didMatch = false,
+ i, binding;
+
+ // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
+ // so we don't strictly match on the shift key, but we prioritize
+ // shifted bindings first, and fallback to unshifted only if no match.
+ // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
+
+ // priority match shifted bindings first
+ for (i = 0; i < bindings.length; i++) {
+ binding = bindings[i];
+ if (!binding.event.modifiers.shiftKey) continue; // no shift
+ if (!!binding.capture !== isCapturing) continue;
+ if (matches(binding, true)) {
+ binding.callback();
+ didMatch = true;
+ }
+ }
+
+ // then unshifted bindings
+ if (didMatch) return;
+ for (i = 0; i < bindings.length; i++) {
+ binding = bindings[i];
+ if (binding.event.modifiers.shiftKey) continue; // shift
+ if (!!binding.capture !== isCapturing) continue;
+ if (matches(binding, false)) {
+ binding.callback();
+ }
+ }
+
+
+ function matches(binding, testShift) {
+ var event$$1 = event;
+ if (event$$1.key !== undefined) {
+ if (binding.event.key === undefined) {
+ return false;
+ } else if (lodash.isArray(binding.event.key)) {
+ if (binding.event.key.map(function(s) { return s.toLowerCase(); }).indexOf(event$$1.key.toLowerCase()) === -1)
+ return false;
+ } else {
+ if (event$$1.key.toLowerCase() !== binding.event.key.toLowerCase())
+ return false;
+ }
+ } else {
+ // check keycodes if browser doesn't support KeyboardEvent.key
+ if (event$$1.keyCode !== binding.event.keyCode)
+ return false;
+ }
+
+ // test modifier keys
+ if (!(event$$1.ctrlKey && event$$1.altKey)) { // if both are set, assume AltGr and skip it - #4096
+ if (event$$1.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
+ if (event$$1.altKey !== binding.event.modifiers.altKey) return false;
+ }
+ if (event$$1.metaKey !== binding.event.modifiers.metaKey) return false;
+ if (testShift && event$$1.shiftKey !== binding.event.modifiers.shiftKey) return false;
+
+ return true;
+ }
+ }
+
+
+ function capture() {
+ testBindings(true);
+ }
+
+
+ function bubble() {
+ var tagName = select(event.target).node().tagName;
+ if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
+ return;
+ }
+ testBindings(false);
+ }
+
+
+ function keybinding(selection$$1) {
+ selection$$1 = selection$$1 || select(document);
+ selection$$1.on('keydown.capture' + namespace$$1, capture, true);
+ selection$$1.on('keydown.bubble' + namespace$$1, bubble, false);
+ return keybinding;
+ }
+
+
+ keybinding.off = function(selection$$1) {
+ bindings = [];
+ selection$$1 = selection$$1 || select(document);
+ selection$$1.on('keydown.capture' + namespace$$1, null);
+ selection$$1.on('keydown.bubble' + namespace$$1, null);
+ return keybinding;
+ };
+
+
+ keybinding.on = function(codes, callback, capture) {
+ var arr = [].concat(codes);
+ for (var i = 0; i < arr.length; i++) {
+ var code = arr[i];
+ var binding = {
+ event: {
+ key: undefined,
+ keyCode: 0, // only for browsers that don't support KeyboardEvent.key
+ modifiers: {
+ shiftKey: false,
+ ctrlKey: false,
+ altKey: false,
+ metaKey: false
+ }
+ },
+ capture: capture,
+ callback: callback
+ };
+
+ code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
+
+ for (var j = 0; j < code.length; j++) {
+ // Normalise matching errors
+ if (code[j] === '++') code[j] = '+';
+
+ if (code[j] in d3keybinding.modifierCodes) {
+ binding.event.modifiers[d3keybinding.modifierProperties[d3keybinding.modifierCodes[code[j]]]] = true;
+ } else {
+ binding.event.key = d3keybinding.keys[code[j]] || code[j];
+ if (code[j] in d3keybinding.keyCodes) {
+ binding.event.keyCode = d3keybinding.keyCodes[code[j]];
+ }
+ }
+ }
+
+ bindings.push(binding);
+ }
+
+ return keybinding;
+ };
+
+ return keybinding;
+}
+
+
+d3keybinding.modifierCodes = {
+ // Shift key, ⇧
+ '⇧': 16, shift: 16,
+ // CTRL key, on Mac: ⌃
+ '⌃': 17, ctrl: 17,
+ // ALT key, on Mac: ⌥ (Alt)
+ '⌥': 18, alt: 18, option: 18,
+ // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
+ '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
+};
+
+d3keybinding.modifierProperties = {
+ 16: 'shiftKey',
+ 17: 'ctrlKey',
+ 18: 'altKey',
+ 91: 'metaKey'
+};
+
+d3keybinding.keys = {
+ // Backspace key, on Mac: ⌫ (Backspace)
+ '⌫': 'Backspace', backspace: 'Backspace',
+ // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
+ '⇥': 'Tab', '⇆': 'Tab', tab: 'Tab',
+ // Return key, ↩
+ '↩': 'Enter', 'return': 'Enter', enter: 'Enter', '⌅': 'Enter',
+ // Pause/Break key
+ 'pause': 'Pause', 'pause-break': 'Pause',
+ // Caps Lock key, ⇪
+ '⇪': 'CapsLock', caps: 'CapsLock', 'caps-lock': 'CapsLock',
+ // Escape key, on Mac: ⎋, on Windows: Esc
+ '⎋': ['Escape', 'Esc'], escape: ['Escape', 'Esc'], esc: ['Escape', 'Esc'],
+ // Space key
+ space: [' ', 'Spacebar'],
+ // Page-Up key, or pgup, on Mac: ↖
+ '↖': 'PageUp', pgup: 'PageUp', 'page-up': 'PageUp',
+ // Page-Down key, or pgdown, on Mac: ↘
+ '↘': 'PageDown', pgdown: 'PageDown', 'page-down': 'PageDown',
+ // END key, on Mac: ⇟
+ '⇟': 'End', end: 'End',
+ // HOME key, on Mac: ⇞
+ '⇞': 'Home', home: 'Home',
+ // Insert key, or ins
+ ins: 'Insert', insert: 'Insert',
+ // Delete key, on Mac: ⌦ (Delete)
+ '⌦': ['Delete', 'Del'], del: ['Delete', 'Del'], 'delete': ['Delete', 'Del'],
+ // Left Arrow Key, or ←
+ '←': ['ArrowLeft', 'Left'], left: ['ArrowLeft', 'Left'], 'arrow-left': ['ArrowLeft', 'Left'],
+ // Up Arrow Key, or ↑
+ '↑': ['ArrowUp', 'Up'], up: ['ArrowUp', 'Up'], 'arrow-up': ['ArrowUp', 'Up'],
+ // Right Arrow Key, or →
+ '→': ['ArrowRight', 'Right'], right: ['ArrowRight', 'Right'], 'arrow-right': ['ArrowRight', 'Right'],
+ // Up Arrow Key, or ↓
+ '↓': ['ArrowDown', 'Down'], down: ['ArrowDown', 'Down'], 'arrow-down': ['ArrowDown', 'Down'],
+ // odities, stuff for backward compatibility (browsers and code):
+ // Num-Multiply, or *
+ '*': ['*', 'Multiply'], star: ['*', 'Multiply'], asterisk: ['*', 'Multiply'], multiply: ['*', 'Multiply'],
+ // Num-Plus or +
+ '+': ['+', 'Add'], 'plus': ['+', 'Add'],
+ // Num-Subtract, or -
+ '-': ['-', 'Subtract'], subtract: ['-', 'Subtract'], 'dash': ['-', 'Subtract'],
+ // Semicolon
+ semicolon: ';',
+ // = or equals
+ equals: '=',
+ // Comma, or ,
+ comma: ',',
+ // Period, or ., or full-stop
+ period: '.', 'full-stop': '.',
+ // Slash, or /, or forward-slash
+ slash: '/', 'forward-slash': '/',
+ // Tick, or `, or back-quote
+ tick: '`', 'back-quote': '`',
+ // Open bracket, or [
+ 'open-bracket': '[',
+ // Back slash, or \
+ 'back-slash': '\\',
+ // Close backet, or ]
+ 'close-bracket': ']',
+ // Apostrophe, or Quote, or '
+ quote: '\'', apostrophe: '\'',
+ // NUMPAD 0-9
+ 'num-0': '0',
+ 'num-1': '1',
+ 'num-2': '2',
+ 'num-3': '3',
+ 'num-4': '4',
+ 'num-5': '5',
+ 'num-6': '6',
+ 'num-7': '7',
+ 'num-8': '8',
+ 'num-9': '9',
+ // F1-F25
+ f1: 'F1',
+ f2: 'F2',
+ f3: 'F3',
+ f4: 'F4',
+ f5: 'F5',
+ f6: 'F6',
+ f7: 'F7',
+ f8: 'F8',
+ f9: 'F9',
+ f10: 'F10',
+ f11: 'F11',
+ f12: 'F12',
+ f13: 'F13',
+ f14: 'F14',
+ f15: 'F15',
+ f16: 'F16',
+ f17: 'F17',
+ f18: 'F18',
+ f19: 'F19',
+ f20: 'F20',
+ f21: 'F21',
+ f22: 'F22',
+ f23: 'F23',
+ f24: 'F24',
+ f25: 'F25'
+};
+
+d3keybinding.keyCodes = {
+ // Backspace key, on Mac: ⌫ (Backspace)
+ '⌫': 8, backspace: 8,
+ // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
+ '⇥': 9, '⇆': 9, tab: 9,
+ // Return key, ↩
+ '↩': 13, 'return': 13, enter: 13, '⌅': 13,
+ // Pause/Break key
+ 'pause': 19, 'pause-break': 19,
+ // Caps Lock key, ⇪
+ '⇪': 20, caps: 20, 'caps-lock': 20,
+ // Escape key, on Mac: ⎋, on Windows: Esc
+ '⎋': 27, escape: 27, esc: 27,
+ // Space key
+ space: 32,
+ // Page-Up key, or pgup, on Mac: ↖
+ '↖': 33, pgup: 33, 'page-up': 33,
+ // Page-Down key, or pgdown, on Mac: ↘
+ '↘': 34, pgdown: 34, 'page-down': 34,
+ // END key, on Mac: ⇟
+ '⇟': 35, end: 35,
+ // HOME key, on Mac: ⇞
+ '⇞': 36, home: 36,
+ // Insert key, or ins
+ ins: 45, insert: 45,
+ // Delete key, on Mac: ⌦ (Delete)
+ '⌦': 46, del: 46, 'delete': 46,
+ // Left Arrow Key, or ←
+ '←': 37, left: 37, 'arrow-left': 37,
+ // Up Arrow Key, or ↑
+ '↑': 38, up: 38, 'arrow-up': 38,
+ // Right Arrow Key, or →
+ '→': 39, right: 39, 'arrow-right': 39,
+ // 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,
+ // = or equals
+ '=': 187, 'equals': 187,
+ // Comma, or ,
+ ',': 188, comma: 188,
+ // Dash / Underscore key
+ 'dash': 189,
+ // Period, or ., or full-stop
+ '.': 190, period: 190, 'full-stop': 190,
+ // Slash, or /, or forward-slash
+ '/': 191, slash: 191, 'forward-slash': 191,
+ // Tick, or `, or back-quote
+ '`': 192, tick: 192, 'back-quote': 192,
+ // Open bracket, or [
+ '[': 219, 'open-bracket': 219,
+ // Back slash, or \
+ '\\': 220, 'back-slash': 220,
+ // Close backet, or ]
+ ']': 221, 'close-bracket': 221,
+ // Apostrophe, or Quote, or '
+ '\'': 222, quote: 222, apostrophe: 222
+};
+
+// NUMPAD 0-9
+var i$1 = 95;
+var n = 0;
+while (++i$1 < 106) {
+ d3keybinding.keyCodes['num-' + n] = i$1;
+ ++n;
+}
+
+// 0-9
+i$1 = 47; n = 0;
+while (++i$1 < 58) {
+ d3keybinding.keyCodes[n] = i$1;
+ ++n;
+}
+
+// F1-F25
+i$1 = 111; n = 1;
+while (++i$1 < 136) {
+ d3keybinding.keyCodes['f' + n] = i$1;
+ ++n;
+}
+
+// a-z
+i$1 = 64;
+while (++i$1 < 91) {
+ d3keybinding.keyCodes[String.fromCharCode(i$1).toLowerCase()] = i$1;
+}
+
+function tooltip() {
+ var tooltip = function(selection$$1) {
+ selection$$1.each(setup);
+ },
+ animation = utilFunctor(false),
+ html$$1 = utilFunctor(false),
+ title = function() {
+ var title = this.getAttribute('data-original-title');
+ if (title) {
+ return title;
+ } else {
+ title = this.getAttribute('title');
+ this.removeAttribute('title');
+ this.setAttribute('data-original-title', title);
+ }
+ return title;
+ },
+ over = 'mouseenter.tooltip',
+ out = 'mouseleave.tooltip',
+ placement = utilFunctor('top');
+
+
+ tooltip.title = function(_) {
+ if (arguments.length) {
+ title = utilFunctor(_);
+ return tooltip;
+ } else {
+ return title;
+ }
+ };
+
+
+ tooltip.html = function(_) {
+ if (arguments.length) {
+ html$$1 = utilFunctor(_);
+ return tooltip;
+ } else {
+ return html$$1;
+ }
+ };
+
+
+ tooltip.placement = function(_) {
+ if (arguments.length) {
+ placement = utilFunctor(_);
+ return tooltip;
+ } else {
+ return placement;
+ }
+ };
+
+
+ tooltip.show = function(selection$$1) {
+ selection$$1.each(show);
+ };
+
+
+ tooltip.hide = function(selection$$1) {
+ selection$$1.each(hide);
+ };
+
+
+ tooltip.toggle = function(selection$$1) {
+ selection$$1.each(toggle);
+ };
+
+
+ tooltip.destroy = function(selection$$1) {
+ selection$$1
+ .on(over, null)
+ .on(out, null)
+ .attr('title', function() {
+ return this.getAttribute('data-original-title') || this.getAttribute('title');
+ })
+ .attr('data-original-title', null)
+ .selectAll('.tooltip')
+ .remove();
+ };
+
+
+ function setup() {
+ var root = select(this),
+ animate = animation.apply(this, arguments),
+ tip = root.selectAll('.tooltip').data([0]);
+
+ var enter = tip.enter()
+ .append('div')
+ .attr('class', 'tooltip');
+
+ enter
+ .append('div')
+ .attr('class', 'tooltip-arrow');
+
+ enter
+ .append('div')
+ .attr('class', 'tooltip-inner');
+
+ tip = enter
+ .merge(tip);
+
+ if (animate) {
+ tip.classed('fade', true);
+ }
+
+ var place = placement.apply(this, arguments);
+ tip.classed(place, true);
+
+ root.on(over, show);
+ root.on(out, hide);
+ }
+
+
+ function show() {
+ var root = select(this),
+ content = title.apply(this, arguments),
+ tip = root.selectAll('.tooltip')
+ .classed('in', true),
+ markup = html$$1.apply(this, arguments);
+
+ tip.selectAll('.tooltip-inner')[markup ? 'html' : 'text'](content);
+ var place = placement.apply(this, arguments),
+ outer = getPosition(root.node()),
+ inner = getPosition(tip.node()),
+ pos;
+
+ switch (place) {
+ case 'top':
+ pos = {x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h};
+ break;
+ case 'right':
+ pos = {x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2};
+ break;
+ case 'left':
+ pos = {x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2};
+ break;
+ case 'bottom':
+ pos = {x: Math.max(0, outer.x + (outer.w - inner.w) / 2), y: outer.y + outer.h};
+ break;
+ }
+
+ if (pos) {
+ tip.style('left', ~~pos.x + 'px').style('top', ~~pos.y + 'px');
+ } else {
+ tip.style('left', null).style('top', null);
+ }
+
+ this.tooltipVisible = true;
+ }
+
+
+ function hide() {
+ select(this).selectAll('.tooltip')
+ .classed('in', false);
+
+ this.tooltipVisible = false;
+ }
+
+
+ function toggle() {
+ if (this.tooltipVisible) {
+ hide.apply(this, arguments);
+ } else {
+ show.apply(this, arguments);
+ }
+ }
+
+ return tooltip;
+}
+
+
+function getPosition(node) {
+ var mode = select(node).style('position');
+ if (mode === 'absolute' || mode === 'static') {
+ return {
+ x: node.offsetLeft,
+ y: node.offsetTop,
+ w: node.offsetWidth,
+ h: node.offsetHeight
+ };
+ } else {
+ return {
+ x: 0,
+ y: 0,
+ w: node.offsetWidth,
+ h: node.offsetHeight
+ };
+ }
+}
+
+function svgAreas$$1(projection$$1, context) {
+ // Patterns only work in Firefox when set directly on element.
+ // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
+ var patterns = {
+ beach: 'beach',
+ cemetery: 'cemetery',
+ construction: 'construction',
+ farm: 'farmland',
+ farmland: 'farmland',
+ grave_yard: 'cemetery',
+ meadow: 'meadow',
+ military: 'construction',
+ orchard: 'orchard',
+ sand: 'beach',
+ scrub: 'scrub',
+ wetland: 'wetland',
+ };
+
+ var patternKeys = ['landuse', 'natural', 'amenity'];
+
+
+ function setPattern(d) {
+ for (var i = 0; i < patternKeys.length; i++) {
+ if (d.tags.building && d.tags.building !== 'no') continue;
+
+ if (patterns.hasOwnProperty(d.tags[patternKeys[i]])) {
+ this.style.fill = this.style.stroke = 'url("#pattern-' + patterns[d.tags[patternKeys[i]]] + '")';
+ return;
+ }
+ }
+ this.style.fill = this.style.stroke = '';
+ }
+
+
+ return function drawAreas(selection$$1, graph, entities, filter) {
+ var path$$1 = svgPath(projection$$1, graph, true),
+ areas = {},
+ multipolygon;
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+ if (entity.geometry(graph) !== 'area') continue;
+
+ multipolygon = osmIsSimpleMultipolygonOuterMember(entity, graph);
+ if (multipolygon) {
+ areas[multipolygon.id] = {
+ entity: multipolygon.mergeTags(entity.tags),
+ area: Math.abs(entity.area(graph))
+ };
+ } else if (!areas[entity.id]) {
+ areas[entity.id] = {
+ entity: entity,
+ area: Math.abs(entity.area(graph))
+ };
+ }
+ }
+
+ areas = values(areas).filter(function hasPath(a) { return path$$1(a.entity); });
+ areas.sort(function areaSort(a, b) { return b.area - a.area; });
+ areas = lodash.map(areas, 'entity');
+
+ var strokes = areas.filter(function(area$$1) {
+ return area$$1.type === 'way';
+ });
+
+ var data = {
+ clip: areas,
+ shadow: strokes,
+ stroke: strokes,
+ fill: areas
+ };
+
+ var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath')
+ .filter(filter)
+ .data(data.clip, osmEntity$$1.key);
+
+ clipPaths.exit()
+ .remove();
+
+ var clipPathsEnter = clipPaths.enter()
+ .append('clipPath')
+ .attr('class', 'clipPath')
+ .attr('id', function(entity) { return entity.id + '-clippath'; });
+
+ clipPathsEnter
+ .append('path');
+
+ clipPaths.merge(clipPathsEnter)
+ .selectAll('path')
+ .attr('d', path$$1);
+
+
+ var layer = selection$$1.selectAll('.layer-areas');
+
+ var areagroup = layer
+ .selectAll('g.areagroup')
+ .data(['fill', 'shadow', 'stroke']);
+
+ areagroup = areagroup.enter()
+ .append('g')
+ .attr('class', function(d) { return 'areagroup area-' + d; })
+ .merge(areagroup);
+
+ var paths = areagroup
+ .selectAll('path')
+ .filter(filter)
+ .data(function(layer) { return data[layer]; }, osmEntity$$1.key);
+
+ paths.exit()
+ .remove();
+
+ var fills = selection$$1.selectAll('.area-fill path.area').nodes();
+
+ var bisect = bisector(function(node) {
+ return -node.__data__.area(graph);
+ }).left;
+
+ function sortedByArea(entity) {
+ if (this._parent.__data__ === 'fill') {
+ return fills[bisect(fills, -entity.area(graph))];
+ }
+ }
+
+ paths = paths.enter()
+ .insert('path', sortedByArea)
+ .merge(paths)
+ .each(function(entity) {
+ var layer = this.parentNode.__data__;
+
+ this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
+
+ if (layer === 'fill') {
+ this.setAttribute('clip-path', 'url(#' + entity.id + '-clippath)');
+ setPattern.apply(this, arguments);
+ }
+ })
+ .call(svgTagClasses())
+ .attr('d', path$$1);
+ };
+}
+
+function svgDebug(projection$$1, context) {
+
+ function multipolygons(imagery) {
+ return imagery.map(function(data$$1) {
+ return {
+ type: 'MultiPolygon',
+ coordinates: [ data$$1.polygon ]
+ };
+ });
+ }
+
+ function drawDebug(selection$$1) {
+ var showsTile = context.getDebug('tile'),
+ showsCollision = context.getDebug('collision'),
+ showsImagery = context.getDebug('imagery'),
+ showsImperial = context.getDebug('imperial'),
+ showsDriveLeft = context.getDebug('driveLeft'),
+ path$$1 = index$4(projection$$1);
+
+
+ var debugData = [];
+ if (showsTile) {
+ debugData.push({ class: 'red', label: 'tile' });
+ }
+ if (showsCollision) {
+ debugData.push({ class: 'yellow', label: 'collision' });
+ }
+ if (showsImagery) {
+ debugData.push({ class: 'orange', label: 'imagery' });
+ }
+ if (showsImperial) {
+ debugData.push({ class: 'cyan', label: 'imperial' });
+ }
+ if (showsDriveLeft) {
+ debugData.push({ class: 'green', label: 'driveLeft' });
+ }
+
+
+ var legend = select('#content')
+ .selectAll('.debug-legend')
+ .data(debugData.length ? [0] : []);
+
+ legend.exit()
+ .remove();
+
+ legend = legend.enter()
+ .append('div')
+ .attr('class', 'fillD debug-legend')
+ .merge(legend);
+
+
+ var legendItems = legend.selectAll('.debug-legend-item')
+ .data(debugData, function(d) { return d.label; });
+
+ legendItems.exit()
+ .remove();
+
+ legendItems.enter()
+ .append('span')
+ .attr('class', function(d) { return 'debug-legend-item ' + d.class; })
+ .text(function(d) { return d.label; });
+
+
+ var layer = selection$$1.selectAll('.layer-debug')
+ .data(showsImagery || showsImperial || showsDriveLeft ? [0] : []);
+
+ layer.exit()
+ .remove();
+
+ layer = layer.enter()
+ .append('g')
+ .attr('class', 'layer-debug')
+ .merge(layer);
+
+
+ var extent$$1 = context.map().extent(),
+ dataImagery = data.imagery || [],
+ availableImagery = showsImagery && multipolygons(dataImagery.filter(function(source) {
+ if (!source.polygon) return false;
+ return source.polygon.some(function(polygon) {
+ return geoPolygonIntersectsPolygon(polygon, extent$$1, true);
+ });
+ }));
+
+ var imagery = layer.selectAll('path.debug-imagery')
+ .data(showsImagery ? availableImagery : []);
+
+ imagery.exit()
+ .remove();
+
+ imagery.enter()
+ .append('path')
+ .attr('class', 'debug-imagery debug orange');
+
+
+ var imperial = layer
+ .selectAll('path.debug-imperial')
+ .data(showsImperial ? [dataImperial] : []);
+
+ imperial.exit()
+ .remove();
+
+ imperial.enter()
+ .append('path')
+ .attr('class', 'debug-imperial debug cyan');
+
+
+ var driveLeft = layer
+ .selectAll('path.debug-drive-left')
+ .data(showsDriveLeft ? [dataDriveLeft] : []);
+
+ driveLeft.exit()
+ .remove();
+
+ driveLeft.enter()
+ .append('path')
+ .attr('class', 'debug-drive-left debug green');
+
+
+ // update
+ layer.selectAll('path')
+ .attr('d', path$$1);
+ }
+
+
+ // This looks strange because `enabled` methods on other layers are
+ // chainable getter/setters, and this one is just a getter.
+ drawDebug.enabled = function() {
+ if (!arguments.length) {
+ return context.getDebug('tile') ||
+ context.getDebug('collision') ||
+ context.getDebug('imagery') ||
+ context.getDebug('imperial') ||
+ context.getDebug('driveLeft');
+ } else {
+ return this;
+ }
+ };
+
+
+ return drawDebug;
+}
+
+/*
+ A standalone SVG element that contains only a `defs` sub-element. To be
+ used once globally, since defs IDs must be unique within a document.
+*/
+function svgDefs(context) {
+
+ function SVGSpriteDefinition(id, href) {
+ return function(defs) {
+ request(href)
+ .mimeType('image/svg+xml')
+ .response(function(xhr) { return xhr.responseXML; })
+ .get(function(err, svg) {
+ if (err) return;
+ defs.node().appendChild(
+ select(svg.documentElement).attr('id', id).node()
+ );
+ });
+ };
+ }
+
+
+ return function drawDefs(selection$$1) {
+ var defs = selection$$1.append('defs');
+
+ // marker
+ defs.append('marker')
+ .attr('id', 'oneway-marker')
+ .attr('viewBox', '0 0 10 10')
+ .attr('refY', 2.5)
+ .attr('refX', 5)
+ .attr('markerWidth', 2)
+ .attr('markerHeight', 2)
+ .attr('markerUnits', 'strokeWidth')
+ .attr('orient', 'auto')
+
+ .append('path')
+ .attr('class', 'oneway')
+ .attr('d', 'M 5 3 L 0 3 L 0 2 L 5 2 L 5 0 L 10 2.5 L 5 5 z')
+ .attr('stroke', 'none')
+ .attr('fill', '#000')
+ .attr('opacity', '0.75');
+
+ // patterns
+ var patterns = defs.selectAll('pattern')
+ .data([
+ // pattern name, pattern image name
+ ['wetland', 'wetland'],
+ ['construction', 'construction'],
+ ['cemetery', 'cemetery'],
+ ['orchard', 'orchard'],
+ ['farmland', 'farmland'],
+ ['beach', 'dots'],
+ ['scrub', 'dots'],
+ ['meadow', 'dots']
+ ])
+ .enter()
+ .append('pattern')
+ .attr('id', function (d) {
+ return 'pattern-' + d[0];
+ })
+ .attr('width', 32)
+ .attr('height', 32)
+ .attr('patternUnits', 'userSpaceOnUse');
+
+ patterns.append('rect')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('width', 32)
+ .attr('height', 32)
+ .attr('class', function (d) {
+ return 'pattern-color-' + d[0];
+ });
+
+ patterns.append('image')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('width', 32)
+ .attr('height', 32)
+ .attr('xlink:href', function (d) {
+ return context.imagePath('pattern/' + d[1] + '.png');
+ });
+
+ // clip paths
+ defs.selectAll()
+ .data([12, 18, 20, 32, 45])
+ .enter()
+ .append('clipPath')
+ .attr('id', function (d) {
+ return 'clip-square-' + d;
+ })
+ .append('rect')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('width', function (d) {
+ return d;
+ })
+ .attr('height', function (d) {
+ return d;
+ });
+
+ defs.call(SVGSpriteDefinition(
+ 'iD-sprite',
+ context.imagePath('iD-sprite.svg')));
+
+ defs.call(SVGSpriteDefinition(
+ 'maki-sprite',
+ context.imagePath('maki-sprite.svg')));
+ };
+}
+
+//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
+//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
+//[5] Name ::= NameStartChar (NameChar)*
+var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/;//\u10000-\uEFFFF
+var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
+var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
+//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
+//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
+
+//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
+//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
+var S_TAG = 0;//tag name offerring
+var S_ATTR = 1;//attr name offerring
+var S_ATTR_SPACE=2;//attr name end and space offer
+var S_EQ = 3;//=space?
+var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
+var S_ATTR_END = 5;//attr value end and no space(quot end)
+var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
+var S_TAG_CLOSE = 7;//closed el<el />
+
+function XMLReader(){
+
+}
+
+XMLReader.prototype = {
+ parse:function(source,defaultNSMap,entityMap){
+ var domBuilder = this.domBuilder;
+ domBuilder.startDocument();
+ _copy(defaultNSMap ,defaultNSMap = {});
+ parse(source,defaultNSMap,entityMap,
+ domBuilder,this.errorHandler);
+ domBuilder.endDocument();
+ }
+};
+function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
+ function fixedFromCharCode(code) {
+ // String.prototype.fromCharCode does not supports
+ // > 2 bytes unicode chars directly
+ if (code > 0xffff) {
+ code -= 0x10000;
+ var surrogate1 = 0xd800 + (code >> 10)
+ , surrogate2 = 0xdc00 + (code & 0x3ff);
+
+ return String.fromCharCode(surrogate1, surrogate2);
+ } else {
+ return String.fromCharCode(code);
+ }
+ }
+ function entityReplacer(a){
+ var k = a.slice(1,-1);
+ if(k in entityMap){
+ return entityMap[k];
+ }else if(k.charAt(0) === '#'){
+ return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
+ }else{
+ errorHandler.error('entity not found:'+a);
+ return a;
+ }
+ }
+ function appendText(end){//has some bugs
+ if(end>start){
+ var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
+ locator&&position(start);
+ domBuilder.characters(xt,0,end-start);
+ start = end;
+ }
+ }
+ function position(p,m){
+ while(p>=lineEnd && (m = linePattern.exec(source))){
+ lineStart = m.index;
+ lineEnd = lineStart + m[0].length;
+ locator.lineNumber++;
+ //console.log('line++:',locator,startPos,endPos)
+ }
+ locator.columnNumber = p-lineStart+1;
+ }
+ var lineStart = 0;
+ var lineEnd = 0;
+ var linePattern = /.*(?:\r\n?|\n)|.*$/g;
+ var locator = domBuilder.locator;
+
+ var parseStack = [{currentNSMap:defaultNSMapCopy}];
+ var closeMap = {};
+ var start = 0;
+ while(true){
+ try{
+ var tagStart = source.indexOf('<',start);
+ if(tagStart<0){
+ if(!source.substr(start).match(/^\s*$/)){
+ var doc = domBuilder.doc;
+ var text = doc.createTextNode(source.substr(start));
+ doc.appendChild(text);
+ domBuilder.currentElement = text;
+ }
+ return;
+ }
+ if(tagStart>start){
+ appendText(tagStart);
+ }
+ switch(source.charAt(tagStart+1)){
+ case '/':
+ var end = source.indexOf('>',tagStart+3);
+ var tagName = source.substring(tagStart+2,end);
+ var config = parseStack.pop();
+ if(end<0){
+
+ tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
+ //console.error('#@@@@@@'+tagName)
+ errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
+ end = tagStart+1+tagName.length;
+ }else if(tagName.match(/\s</)){
+ tagName = tagName.replace(/[\s<].*/,'');
+ errorHandler.error("end tag name: "+tagName+' maybe not complete');
+ end = tagStart+1+tagName.length;
+ }
+ //console.error(parseStack.length,parseStack)
+ //console.error(config);
+ var localNSMap = config.localNSMap;
+ var endMatch = config.tagName == tagName;
+ var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase();
+ if(endIgnoreCaseMach){
+ domBuilder.endElement(config.uri,config.localName,tagName);
+ if(localNSMap){
+ for(var prefix in localNSMap){
+ domBuilder.endPrefixMapping(prefix) ;
+ }
+ }
+ if(!endMatch){
+ errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
+ }
+ }else{
+ parseStack.push(config);
+ }
+
+ end++;
+ break;
+ // end elment
+ case '?':// <?...?>
+ locator&&position(tagStart);
+ end = parseInstruction(source,tagStart,domBuilder);
+ break;
+ case '!':// <!doctype,<![CDATA,<!--
+ locator&&position(tagStart);
+ end = parseDCC(source,tagStart,domBuilder,errorHandler);
+ break;
+ default:
+ locator&&position(tagStart);
+ var el = new ElementAttributes();
+ var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
+ //elStartEnd
+ var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
+ var len = el.length;
+
+
+ if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
+ el.closed = true;
+ if(!entityMap.nbsp){
+ errorHandler.warning('unclosed xml attribute');
+ }
+ }
+ if(locator && len){
+ var locator2 = copyLocator(locator,{});
+ //try{//attribute position fixed
+ for(var i = 0;i<len;i++){
+ var a = el[i];
+ position(a.offset);
+ a.locator = copyLocator(locator,{});
+ }
+ //}catch(e){console.error('@@@@@'+e)}
+ domBuilder.locator = locator2;
+ if(appendElement(el,domBuilder,currentNSMap)){
+ parseStack.push(el);
+ }
+ domBuilder.locator = locator;
+ }else{
+ if(appendElement(el,domBuilder,currentNSMap)){
+ parseStack.push(el);
+ }
+ }
+
+
+
+ if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
+ end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder);
+ }else{
+ end++;
+ }
+ }
+ }catch(e){
+ errorHandler.error('element parse error: '+e);
+ //errorHandler.error('element parse error: '+e);
+ end = -1;
+ //throw e;
+ }
+ if(end>start){
+ start = end;
+ }else{
+ //TODO: 这里有可能sax回退,有位置错误风险
+ appendText(Math.max(tagStart,start)+1);
+ }
+ }
+}
+function copyLocator(f,t){
+ t.lineNumber = f.lineNumber;
+ t.columnNumber = f.columnNumber;
+ return t;
+}
+
+/**
+ * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
+ * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
+ */
+function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
+ var attrName;
+ var value;
+ var p = ++start;
+ var s = S_TAG;//status
+ while(true){
+ var c = source.charAt(p);
+ switch(c){
+ case '=':
+ if(s === S_ATTR){//attrName
+ attrName = source.slice(start,p);
+ s = S_EQ;
+ }else if(s === S_ATTR_SPACE){
+ s = S_EQ;
+ }else{
+ //fatalError: equal must after attrName or space after attrName
+ throw new Error('attribute equal must after attrName');
+ }
+ break;
+ case '\'':
+ case '"':
+ if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
+ ){//equal
+ if(s === S_ATTR){
+ errorHandler.warning('attribute value must after "="');
+ attrName = source.slice(start,p);
+ }
+ start = p+1;
+ p = source.indexOf(c,start);
+ if(p>0){
+ value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+ el.add(attrName,value,start-1);
+ s = S_ATTR_END;
+ }else{
+ //fatalError: no end quot match
+ throw new Error('attribute value no end \''+c+'\' match');
+ }
+ }else if(s == S_ATTR_NOQUOT_VALUE){
+ value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+ //console.log(attrName,value,start,p)
+ el.add(attrName,value,start);
+ //console.dir(el)
+ errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
+ start = p+1;
+ s = S_ATTR_END;
+ }else{
+ //fatalError: no equal before
+ throw new Error('attribute value must after "="');
+ }
+ break;
+ case '/':
+ switch(s){
+ case S_TAG:
+ el.setTagName(source.slice(start,p));
+ case S_ATTR_END:
+ case S_TAG_SPACE:
+ case S_TAG_CLOSE:
+ s =S_TAG_CLOSE;
+ el.closed = true;
+ case S_ATTR_NOQUOT_VALUE:
+ case S_ATTR:
+ case S_ATTR_SPACE:
+ break;
+ //case S_EQ:
+ default:
+ throw new Error("attribute invalid close char('/')")
+ }
+ break;
+ case ''://end document
+ //throw new Error('unexpected end of input')
+ errorHandler.error('unexpected end of input');
+ if(s == S_TAG){
+ el.setTagName(source.slice(start,p));
+ }
+ return p;
+ case '>':
+ switch(s){
+ case S_TAG:
+ el.setTagName(source.slice(start,p));
+ case S_ATTR_END:
+ case S_TAG_SPACE:
+ case S_TAG_CLOSE:
+ break;//normal
+ case S_ATTR_NOQUOT_VALUE://Compatible state
+ case S_ATTR:
+ value = source.slice(start,p);
+ if(value.slice(-1) === '/'){
+ el.closed = true;
+ value = value.slice(0,-1);
+ }
+ case S_ATTR_SPACE:
+ if(s === S_ATTR_SPACE){
+ value = attrName;
+ }
+ if(s == S_ATTR_NOQUOT_VALUE){
+ errorHandler.warning('attribute "'+value+'" missed quot(")!!');
+ el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start);
+ }else{
+ if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
+ errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!');
+ }
+ el.add(value,value,start);
+ }
+ break;
+ case S_EQ:
+ throw new Error('attribute value missed!!');
+ }
+// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
+ return p;
+ /*xml space '\x20' | #x9 | #xD | #xA; */
+ case '\u0080':
+ c = ' ';
+ default:
+ if(c<= ' '){//space
+ switch(s){
+ case S_TAG:
+ el.setTagName(source.slice(start,p));//tagName
+ s = S_TAG_SPACE;
+ break;
+ case S_ATTR:
+ attrName = source.slice(start,p);
+ s = S_ATTR_SPACE;
+ break;
+ case S_ATTR_NOQUOT_VALUE:
+ var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+ errorHandler.warning('attribute "'+value+'" missed quot(")!!');
+ el.add(attrName,value,start);
+ case S_ATTR_END:
+ s = S_TAG_SPACE;
+ break;
+ //case S_TAG_SPACE:
+ //case S_EQ:
+ //case S_ATTR_SPACE:
+ // void();break;
+ //case S_TAG_CLOSE:
+ //ignore warning
+ }
+ }else{//not space
+//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
+//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
+ switch(s){
+ //case S_TAG:void();break;
+ //case S_ATTR:void();break;
+ //case S_ATTR_NOQUOT_VALUE:void();break;
+ case S_ATTR_SPACE:
+ var tagName = el.tagName;
+ if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
+ errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!');
+ }
+ el.add(attrName,attrName,start);
+ start = p;
+ s = S_ATTR;
+ break;
+ case S_ATTR_END:
+ errorHandler.warning('attribute space is required"'+attrName+'"!!');
+ case S_TAG_SPACE:
+ s = S_ATTR;
+ start = p;
+ break;
+ case S_EQ:
+ s = S_ATTR_NOQUOT_VALUE;
+ start = p;
+ break;
+ case S_TAG_CLOSE:
+ throw new Error("elements closed character '/' and '>' must be connected to");
+ }
+ }
+ }//end outer switch
+ //console.log('p++',p)
+ p++;
+ }
+}
+/**
+ * @return true if has new namespace define
+ */
+function appendElement(el,domBuilder,currentNSMap){
+ var tagName = el.tagName;
+ var localNSMap = null;
+ //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
+ var i = el.length;
+ while(i--){
+ var a = el[i];
+ var qName = a.qName;
+ var value = a.value;
+ var nsp = qName.indexOf(':');
+ if(nsp>0){
+ var prefix = a.prefix = qName.slice(0,nsp);
+ var localName = qName.slice(nsp+1);
+ var nsPrefix = prefix === 'xmlns' && localName;
+ }else{
+ localName = qName;
+ prefix = null;
+ nsPrefix = qName === 'xmlns' && '';
+ }
+ //can not set prefix,because prefix !== ''
+ a.localName = localName ;
+ //prefix == null for no ns prefix attribute
+ if(nsPrefix !== false){//hack!!
+ if(localNSMap == null){
+ localNSMap = {};
+ //console.log(currentNSMap,0)
+ _copy(currentNSMap,currentNSMap={});
+ //console.log(currentNSMap,1)
+ }
+ currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
+ a.uri = 'http://www.w3.org/2000/xmlns/';
+ domBuilder.startPrefixMapping(nsPrefix, value);
+ }
+ }
+ var i = el.length;
+ while(i--){
+ a = el[i];
+ var prefix = a.prefix;
+ if(prefix){//no prefix attribute has no namespace
+ if(prefix === 'xml'){
+ a.uri = 'http://www.w3.org/XML/1998/namespace';
+ }if(prefix !== 'xmlns'){
+ a.uri = currentNSMap[prefix || ''];
+
+ //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
+ }
+ }
+ }
+ var nsp = tagName.indexOf(':');
+ if(nsp>0){
+ prefix = el.prefix = tagName.slice(0,nsp);
+ localName = el.localName = tagName.slice(nsp+1);
+ }else{
+ prefix = null;//important!!
+ localName = el.localName = tagName;
+ }
+ //no prefix element has default namespace
+ var ns = el.uri = currentNSMap[prefix || ''];
+ domBuilder.startElement(ns,localName,tagName,el);
+ //endPrefixMapping and startPrefixMapping have not any help for dom builder
+ //localNSMap = null
+ if(el.closed){
+ domBuilder.endElement(ns,localName,tagName);
+ if(localNSMap){
+ for(prefix in localNSMap){
+ domBuilder.endPrefixMapping(prefix);
+ }
+ }
+ }else{
+ el.currentNSMap = currentNSMap;
+ el.localNSMap = localNSMap;
+ //parseStack.push(el);
+ return true;
+ }
+}
+function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
+ if(/^(?:script|textarea)$/i.test(tagName)){
+ var elEndStart = source.indexOf('</'+tagName+'>',elStartEnd);
+ var text = source.substring(elStartEnd+1,elEndStart);
+ if(/[&<]/.test(text)){
+ if(/^script$/i.test(tagName)){
+ //if(!/\]\]>/.test(text)){
+ //lexHandler.startCDATA();
+ domBuilder.characters(text,0,text.length);
+ //lexHandler.endCDATA();
+ return elEndStart;
+ //}
+ }//}else{//text area
+ text = text.replace(/&#?\w+;/g,entityReplacer);
+ domBuilder.characters(text,0,text.length);
+ return elEndStart;
+ //}
+
+ }
+ }
+ return elStartEnd+1;
+}
+function fixSelfClosed(source,elStartEnd,tagName,closeMap){
+ //if(tagName in closeMap){
+ var pos = closeMap[tagName];
+ if(pos == null){
+ //console.log(tagName)
+ pos = source.lastIndexOf('</'+tagName+'>');
+ if(pos<elStartEnd){//忘记闭合
+ pos = source.lastIndexOf('</'+tagName);
+ }
+ closeMap[tagName] =pos;
+ }
+ return pos<elStartEnd;
+ //}
+}
+function _copy(source,target){
+ for(var n in source){target[n] = source[n];}
+}
+function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
+ var next= source.charAt(start+2);
+ switch(next){
+ case '-':
+ if(source.charAt(start + 3) === '-'){
+ var end = source.indexOf('-->',start+4);
+ //append comment source.substring(4,end)//<!--
+ if(end>start){
+ domBuilder.comment(source,start+4,end-start-4);
+ return end+3;
+ }else{
+ errorHandler.error("Unclosed comment");
+ return -1;
+ }
+ }else{
+ //error
+ return -1;
+ }
+ default:
+ if(source.substr(start+3,6) == 'CDATA['){
+ var end = source.indexOf(']]>',start+9);
+ domBuilder.startCDATA();
+ domBuilder.characters(source,start+9,end-start-9);
+ domBuilder.endCDATA();
+ return end+3;
+ }
+ //<!DOCTYPE
+ //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
+ var matchs = split(source,start);
+ var len = matchs.length;
+ if(len>1 && /!doctype/i.test(matchs[0][0])){
+ var name = matchs[1][0];
+ var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
+ var sysid = len>4 && matchs[4][0];
+ var lastMatch = matchs[len-1];
+ domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
+ sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
+ domBuilder.endDTD();
+
+ return lastMatch.index+lastMatch[0].length
+ }
+ }
+ return -1;
+}
+
+
+
+function parseInstruction(source,start,domBuilder){
+ var end = source.indexOf('?>',start);
+ if(end){
+ var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
+ if(match){
+ var len = match[0].length;
+ domBuilder.processingInstruction(match[1], match[2]) ;
+ return end+2;
+ }else{//error
+ return -1;
+ }
+ }
+ return -1;
+}
+
+/**
+ * @param source
+ */
+function ElementAttributes(source){
+
+}
+ElementAttributes.prototype = {
+ setTagName:function(tagName){
+ if(!tagNamePattern.test(tagName)){
+ throw new Error('invalid tagName:'+tagName)
+ }
+ this.tagName = tagName;
+ },
+ add:function(qName,value,offset){
+ if(!tagNamePattern.test(qName)){
+ throw new Error('invalid attribute:'+qName)
+ }
+ this[this.length++] = {qName:qName,value:value,offset:offset};
+ },
+ length:0,
+ getLocalName:function(i){return this[i].localName},
+ getLocator:function(i){return this[i].locator},
+ getQName:function(i){return this[i].qName},
+ getURI:function(i){return this[i].uri},
+ getValue:function(i){return this[i].value}
+// ,getIndex:function(uri, localName)){
+// if(localName){
+//
+// }else{
+// var qName = uri
+// }
+// },
+// getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
+// getType:function(uri,localName){}
+// getType:function(i){},
+};
+
+
+
+
+function _set_proto_(thiz,parent){
+ thiz.__proto__ = parent;
+ return thiz;
+}
+if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
+ _set_proto_ = function(thiz,parent){
+ function p(){}
+ p.prototype = parent;
+ p = new p();
+ for(parent in thiz){
+ p[parent] = thiz[parent];
+ }
+ return p;
+ };
+}
+
+function split(source,start){
+ var match;
+ var buf = [];
+ var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
+ reg.lastIndex = start;
+ reg.exec(source);//skip <
+ while(match = reg.exec(source)){
+ buf.push(match);
+ if(match[1])return buf;
+ }
+}
+
+var XMLReader_1 = XMLReader;
+
+var sax = {
+ XMLReader: XMLReader_1
+};
+
+/*
+ * DOM Level 2
+ * Object DOMException
+ * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
+ */
+
+function copy$1(src,dest){
+ for(var p in src){
+ dest[p] = src[p];
+ }
+}
+/**
+^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
+^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
+ */
+function _extends(Class,Super){
+ var pt = Class.prototype;
+ if(Object.create){
+ var ppt = Object.create(Super.prototype);
+ pt.__proto__ = ppt;
+ }
+ if(!(pt instanceof Super)){
+ function t(){}
+ t.prototype = Super.prototype;
+ t = new t();
+ copy$1(pt,t);
+ Class.prototype = pt = t;
+ }
+ if(pt.constructor != Class){
+ if(typeof Class != 'function'){
+ console.error("unknow Class:"+Class);
+ }
+ pt.constructor = Class;
+ }
+}
+var htmlns = 'http://www.w3.org/1999/xhtml';
+// Node Types
+var NodeType = {};
+var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
+var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
+var TEXT_NODE = NodeType.TEXT_NODE = 3;
+var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
+var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
+var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
+var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
+var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
+var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
+var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
+var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
+var NOTATION_NODE = NodeType.NOTATION_NODE = 12;
+
+// ExceptionCode
+var ExceptionCode = {};
+var ExceptionMessage = {};
+var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1);
+var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2);
+var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3);
+var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4);
+var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5);
+var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6);
+var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
+var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8);
+var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9);
+var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10);
+//level2
+var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11);
+var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12);
+var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13);
+var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14);
+var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15);
+
+
+function DOMException(code, message) {
+ if(message instanceof Error){
+ var error = message;
+ }else{
+ error = this;
+ Error.call(this, ExceptionMessage[code]);
+ this.message = ExceptionMessage[code];
+ if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException);
+ }
+ error.code = code;
+ if(message) this.message = this.message + ": " + message;
+ return error;
+}
+DOMException.prototype = Error.prototype;
+copy$1(ExceptionCode,DOMException);
+/**
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
+ * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
+ * The items in the NodeList are accessible via an integral index, starting from 0.
+ */
+function NodeList() {
+}
+NodeList.prototype = {
+ /**
+ * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
+ * @standard level1
+ */
+ length:0,
+ /**
+ * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
+ * @standard level1
+ * @param index unsigned long
+ * Index into the collection.
+ * @return Node
+ * The node at the indexth position in the NodeList, or null if that is not a valid index.
+ */
+ item: function(index) {
+ return this[index] || null;
+ },
+ toString:function(isHTML,nodeFilter){
+ for(var buf = [], i = 0;i<this.length;i++){
+ serializeToString(this[i],buf,isHTML,nodeFilter);
+ }
+ return buf.join('');
+ }
+};
+function LiveNodeList(node,refresh){
+ this._node = node;
+ this._refresh = refresh;
+ _updateLiveList(this);
+}
+function _updateLiveList(list){
+ var inc = list._node._inc || list._node.ownerDocument._inc;
+ if(list._inc != inc){
+ var ls = list._refresh(list._node);
+ //console.log(ls.length)
+ __set__(list,'length',ls.length);
+ copy$1(ls,list);
+ list._inc = inc;
+ }
+}
+LiveNodeList.prototype.item = function(i){
+ _updateLiveList(this);
+ return this[i];
+};
+
+_extends(LiveNodeList,NodeList);
+/**
+ *
+ * Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.
+ * NamedNodeMap objects in the DOM are live.
+ * used for attributes or DocumentType entities
+ */
+function NamedNodeMap() {
+}
+
+function _findNodeIndex(list,node){
+ var i = list.length;
+ while(i--){
+ if(list[i] === node){return i}
+ }
+}
+
+function _addNamedNode(el,list,newAttr,oldAttr){
+ if(oldAttr){
+ list[_findNodeIndex(list,oldAttr)] = newAttr;
+ }else{
+ list[list.length++] = newAttr;
+ }
+ if(el){
+ newAttr.ownerElement = el;
+ var doc = el.ownerDocument;
+ if(doc){
+ oldAttr && _onRemoveAttribute(doc,el,oldAttr);
+ _onAddAttribute(doc,el,newAttr);
+ }
+ }
+}
+function _removeNamedNode(el,list,attr){
+ //console.log('remove attr:'+attr)
+ var i = _findNodeIndex(list,attr);
+ if(i>=0){
+ var lastIndex = list.length-1;
+ while(i<lastIndex){
+ list[i] = list[++i];
+ }
+ list.length = lastIndex;
+ if(el){
+ var doc = el.ownerDocument;
+ if(doc){
+ _onRemoveAttribute(doc,el,attr);
+ attr.ownerElement = null;
+ }
+ }
+ }else{
+ throw DOMException(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
+ }
+}
+NamedNodeMap.prototype = {
+ length:0,
+ item:NodeList.prototype.item,
+ getNamedItem: function(key) {
+// if(key.indexOf(':')>0 || key == 'xmlns'){
+// return null;
+// }
+ //console.log()
+ var i = this.length;
+ while(i--){
+ var attr = this[i];
+ //console.log(attr.nodeName,key)
+ if(attr.nodeName == key){
+ return attr;
+ }
+ }
+ },
+ setNamedItem: function(attr) {
+ var el = attr.ownerElement;
+ if(el && el!=this._ownerElement){
+ throw new DOMException(INUSE_ATTRIBUTE_ERR);
+ }
+ var oldAttr = this.getNamedItem(attr.nodeName);
+ _addNamedNode(this._ownerElement,this,attr,oldAttr);
+ return oldAttr;
+ },
+ /* returns Node */
+ setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
+ var el = attr.ownerElement, oldAttr;
+ if(el && el!=this._ownerElement){
+ throw new DOMException(INUSE_ATTRIBUTE_ERR);
+ }
+ oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
+ _addNamedNode(this._ownerElement,this,attr,oldAttr);
+ return oldAttr;
+ },
+
+ /* returns Node */
+ removeNamedItem: function(key) {
+ var attr = this.getNamedItem(key);
+ _removeNamedNode(this._ownerElement,this,attr);
+ return attr;
+
+
+ },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
+
+ //for level2
+ removeNamedItemNS:function(namespaceURI,localName){
+ var attr = this.getNamedItemNS(namespaceURI,localName);
+ _removeNamedNode(this._ownerElement,this,attr);
+ return attr;
+ },
+ getNamedItemNS: function(namespaceURI, localName) {
+ var i = this.length;
+ while(i--){
+ var node = this[i];
+ if(node.localName == localName && node.namespaceURI == namespaceURI){
+ return node;
+ }
+ }
+ return null;
+ }
+};
+/**
+ * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
+ */
+function DOMImplementation(/* Object */ features) {
+ this._features = {};
+ if (features) {
+ for (var feature in features) {
+ this._features = features[feature];
+ }
+ }
+}
+
+DOMImplementation.prototype = {
+ hasFeature: function(/* string */ feature, /* string */ version) {
+ var versions = this._features[feature.toLowerCase()];
+ if (versions && (!version || version in versions)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ // Introduced in DOM Level 2:
+ createDocument:function(namespaceURI, qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
+ var doc = new Document();
+ doc.implementation = this;
+ doc.childNodes = new NodeList();
+ doc.doctype = doctype;
+ if(doctype){
+ doc.appendChild(doctype);
+ }
+ if(qualifiedName){
+ var root = doc.createElementNS(namespaceURI,qualifiedName);
+ doc.appendChild(root);
+ }
+ return doc;
+ },
+ // Introduced in DOM Level 2:
+ createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
+ var node = new DocumentType();
+ node.name = qualifiedName;
+ node.nodeName = qualifiedName;
+ node.publicId = publicId;
+ node.systemId = systemId;
+ // Introduced in DOM Level 2:
+ //readonly attribute DOMString internalSubset;
+
+ //TODO:..
+ // readonly attribute NamedNodeMap entities;
+ // readonly attribute NamedNodeMap notations;
+ return node;
+ }
+};
+
+
+/**
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
+ */
+
+function Node$3() {
+}
+
+Node$3.prototype = {
+ firstChild : null,
+ lastChild : null,
+ previousSibling : null,
+ nextSibling : null,
+ attributes : null,
+ parentNode : null,
+ childNodes : null,
+ ownerDocument : null,
+ nodeValue : null,
+ namespaceURI : null,
+ prefix : null,
+ localName : null,
+ // Modified in DOM Level 2:
+ insertBefore:function(newChild, refChild){//raises
+ return _insertBefore(this,newChild,refChild);
+ },
+ replaceChild:function(newChild, oldChild){//raises
+ this.insertBefore(newChild,oldChild);
+ if(oldChild){
+ this.removeChild(oldChild);
+ }
+ },
+ removeChild:function(oldChild){
+ return _removeChild(this,oldChild);
+ },
+ appendChild:function(newChild){
+ return this.insertBefore(newChild,null);
+ },
+ hasChildNodes:function(){
+ return this.firstChild != null;
+ },
+ cloneNode:function(deep){
+ return cloneNode(this.ownerDocument||this,this,deep);
+ },
+ // Modified in DOM Level 2:
+ normalize:function(){
+ var child = this.firstChild;
+ while(child){
+ var next = child.nextSibling;
+ if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
+ this.removeChild(next);
+ child.appendData(next.data);
+ }else{
+ child.normalize();
+ child = next;
+ }
+ }
+ },
+ // Introduced in DOM Level 2:
+ isSupported:function(feature, version){
+ return this.ownerDocument.implementation.hasFeature(feature,version);
+ },
+ // Introduced in DOM Level 2:
+ hasAttributes:function(){
+ return this.attributes.length>0;
+ },
+ lookupPrefix:function(namespaceURI){
+ var el = this;
+ while(el){
+ var map = el._nsMap;
+ //console.dir(map)
+ if(map){
+ for(var n in map){
+ if(map[n] == namespaceURI){
+ return n;
+ }
+ }
+ }
+ el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
+ }
+ return null;
+ },
+ // Introduced in DOM Level 3:
+ lookupNamespaceURI:function(prefix){
+ var el = this;
+ while(el){
+ var map = el._nsMap;
+ //console.dir(map)
+ if(map){
+ if(prefix in map){
+ return map[prefix] ;
+ }
+ }
+ el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
+ }
+ return null;
+ },
+ // Introduced in DOM Level 3:
+ isDefaultNamespace:function(namespaceURI){
+ var prefix = this.lookupPrefix(namespaceURI);
+ return prefix == null;
+ }
+};
+
+
+function _xmlEncoder(c){
+ return c == '<' && '<' ||
+ c == '>' && '>' ||
+ c == '&' && '&' ||
+ c == '"' && '"' ||
+ '&#'+c.charCodeAt()+';'
+}
+
+
+copy$1(NodeType,Node$3);
+copy$1(NodeType,Node$3.prototype);
+
+/**
+ * @param callback return true for continue,false for break
+ * @return boolean true: break visit;
+ */
+function _visitNode(node,callback){
+ if(callback(node)){
+ return true;
+ }
+ if(node = node.firstChild){
+ do{
+ if(_visitNode(node,callback)){return true}
+ }while(node=node.nextSibling)
+ }
+}
+
+
+
+function Document(){
+}
+function _onAddAttribute(doc,el,newAttr){
+ doc && doc._inc++;
+ var ns = newAttr.namespaceURI;
+ if(ns == 'http://www.w3.org/2000/xmlns/'){
+ //update namespace
+ el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value;
+ }
+}
+function _onRemoveAttribute(doc,el,newAttr,remove){
+ doc && doc._inc++;
+ var ns = newAttr.namespaceURI;
+ if(ns == 'http://www.w3.org/2000/xmlns/'){
+ //update namespace
+ delete el._nsMap[newAttr.prefix?newAttr.localName:''];
+ }
+}
+function _onUpdateChild(doc,el,newChild){
+ if(doc && doc._inc){
+ doc._inc++;
+ //update childNodes
+ var cs = el.childNodes;
+ if(newChild){
+ cs[cs.length++] = newChild;
+ }else{
+ //console.log(1)
+ var child = el.firstChild;
+ var i = 0;
+ while(child){
+ cs[i++] = child;
+ child =child.nextSibling;
+ }
+ cs.length = i;
+ }
+ }
+}
+
+/**
+ * attributes;
+ * children;
+ *
+ * writeable properties:
+ * nodeValue,Attr:value,CharacterData:data
+ * prefix
+ */
+function _removeChild(parentNode,child){
+ var previous = child.previousSibling;
+ var next = child.nextSibling;
+ if(previous){
+ previous.nextSibling = next;
+ }else{
+ parentNode.firstChild = next;
+ }
+ if(next){
+ next.previousSibling = previous;
+ }else{
+ parentNode.lastChild = previous;
+ }
+ _onUpdateChild(parentNode.ownerDocument,parentNode);
+ return child;
+}
+/**
+ * preformance key(refChild == null)
+ */
+function _insertBefore(parentNode,newChild,nextChild){
+ var cp = newChild.parentNode;
+ if(cp){
+ cp.removeChild(newChild);//remove and update
+ }
+ if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
+ var newFirst = newChild.firstChild;
+ if (newFirst == null) {
+ return newChild;
+ }
+ var newLast = newChild.lastChild;
+ }else{
+ newFirst = newLast = newChild;
+ }
+ var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
+
+ newFirst.previousSibling = pre;
+ newLast.nextSibling = nextChild;
+
+
+ if(pre){
+ pre.nextSibling = newFirst;
+ }else{
+ parentNode.firstChild = newFirst;
+ }
+ if(nextChild == null){
+ parentNode.lastChild = newLast;
+ }else{
+ nextChild.previousSibling = newLast;
+ }
+ do{
+ newFirst.parentNode = parentNode;
+ }while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
+ _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
+ //console.log(parentNode.lastChild.nextSibling == null)
+ if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
+ newChild.firstChild = newChild.lastChild = null;
+ }
+ return newChild;
+}
+function _appendSingleChild(parentNode,newChild){
+ var cp = newChild.parentNode;
+ if(cp){
+ var pre = parentNode.lastChild;
+ cp.removeChild(newChild);//remove and update
+ var pre = parentNode.lastChild;
+ }
+ var pre = parentNode.lastChild;
+ newChild.parentNode = parentNode;
+ newChild.previousSibling = pre;
+ newChild.nextSibling = null;
+ if(pre){
+ pre.nextSibling = newChild;
+ }else{
+ parentNode.firstChild = newChild;
+ }
+ parentNode.lastChild = newChild;
+ _onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
+ return newChild;
+ //console.log("__aa",parentNode.lastChild.nextSibling == null)
+}
+Document.prototype = {
+ //implementation : null,
+ nodeName : '#document',
+ nodeType : DOCUMENT_NODE,
+ doctype : null,
+ documentElement : null,
+ _inc : 1,
+
+ insertBefore : function(newChild, refChild){//raises
+ if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
+ var child = newChild.firstChild;
+ while(child){
+ var next = child.nextSibling;
+ this.insertBefore(child,refChild);
+ child = next;
+ }
+ return newChild;
+ }
+ if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
+ this.documentElement = newChild;
+ }
+
+ return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
+ },
+ removeChild : function(oldChild){
+ if(this.documentElement == oldChild){
+ this.documentElement = null;
+ }
+ return _removeChild(this,oldChild);
+ },
+ // Introduced in DOM Level 2:
+ importNode : function(importedNode,deep){
+ return importNode(this,importedNode,deep);
+ },
+ // Introduced in DOM Level 2:
+ getElementById : function(id){
+ var rtv = null;
+ _visitNode(this.documentElement,function(node){
+ if(node.nodeType == ELEMENT_NODE){
+ if(node.getAttribute('id') == id){
+ rtv = node;
+ return true;
+ }
+ }
+ });
+ return rtv;
+ },
+
+ //document factory method:
+ createElement : function(tagName){
+ var node = new Element();
+ node.ownerDocument = this;
+ node.nodeName = tagName;
+ node.tagName = tagName;
+ node.childNodes = new NodeList();
+ var attrs = node.attributes = new NamedNodeMap();
+ attrs._ownerElement = node;
+ return node;
+ },
+ createDocumentFragment : function(){
+ var node = new DocumentFragment();
+ node.ownerDocument = this;
+ node.childNodes = new NodeList();
+ return node;
+ },
+ createTextNode : function(data){
+ var node = new Text();
+ node.ownerDocument = this;
+ node.appendData(data);
+ return node;
+ },
+ createComment : function(data){
+ var node = new Comment();
+ node.ownerDocument = this;
+ node.appendData(data);
+ return node;
+ },
+ createCDATASection : function(data){
+ var node = new CDATASection();
+ node.ownerDocument = this;
+ node.appendData(data);
+ return node;
+ },
+ createProcessingInstruction : function(target,data){
+ var node = new ProcessingInstruction();
+ node.ownerDocument = this;
+ node.tagName = node.target = target;
+ node.nodeValue= node.data = data;
+ return node;
+ },
+ createAttribute : function(name){
+ var node = new Attr();
+ node.ownerDocument = this;
+ node.name = name;
+ node.nodeName = name;
+ node.localName = name;
+ node.specified = true;
+ return node;
+ },
+ createEntityReference : function(name){
+ var node = new EntityReference();
+ node.ownerDocument = this;
+ node.nodeName = name;
+ return node;
+ },
+ // Introduced in DOM Level 2:
+ createElementNS : function(namespaceURI,qualifiedName){
+ var node = new Element();
+ var pl = qualifiedName.split(':');
+ var attrs = node.attributes = new NamedNodeMap();
+ node.childNodes = new NodeList();
+ node.ownerDocument = this;
+ node.nodeName = qualifiedName;
+ node.tagName = qualifiedName;
+ node.namespaceURI = namespaceURI;
+ if(pl.length == 2){
+ node.prefix = pl[0];
+ node.localName = pl[1];
+ }else{
+ //el.prefix = null;
+ node.localName = qualifiedName;
+ }
+ attrs._ownerElement = node;
+ return node;
+ },
+ // Introduced in DOM Level 2:
+ createAttributeNS : function(namespaceURI,qualifiedName){
+ var node = new Attr();
+ var pl = qualifiedName.split(':');
+ node.ownerDocument = this;
+ node.nodeName = qualifiedName;
+ node.name = qualifiedName;
+ node.namespaceURI = namespaceURI;
+ node.specified = true;
+ if(pl.length == 2){
+ node.prefix = pl[0];
+ node.localName = pl[1];
+ }else{
+ //el.prefix = null;
+ node.localName = qualifiedName;
+ }
+ return node;
+ }
+};
+_extends(Document,Node$3);
+
+
+function Element() {
+ this._nsMap = {};
+}
+Element.prototype = {
+ nodeType : ELEMENT_NODE,
+ hasAttribute : function(name){
+ return this.getAttributeNode(name)!=null;
+ },
+ getAttribute : function(name){
+ var attr = this.getAttributeNode(name);
+ return attr && attr.value || '';
+ },
+ getAttributeNode : function(name){
+ return this.attributes.getNamedItem(name);
+ },
+ setAttribute : function(name, value){
+ var attr = this.ownerDocument.createAttribute(name);
+ attr.value = attr.nodeValue = "" + value;
+ this.setAttributeNode(attr);
+ },
+ removeAttribute : function(name){
+ var attr = this.getAttributeNode(name);
+ attr && this.removeAttributeNode(attr);
+ },
+
+ //four real opeartion method
+ appendChild:function(newChild){
+ if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
+ return this.insertBefore(newChild,null);
+ }else{
+ return _appendSingleChild(this,newChild);
+ }
+ },
+ setAttributeNode : function(newAttr){
+ return this.attributes.setNamedItem(newAttr);
+ },
+ setAttributeNodeNS : function(newAttr){
+ return this.attributes.setNamedItemNS(newAttr);
+ },
+ removeAttributeNode : function(oldAttr){
+ //console.log(this == oldAttr.ownerElement)
+ return this.attributes.removeNamedItem(oldAttr.nodeName);
+ },
+ //get real attribute name,and remove it by removeAttributeNode
+ removeAttributeNS : function(namespaceURI, localName){
+ var old = this.getAttributeNodeNS(namespaceURI, localName);
+ old && this.removeAttributeNode(old);
+ },
+
+ hasAttributeNS : function(namespaceURI, localName){
+ return this.getAttributeNodeNS(namespaceURI, localName)!=null;
+ },
+ getAttributeNS : function(namespaceURI, localName){
+ var attr = this.getAttributeNodeNS(namespaceURI, localName);
+ return attr && attr.value || '';
+ },
+ setAttributeNS : function(namespaceURI, qualifiedName, value){
+ var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
+ attr.value = attr.nodeValue = "" + value;
+ this.setAttributeNode(attr);
+ },
+ getAttributeNodeNS : function(namespaceURI, localName){
+ return this.attributes.getNamedItemNS(namespaceURI, localName);
+ },
+
+ getElementsByTagName : function(tagName){
+ return new LiveNodeList(this,function(base){
+ var ls = [];
+ _visitNode(base,function(node){
+ if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
+ ls.push(node);
+ }
+ });
+ return ls;
+ });
+ },
+ getElementsByTagNameNS : function(namespaceURI, localName){
+ return new LiveNodeList(this,function(base){
+ var ls = [];
+ _visitNode(base,function(node){
+ if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
+ ls.push(node);
+ }
+ });
+ return ls;
+
+ });
+ }
+};
+Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
+Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
+
+
+_extends(Element,Node$3);
+function Attr() {
+}
+Attr.prototype.nodeType = ATTRIBUTE_NODE;
+_extends(Attr,Node$3);
+
+
+function CharacterData() {
+}
+CharacterData.prototype = {
+ data : '',
+ substringData : function(offset, count) {
+ return this.data.substring(offset, offset+count);
+ },
+ appendData: function(text) {
+ text = this.data+text;
+ this.nodeValue = this.data = text;
+ this.length = text.length;
+ },
+ insertData: function(offset,text) {
+ this.replaceData(offset,0,text);
+
+ },
+ appendChild:function(newChild){
+ throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
+ },
+ deleteData: function(offset, count) {
+ this.replaceData(offset,count,"");
+ },
+ replaceData: function(offset, count, text) {
+ var start = this.data.substring(0,offset);
+ var end = this.data.substring(offset+count);
+ text = start + text + end;
+ this.nodeValue = this.data = text;
+ this.length = text.length;
+ }
+};
+_extends(CharacterData,Node$3);
+function Text() {
+}
+Text.prototype = {
+ nodeName : "#text",
+ nodeType : TEXT_NODE,
+ splitText : function(offset) {
+ var text = this.data;
+ var newText = text.substring(offset);
+ text = text.substring(0, offset);
+ this.data = this.nodeValue = text;
+ this.length = text.length;
+ var newNode = this.ownerDocument.createTextNode(newText);
+ if(this.parentNode){
+ this.parentNode.insertBefore(newNode, this.nextSibling);
+ }
+ return newNode;
+ }
+};
+_extends(Text,CharacterData);
+function Comment() {
+}
+Comment.prototype = {
+ nodeName : "#comment",
+ nodeType : COMMENT_NODE
+};
+_extends(Comment,CharacterData);
+
+function CDATASection() {
+}
+CDATASection.prototype = {
+ nodeName : "#cdata-section",
+ nodeType : CDATA_SECTION_NODE
+};
+_extends(CDATASection,CharacterData);
+
+
+function DocumentType() {
+}
+DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
+_extends(DocumentType,Node$3);
+
+function Notation() {
+}
+Notation.prototype.nodeType = NOTATION_NODE;
+_extends(Notation,Node$3);
+
+function Entity() {
+}
+Entity.prototype.nodeType = ENTITY_NODE;
+_extends(Entity,Node$3);
+
+function EntityReference() {
+}
+EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
+_extends(EntityReference,Node$3);
+
+function DocumentFragment() {
+}
+DocumentFragment.prototype.nodeName = "#document-fragment";
+DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
+_extends(DocumentFragment,Node$3);
+
+
+function ProcessingInstruction() {
+}
+ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
+_extends(ProcessingInstruction,Node$3);
+function XMLSerializer$1(){}
+XMLSerializer$1.prototype.serializeToString = function(node,isHtml,nodeFilter){
+ return nodeSerializeToString.call(node,isHtml,nodeFilter);
+};
+Node$3.prototype.toString = nodeSerializeToString;
+function nodeSerializeToString(isHtml,nodeFilter){
+ var buf = [];
+ var refNode = this.nodeType == 9?this.documentElement:this;
+ var prefix = refNode.prefix;
+ var uri = refNode.namespaceURI;
+
+ if(uri && prefix == null){
+ //console.log(prefix)
+ var prefix = refNode.lookupPrefix(uri);
+ if(prefix == null){
+ //isHTML = true;
+ var visibleNamespaces=[
+ {namespace:uri,prefix:null}
+ //{namespace:uri,prefix:''}
+ ];
+ }
+ }
+ serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
+ //console.log('###',this.nodeType,uri,prefix,buf.join(''))
+ return buf.join('');
+}
+function needNamespaceDefine(node,isHTML, visibleNamespaces) {
+ var prefix = node.prefix||'';
+ var uri = node.namespaceURI;
+ if (!prefix && !uri){
+ return false;
+ }
+ if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace"
+ || uri == 'http://www.w3.org/2000/xmlns/'){
+ return false;
+ }
+
+ var i = visibleNamespaces.length;
+ //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
+ while (i--) {
+ var ns = visibleNamespaces[i];
+ // get namespace prefix
+ //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
+ if (ns.prefix == prefix){
+ return ns.namespace != uri;
+ }
+ }
+ //console.log(isHTML,uri,prefix=='')
+ //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
+ // return false;
+ //}
+ //node.flag = '11111'
+ //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
+ return true;
+}
+function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
+ if(nodeFilter){
+ node = nodeFilter(node);
+ if(node){
+ if(typeof node == 'string'){
+ buf.push(node);
+ return;
+ }
+ }else{
+ return;
+ }
+ //buf.sort.apply(attrs, attributeSorter);
+ }
+ switch(node.nodeType){
+ case ELEMENT_NODE:
+ if (!visibleNamespaces) visibleNamespaces = [];
+ var startVisibleNamespaces = visibleNamespaces.length;
+ var attrs = node.attributes;
+ var len = attrs.length;
+ var child = node.firstChild;
+ var nodeName = node.tagName;
+
+ isHTML = (htmlns === node.namespaceURI) ||isHTML;
+ buf.push('<',nodeName);
+
+
+
+ for(var i=0;i<len;i++){
+ // add namespaces for attributes
+ var attr = attrs.item(i);
+ if (attr.prefix == 'xmlns') {
+ visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
+ }else if(attr.nodeName == 'xmlns'){
+ visibleNamespaces.push({ prefix: '', namespace: attr.value });
+ }
+ }
+ for(var i=0;i<len;i++){
+ var attr = attrs.item(i);
+ if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
+ var prefix = attr.prefix||'';
+ var uri = attr.namespaceURI;
+ var ns = prefix ? ' xmlns:' + prefix : " xmlns";
+ buf.push(ns, '="' , uri , '"');
+ visibleNamespaces.push({ prefix: prefix, namespace:uri });
+ }
+ serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
+ }
+ // add namespace for current node
+ if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
+ var prefix = node.prefix||'';
+ var uri = node.namespaceURI;
+ var ns = prefix ? ' xmlns:' + prefix : " xmlns";
+ buf.push(ns, '="' , uri , '"');
+ visibleNamespaces.push({ prefix: prefix, namespace:uri });
+ }
+
+ if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
+ buf.push('>');
+ //if is cdata child node
+ if(isHTML && /^script$/i.test(nodeName)){
+ while(child){
+ if(child.data){
+ buf.push(child.data);
+ }else{
+ serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+ }
+ child = child.nextSibling;
+ }
+ }else
+ {
+ while(child){
+ serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+ child = child.nextSibling;
+ }
+ }
+ buf.push('</',nodeName,'>');
+ }else{
+ buf.push('/>');
+ }
+ // remove added visible namespaces
+ //visibleNamespaces.length = startVisibleNamespaces;
+ return;
+ case DOCUMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ var child = node.firstChild;
+ while(child){
+ serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+ child = child.nextSibling;
+ }
+ return;
+ case ATTRIBUTE_NODE:
+ return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
+ case TEXT_NODE:
+ return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
+ case CDATA_SECTION_NODE:
+ return buf.push( '<![CDATA[',node.data,']]>');
+ case COMMENT_NODE:
+ return buf.push( "<!--",node.data,"-->");
+ case DOCUMENT_TYPE_NODE:
+ var pubid = node.publicId;
+ var sysid = node.systemId;
+ buf.push('<!DOCTYPE ',node.name);
+ if(pubid){
+ buf.push(' PUBLIC "',pubid);
+ if (sysid && sysid!='.') {
+ buf.push( '" "',sysid);
+ }
+ buf.push('">');
+ }else if(sysid && sysid!='.'){
+ buf.push(' SYSTEM "',sysid,'">');
+ }else{
+ var sub = node.internalSubset;
+ if(sub){
+ buf.push(" [",sub,"]");
+ }
+ buf.push(">");
+ }
+ return;
+ case PROCESSING_INSTRUCTION_NODE:
+ return buf.push( "<?",node.target," ",node.data,"?>");
+ case ENTITY_REFERENCE_NODE:
+ return buf.push( '&',node.nodeName,';');
+ //case ENTITY_NODE:
+ //case NOTATION_NODE:
+ default:
+ buf.push('??',node.nodeName);
+ }
+}
+function importNode(doc,node,deep){
+ var node2;
+ switch (node.nodeType) {
+ case ELEMENT_NODE:
+ node2 = node.cloneNode(false);
+ node2.ownerDocument = doc;
+ //var attrs = node2.attributes;
+ //var len = attrs.length;
+ //for(var i=0;i<len;i++){
+ //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
+ //}
+ case DOCUMENT_FRAGMENT_NODE:
+ break;
+ case ATTRIBUTE_NODE:
+ deep = true;
+ break;
+ //case ENTITY_REFERENCE_NODE:
+ //case PROCESSING_INSTRUCTION_NODE:
+ ////case TEXT_NODE:
+ //case CDATA_SECTION_NODE:
+ //case COMMENT_NODE:
+ // deep = false;
+ // break;
+ //case DOCUMENT_NODE:
+ //case DOCUMENT_TYPE_NODE:
+ //cannot be imported.
+ //case ENTITY_NODE:
+ //case NOTATION_NODE:
+ //can not hit in level3
+ //default:throw e;
+ }
+ if(!node2){
+ node2 = node.cloneNode(false);//false
+ }
+ node2.ownerDocument = doc;
+ node2.parentNode = null;
+ if(deep){
+ var child = node.firstChild;
+ while(child){
+ node2.appendChild(importNode(doc,child,deep));
+ child = child.nextSibling;
+ }
+ }
+ return node2;
+}
+//
+//var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
+// attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
+function cloneNode(doc,node,deep){
+ var node2 = new node.constructor();
+ for(var n in node){
+ var v = node[n];
+ if(typeof v != 'object' ){
+ if(v != node2[n]){
+ node2[n] = v;
+ }
+ }
+ }
+ if(node.childNodes){
+ node2.childNodes = new NodeList();
+ }
+ node2.ownerDocument = doc;
+ switch (node2.nodeType) {
+ case ELEMENT_NODE:
+ var attrs = node.attributes;
+ var attrs2 = node2.attributes = new NamedNodeMap();
+ var len = attrs.length;
+ attrs2._ownerElement = node2;
+ for(var i=0;i<len;i++){
+ node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
+ }
+ break;;
+ case ATTRIBUTE_NODE:
+ deep = true;
+ }
+ if(deep){
+ var child = node.firstChild;
+ while(child){
+ node2.appendChild(cloneNode(doc,child,deep));
+ child = child.nextSibling;
+ }
+ }
+ return node2;
+}
+
+function __set__(object,key,value){
+ object[key] = value;
+}
+//do dynamic
+try{
+ if(Object.defineProperty){
+ Object.defineProperty(LiveNodeList.prototype,'length',{
+ get:function(){
+ _updateLiveList(this);
+ return this.$$length;
+ }
+ });
+ Object.defineProperty(Node$3.prototype,'textContent',{
+ get:function(){
+ return getTextContent(this);
+ },
+ set:function(data){
+ switch(this.nodeType){
+ case ELEMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ while(this.firstChild){
+ this.removeChild(this.firstChild);
+ }
+ if(data || String(data)){
+ this.appendChild(this.ownerDocument.createTextNode(data));
+ }
+ break;
+ default:
+ //TODO:
+ this.data = data;
+ this.value = data;
+ this.nodeValue = data;
+ }
+ }
+ });
+
+ function getTextContent(node){
+ switch(node.nodeType){
+ case ELEMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ var buf = [];
+ node = node.firstChild;
+ while(node){
+ if(node.nodeType!==7 && node.nodeType !==8){
+ buf.push(getTextContent(node));
+ }
+ node = node.nextSibling;
+ }
+ return buf.join('');
+ default:
+ return node.nodeValue;
+ }
+ }
+ __set__ = function(object,key,value){
+ //console.log(value)
+ object['$$'+key] = value;
+ };
+ }
+}catch(e){//ie8
+}
+
+//if(typeof require == 'function'){
+ var DOMImplementation_1 = DOMImplementation;
+ var XMLSerializer_1 = XMLSerializer$1;
+//}
+
+var dom = {
+ DOMImplementation: DOMImplementation_1,
+ XMLSerializer: XMLSerializer_1
+};
+
+var domParser = createCommonjsModule(function (module, exports) {
+function DOMParser(options){
+ this.options = options ||{locator:{}};
+
+}
+DOMParser.prototype.parseFromString = function(source,mimeType){
+ var options = this.options;
+ var sax$$1 = new XMLReader();
+ var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
+ var errorHandler = options.errorHandler;
+ var locator = options.locator;
+ var defaultNSMap = options.xmlns||{};
+ var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
+ if(locator){
+ domBuilder.setDocumentLocator(locator);
+ }
+
+ sax$$1.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
+ sax$$1.domBuilder = options.domBuilder || domBuilder;
+ if(/\/x?html?$/.test(mimeType)){
+ entityMap.nbsp = '\xa0';
+ entityMap.copy = '\xa9';
+ defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
+ }
+ defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
+ if(source){
+ sax$$1.parse(source,defaultNSMap,entityMap);
+ }else{
+ sax$$1.errorHandler.error("invalid doc source");
+ }
+ return domBuilder.doc;
+};
+function buildErrorHandler(errorImpl,domBuilder,locator){
+ if(!errorImpl){
+ if(domBuilder instanceof DOMHandler){
+ return domBuilder;
+ }
+ errorImpl = domBuilder ;
+ }
+ var errorHandler = {};
+ var isCallback = errorImpl instanceof Function;
+ locator = locator||{};
+ function build(key){
+ var fn = errorImpl[key];
+ if(!fn && isCallback){
+ fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl;
+ }
+ errorHandler[key] = fn && function(msg){
+ fn('[xmldom '+key+']\t'+msg+_locator(locator));
+ }||function(){};
+ }
+ build('warning');
+ build('error');
+ build('fatalError');
+ return errorHandler;
+}
+
+//console.log('#\n\n\n\n\n\n\n####')
+/**
+ * +ContentHandler+ErrorHandler
+ * +LexicalHandler+EntityResolver2
+ * -DeclHandler-DTDHandler
+ *
+ * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
+ * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
+ */
+function DOMHandler() {
+ this.cdata = false;
+}
+function position(locator,node){
+ node.lineNumber = locator.lineNumber;
+ node.columnNumber = locator.columnNumber;
+}
+/**
+ * @see org.xml.sax.ContentHandler#startDocument
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
+ */
+DOMHandler.prototype = {
+ startDocument : function() {
+ this.doc = new DOMImplementation().createDocument(null, null, null);
+ if (this.locator) {
+ this.doc.documentURI = this.locator.systemId;
+ }
+ },
+ startElement:function(namespaceURI, localName, qName, attrs) {
+ var doc = this.doc;
+ var el = doc.createElementNS(namespaceURI, qName||localName);
+ var len = attrs.length;
+ appendElement(this, el);
+ this.currentElement = el;
+
+ this.locator && position(this.locator,el);
+ for (var i = 0 ; i < len; i++) {
+ var namespaceURI = attrs.getURI(i);
+ var value = attrs.getValue(i);
+ var qName = attrs.getQName(i);
+ var attr = doc.createAttributeNS(namespaceURI, qName);
+ this.locator &&position(attrs.getLocator(i),attr);
+ attr.value = attr.nodeValue = value;
+ el.setAttributeNode(attr);
+ }
+ },
+ endElement:function(namespaceURI, localName, qName) {
+ var current = this.currentElement;
+ var tagName = current.tagName;
+ this.currentElement = current.parentNode;
+ },
+ startPrefixMapping:function(prefix, uri) {
+ },
+ endPrefixMapping:function(prefix) {
+ },
+ processingInstruction:function(target, data) {
+ var ins = this.doc.createProcessingInstruction(target, data);
+ this.locator && position(this.locator,ins);
+ appendElement(this, ins);
+ },
+ ignorableWhitespace:function(ch, start, length) {
+ },
+ characters:function(chars, start, length) {
+ chars = _toString.apply(this,arguments);
+ //console.log(chars)
+ if(chars){
+ if (this.cdata) {
+ var charNode = this.doc.createCDATASection(chars);
+ } else {
+ var charNode = this.doc.createTextNode(chars);
+ }
+ if(this.currentElement){
+ this.currentElement.appendChild(charNode);
+ }else if(/^\s*$/.test(chars)){
+ this.doc.appendChild(charNode);
+ //process xml
+ }
+ this.locator && position(this.locator,charNode);
+ }
+ },
+ skippedEntity:function(name) {
+ },
+ endDocument:function() {
+ this.doc.normalize();
+ },
+ setDocumentLocator:function (locator) {
+ if(this.locator = locator){// && !('lineNumber' in locator)){
+ locator.lineNumber = 0;
+ }
+ },
+ //LexicalHandler
+ comment:function(chars, start, length) {
+ chars = _toString.apply(this,arguments);
+ var comm = this.doc.createComment(chars);
+ this.locator && position(this.locator,comm);
+ appendElement(this, comm);
+ },
+
+ startCDATA:function() {
+ //used in characters() methods
+ this.cdata = true;
+ },
+ endCDATA:function() {
+ this.cdata = false;
+ },
+
+ startDTD:function(name, publicId, systemId) {
+ var impl = this.doc.implementation;
+ if (impl && impl.createDocumentType) {
+ var dt = impl.createDocumentType(name, publicId, systemId);
+ this.locator && position(this.locator,dt);
+ appendElement(this, dt);
+ }
+ },
+ /**
+ * @see org.xml.sax.ErrorHandler
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
+ */
+ warning:function(error) {
+ console.warn('[xmldom warning]\t'+error,_locator(this.locator));
+ },
+ error:function(error) {
+ console.error('[xmldom error]\t'+error,_locator(this.locator));
+ },
+ fatalError:function(error) {
+ console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
+ throw error;
+ }
+};
+function _locator(l){
+ if(l){
+ return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
+ }
+}
+function _toString(chars,start,length){
+ if(typeof chars == 'string'){
+ return chars.substr(start,length)
+ }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
+ if(chars.length >= start+length || start){
+ return new java.lang.String(chars,start,length)+'';
+ }
+ return chars;
+ }
+}
+
+/*
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
+ * used method of org.xml.sax.ext.LexicalHandler:
+ * #comment(chars, start, length)
+ * #startCDATA()
+ * #endCDATA()
+ * #startDTD(name, publicId, systemId)
+ *
+ *
+ * IGNORED method of org.xml.sax.ext.LexicalHandler:
+ * #endDTD()
+ * #startEntity(name)
+ * #endEntity(name)
+ *
+ *
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
+ * IGNORED method of org.xml.sax.ext.DeclHandler
+ * #attributeDecl(eName, aName, type, mode, value)
+ * #elementDecl(name, model)
+ * #externalEntityDecl(name, publicId, systemId)
+ * #internalEntityDecl(name, value)
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
+ * IGNORED method of org.xml.sax.EntityResolver2
+ * #resolveEntity(String name,String publicId,String baseURI,String systemId)
+ * #resolveEntity(publicId, systemId)
+ * #getExternalSubset(name, baseURI)
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
+ * IGNORED method of org.xml.sax.DTDHandler
+ * #notationDecl(name, publicId, systemId) {};
+ * #unparsedEntityDecl(name, publicId, systemId, notationName) {};
+ */
+"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
+ DOMHandler.prototype[key] = function(){return null};
+});
+
+/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
+function appendElement (hander,node) {
+ if (!hander.currentElement) {
+ hander.doc.appendChild(node);
+ } else {
+ hander.currentElement.appendChild(node);
+ }
+}//appendChild and setAttributeNS are preformance key
+
+//if(typeof require == 'function'){
+ var XMLReader = sax.XMLReader;
+ var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
+ exports.XMLSerializer = dom.XMLSerializer ;
+ exports.DOMParser = DOMParser;
+//}
+});
+
+var togeojson = createCommonjsModule(function (module, exports) {
+var toGeoJSON = (function() {
+ 'use strict';
+
+ var removeSpace = /\s*/g,
+ trimSpace = /^\s*|\s*$/g,
+ splitSpace = /\s+/;
+ // generate a short, numeric hash of a string
+ function okhash(x) {
+ if (!x || !x.length) return 0;
+ for (var i = 0, h = 0; i < x.length; i++) {
+ h = ((h << 5) - h) + x.charCodeAt(i) | 0;
+ } return h;
+ }
+ // all Y children of X
+ function get(x, y) { return x.getElementsByTagName(y); }
+ function attr(x, y) { return x.getAttribute(y); }
+ function attrf(x, y) { return parseFloat(attr(x, y)); }
+ // one Y child of X, if any, otherwise null
+ function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
+ function norm(el) { if (el.normalize) { el.normalize(); } return el; }
+ // cast array x into numbers
+ function numarray(x) {
+ for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); }
+ return o;
+ }
+ // get the content of a text node, if any
+ function nodeVal(x) {
+ if (x) { norm(x); }
+ return (x && x.textContent) || '';
+ }
+ // get the contents of multiple text nodes, if present
+ function getMulti(x, ys) {
+ var o = {}, n, k;
+ for (k = 0; k < ys.length; k++) {
+ n = get1(x, ys[k]);
+ if (n) o[ys[k]] = nodeVal(n);
+ }
+ return o;
+ }
+ // add properties of Y to X, overwriting if present in both
+ function extend(x, y) { for (var k in y) x[k] = y[k]; }
+ // get one coordinate from a coordinate array, if any
+ function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
+ // get all coordinates from a coordinate array as [[],[]]
+ function coord(v) {
+ var coords = v.replace(trimSpace, '').split(splitSpace),
+ o = [];
+ for (var i = 0; i < coords.length; i++) {
+ o.push(coord1(coords[i]));
+ }
+ return o;
+ }
+ function coordPair(x) {
+ var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
+ ele = get1(x, 'ele'),
+ // handle namespaced attribute in browser
+ heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
+ time = get1(x, 'time'),
+ e;
+ if (ele) {
+ e = parseFloat(nodeVal(ele));
+ if (!isNaN(e)) {
+ ll.push(e);
+ }
+ }
+ return {
+ coordinates: ll,
+ time: time ? nodeVal(time) : null,
+ heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
+ };
+ }
+
+ // create a new feature collection parent object
+ function fc() {
+ return {
+ type: 'FeatureCollection',
+ features: []
+ };
+ }
+
+ var serializer;
+ if (typeof XMLSerializer !== 'undefined') {
+ /* istanbul ignore next */
+ serializer = new XMLSerializer();
+ // only require xmldom in a node environment
+ } else if ('object' === 'object' && typeof process === 'object' && !process.browser) {
+ serializer = new (domParser.XMLSerializer)();
+ }
+ function xml2str(str) {
+ // IE9 will create a new XMLSerializer but it'll crash immediately.
+ // This line is ignored because we don't run coverage tests in IE9
+ /* istanbul ignore next */
+ if (str.xml !== undefined) return str.xml;
+ return serializer.serializeToString(str);
+ }
+
+ var t = {
+ kml: function(doc) {
+
+ var gj = fc(),
+ // styleindex keeps track of hashed styles in order to match features
+ styleIndex = {}, styleByHash = {},
+ // stylemapindex keeps track of style maps to expose in properties
+ styleMapIndex = {},
+ // atomic geospatial types supported by KML - MultiGeometry is
+ // handled separately
+ geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
+ // all root placemarks in the file
+ placemarks = get(doc, 'Placemark'),
+ styles = get(doc, 'Style'),
+ styleMaps = get(doc, 'StyleMap');
+
+ for (var k = 0; k < styles.length; k++) {
+ var hash = okhash(xml2str(styles[k])).toString(16);
+ styleIndex['#' + attr(styles[k], 'id')] = hash;
+ styleByHash[hash] = styles[k];
+ }
+ for (var l = 0; l < styleMaps.length; l++) {
+ styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
+ var pairs = get(styleMaps[l], 'Pair');
+ var pairsMap = {};
+ for (var m = 0; m < pairs.length; m++) {
+ pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
+ }
+ styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
+
+ }
+ for (var j = 0; j < placemarks.length; j++) {
+ gj.features = gj.features.concat(getPlacemark(placemarks[j]));
+ }
+ function kmlColor(v) {
+ var color, opacity;
+ v = v || '';
+ if (v.substr(0, 1) === '#') { v = v.substr(1); }
+ if (v.length === 6 || v.length === 3) { color = v; }
+ if (v.length === 8) {
+ opacity = parseInt(v.substr(0, 2), 16) / 255;
+ color = '#' + v.substr(6, 2) +
+ v.substr(4, 2) +
+ v.substr(2, 2);
+ }
+ return [color, isNaN(opacity) ? undefined : opacity];
+ }
+ function gxCoord(v) { return numarray(v.split(' ')); }
+ function gxCoords(root) {
+ var elems = get(root, 'coord', 'gx'), coords = [], times = [];
+ if (elems.length === 0) elems = get(root, 'gx:coord');
+ for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
+ var timeElems = get(root, 'when');
+ for (var j = 0; j < timeElems.length; j++) times.push(nodeVal(timeElems[j]));
+ return {
+ coords: coords,
+ times: times
+ };
+ }
+ function getGeometry(root) {
+ var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
+ if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }
+ if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
+ if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }
+ for (i = 0; i < geotypes.length; i++) {
+ geomNodes = get(root, geotypes[i]);
+ if (geomNodes) {
+ for (j = 0; j < geomNodes.length; j++) {
+ geomNode = geomNodes[j];
+ if (geotypes[i] === 'Point') {
+ geoms.push({
+ type: 'Point',
+ coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
+ });
+ } else if (geotypes[i] === 'LineString') {
+ geoms.push({
+ type: 'LineString',
+ coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
+ });
+ } else if (geotypes[i] === 'Polygon') {
+ var rings = get(geomNode, 'LinearRing'),
+ coords = [];
+ for (k = 0; k < rings.length; k++) {
+ coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
+ }
+ geoms.push({
+ type: 'Polygon',
+ coordinates: coords
+ });
+ } else if (geotypes[i] === 'Track' ||
+ geotypes[i] === 'gx:Track') {
+ var track = gxCoords(geomNode);
+ geoms.push({
+ type: 'LineString',
+ coordinates: track.coords
+ });
+ if (track.times.length) coordTimes.push(track.times);
+ }
+ }
+ }
+ }
+ return {
+ geoms: geoms,
+ coordTimes: coordTimes
+ };
+ }
+ function getPlacemark(root) {
+ var geomsAndTimes = getGeometry(root), i, properties = {},
+ name = nodeVal(get1(root, 'name')),
+ address = nodeVal(get1(root, 'address')),
+ styleUrl = nodeVal(get1(root, 'styleUrl')),
+ description = nodeVal(get1(root, 'description')),
+ timeSpan = get1(root, 'TimeSpan'),
+ timeStamp = get1(root, 'TimeStamp'),
+ extendedData = get1(root, 'ExtendedData'),
+ lineStyle = get1(root, 'LineStyle'),
+ polyStyle = get1(root, 'PolyStyle'),
+ visibility = get1(root, 'visibility');
+
+ if (!geomsAndTimes.geoms.length) return [];
+ if (name) properties.name = name;
+ if (address) properties.address = address;
+ if (styleUrl) {
+ if (styleUrl[0] !== '#') {
+ styleUrl = '#' + styleUrl;
+ }
+
+ properties.styleUrl = styleUrl;
+ if (styleIndex[styleUrl]) {
+ properties.styleHash = styleIndex[styleUrl];
+ }
+ if (styleMapIndex[styleUrl]) {
+ properties.styleMapHash = styleMapIndex[styleUrl];
+ properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
+ }
+ // Try to populate the lineStyle or polyStyle since we got the style hash
+ var style = styleByHash[properties.styleHash];
+ if (style) {
+ if (!lineStyle) lineStyle = get1(style, 'LineStyle');
+ if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
+ }
+ }
+ if (description) properties.description = description;
+ if (timeSpan) {
+ var begin = nodeVal(get1(timeSpan, 'begin'));
+ var end = nodeVal(get1(timeSpan, 'end'));
+ properties.timespan = { begin: begin, end: end };
+ }
+ if (timeStamp) {
+ properties.timestamp = nodeVal(get1(timeStamp, 'when'));
+ }
+ if (lineStyle) {
+ var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
+ color = linestyles[0],
+ opacity = linestyles[1],
+ width = parseFloat(nodeVal(get1(lineStyle, 'width')));
+ if (color) properties.stroke = color;
+ if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
+ if (!isNaN(width)) properties['stroke-width'] = width;
+ }
+ if (polyStyle) {
+ var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
+ pcolor = polystyles[0],
+ popacity = polystyles[1],
+ fill = nodeVal(get1(polyStyle, 'fill')),
+ outline = nodeVal(get1(polyStyle, 'outline'));
+ if (pcolor) properties.fill = pcolor;
+ if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
+ if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
+ if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
+ }
+ if (extendedData) {
+ var datas = get(extendedData, 'Data'),
+ simpleDatas = get(extendedData, 'SimpleData');
+
+ for (i = 0; i < datas.length; i++) {
+ properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
+ }
+ for (i = 0; i < simpleDatas.length; i++) {
+ properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
+ }
+ }
+ if (visibility) {
+ properties.visibility = nodeVal(visibility);
+ }
+ if (geomsAndTimes.coordTimes.length) {
+ properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?
+ geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
+ }
+ var feature = {
+ type: 'Feature',
+ geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
+ type: 'GeometryCollection',
+ geometries: geomsAndTimes.geoms
+ },
+ properties: properties
+ };
+ if (attr(root, 'id')) feature.id = attr(root, 'id');
+ return [feature];
+ }
+ return gj;
+ },
+ gpx: function(doc) {
+ var i,
+ tracks = get(doc, 'trk'),
+ routes = get(doc, 'rte'),
+ waypoints = get(doc, 'wpt'),
+ // a feature collection
+ gj = fc(),
+ feature;
+ for (i = 0; i < tracks.length; i++) {
+ feature = getTrack(tracks[i]);
+ if (feature) gj.features.push(feature);
+ }
+ for (i = 0; i < routes.length; i++) {
+ feature = getRoute(routes[i]);
+ if (feature) gj.features.push(feature);
+ }
+ for (i = 0; i < waypoints.length; i++) {
+ gj.features.push(getPoint(waypoints[i]));
+ }
+ function getPoints(node, pointname) {
+ var pts = get(node, pointname),
+ line = [],
+ times = [],
+ heartRates = [],
+ l = pts.length;
+ if (l < 2) return {}; // Invalid line in GeoJSON
+ for (var i = 0; i < l; i++) {
+ var c = coordPair(pts[i]);
+ line.push(c.coordinates);
+ if (c.time) times.push(c.time);
+ if (c.heartRate) heartRates.push(c.heartRate);
+ }
+ return {
+ line: line,
+ times: times,
+ heartRates: heartRates
+ };
+ }
+ function getTrack(node) {
+ var segments = get(node, 'trkseg'),
+ track = [],
+ times = [],
+ heartRates = [],
+ line;
+ for (var i = 0; i < segments.length; i++) {
+ line = getPoints(segments[i], 'trkpt');
+ if (line) {
+ if (line.line) track.push(line.line);
+ if (line.times && line.times.length) times.push(line.times);
+ if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
+ }
+ }
+ if (track.length === 0) return;
+ var properties = getProperties(node);
+ extend(properties, getLineStyle(get1(node, 'extensions')));
+ if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
+ if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
+ return {
+ type: 'Feature',
+ properties: properties,
+ geometry: {
+ type: track.length === 1 ? 'LineString' : 'MultiLineString',
+ coordinates: track.length === 1 ? track[0] : track
+ }
+ };
+ }
+ function getRoute(node) {
+ var line = getPoints(node, 'rtept');
+ if (!line.line) return;
+ var prop = getProperties(node);
+ extend(prop, getLineStyle(get1(node, 'extensions')));
+ var routeObj = {
+ type: 'Feature',
+ properties: prop,
+ geometry: {
+ type: 'LineString',
+ coordinates: line.line
+ }
+ };
+ return routeObj;
+ }
+ function getPoint(node) {
+ var prop = getProperties(node);
+ extend(prop, getMulti(node, ['sym']));
+ return {
+ type: 'Feature',
+ properties: prop,
+ geometry: {
+ type: 'Point',
+ coordinates: coordPair(node).coordinates
+ }
+ };
+ }
+ function getLineStyle(extensions) {
+ var style = {};
+ if (extensions) {
+ var lineStyle = get1(extensions, 'line');
+ if (lineStyle) {
+ var color = nodeVal(get1(lineStyle, 'color')),
+ opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
+ width = parseFloat(nodeVal(get1(lineStyle, 'width')));
+ if (color) style.stroke = color;
+ if (!isNaN(opacity)) style['stroke-opacity'] = opacity;
+ // GPX width is in mm, convert to px with 96 px per inch
+ if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
+ }
+ }
+ return style;
+ }
+ function getProperties(node) {
+ var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
+ links = get(node, 'link');
+ if (links.length) prop.links = [];
+ for (var i = 0, link; i < links.length; i++) {
+ link = { href: attr(links[i], 'href') };
+ extend(link, getMulti(links[i], ['text', 'type']));
+ prop.links.push(link);
+ }
+ return prop;
+ }
+ return gj;
+ }
+ };
+ return t;
+})();
+
+module.exports = toGeoJSON;
+});
+
+function svgGpx(projection$$1, context, dispatch$$1) {
+ var showLabels = true,
+ detected = utilDetect(),
+ layer;
+
+
+ function init() {
+ if (svgGpx.initialized) return; // run once
+
+ svgGpx.geojson = {};
+ svgGpx.enabled = true;
+
+ function over() {
+ event.stopPropagation();
+ event.preventDefault();
+ event.dataTransfer.dropEffect = 'copy';
+ }
+
+ select('body')
+ .attr('dropzone', 'copy')
+ .on('drop.localgpx', function() {
+ event.stopPropagation();
+ event.preventDefault();
+ if (!detected.filedrop) return;
+ drawGpx.files(event.dataTransfer.files);
+ })
+ .on('dragenter.localgpx', over)
+ .on('dragexit.localgpx', over)
+ .on('dragover.localgpx', over);
+
+ svgGpx.initialized = true;
+ }
+
+
+ function drawGpx(selection$$1) {
+ var geojson = svgGpx.geojson,
+ enabled = svgGpx.enabled;
+
+ layer = selection$$1.selectAll('.layer-gpx')
+ .data(enabled ? [0] : []);
+
+ layer.exit()
+ .remove();
+
+ layer = layer.enter()
+ .append('g')
+ .attr('class', 'layer-gpx')
+ .merge(layer);
+
+
+ var paths = layer
+ .selectAll('path')
+ .data([geojson]);
+
+ paths.exit()
+ .remove();
+
+ paths = paths.enter()
+ .append('path')
+ .attr('class', 'gpx')
+ .merge(paths);
+
+
+ var path$$1 = index$4(projection$$1);
+
+ paths
+ .attr('d', path$$1);
+
+
+ var labels = layer.selectAll('text')
+ .data(showLabels && geojson.features ? geojson.features : []);
+
+ labels.exit()
+ .remove();
+
+ labels = labels.enter()
+ .append('text')
+ .attr('class', 'gpx')
+ .merge(labels);
+
+ labels
+ .text(function(d) {
+ return d.properties.desc || d.properties.name;
+ })
+ .attr('x', function(d) {
+ var centroid = path$$1.centroid(d);
+ return centroid[0] + 7;
+ })
+ .attr('y', function(d) {
+ var centroid = path$$1.centroid(d);
+ return centroid[1];
+ });
+
+ }
+
+
+ function toDom(x) {
+ return (new DOMParser()).parseFromString(x, 'text/xml');
+ }
+
+
+ function getExtension(fileName) {
+ if (lodash.isUndefined(fileName)) {
+ return '';
+ }
+
+ var lastDotIndex = fileName.lastIndexOf('.');
+ if (lastDotIndex < 0) {
+ return '';
+ }
+
+ return fileName.substr(lastDotIndex);
+ }
+
+
+ function parseSaveAndZoom(extension, data) {
+ switch (extension) {
+ default:
+ drawGpx.geojson(togeojson.gpx(toDom(data))).fitZoom();
+ break;
+ case '.kml':
+ drawGpx.geojson(togeojson.kml(toDom(data))).fitZoom();
+ break;
+ case '.geojson':
+ case '.json':
+ drawGpx.geojson(JSON.parse(data)).fitZoom();
+ break;
+ }
+ }
+
+
+ drawGpx.showLabels = function(_) {
+ if (!arguments.length) return showLabels;
+ showLabels = _;
+ return this;
+ };
+
+
+ drawGpx.enabled = function(_) {
+ if (!arguments.length) return svgGpx.enabled;
+ svgGpx.enabled = _;
+ dispatch$$1.call('change');
+ return this;
+ };
+
+
+ drawGpx.hasGpx = function() {
+ var geojson = svgGpx.geojson;
+ return (!(lodash.isEmpty(geojson) || lodash.isEmpty(geojson.features)));
+ };
+
+
+ drawGpx.geojson = function(gj) {
+ if (!arguments.length) return svgGpx.geojson;
+ if (lodash.isEmpty(gj) || lodash.isEmpty(gj.features)) return this;
+ svgGpx.geojson = gj;
+ dispatch$$1.call('change');
+ return this;
+ };
+
+
+ drawGpx.url = function(url) {
+ text(url, function(err, data) {
+ if (!err) {
+ var extension = getExtension(url);
+ parseSaveAndZoom(extension, data);
+ }
+ });
+ return this;
+ };
+
+
+ drawGpx.files = function(fileList) {
+ if (!fileList.length) return this;
+ var f = fileList[0],
+ reader = new FileReader();
+
+ reader.onload = (function(file) {
+ var extension = getExtension(file.name);
+
+ return function (e) {
+ parseSaveAndZoom(extension, e.target.result);
+ };
+ })(f);
+
+ reader.readAsText(f);
+ return this;
+ };
+
+
+ drawGpx.fitZoom = function() {
+ if (!this.hasGpx()) return this;
+ var geojson = svgGpx.geojson;
+
+ var map = context.map(),
+ viewport = map.trimmedExtent().polygon(),
+ coords = lodash.reduce(geojson.features, function(coords, feature) {
+ var c = feature.geometry.coordinates;
+
+ /* eslint-disable no-fallthrough */
+ switch (feature.geometry.type) {
+ case 'Point':
+ c = [c];
+ case 'MultiPoint':
+ case 'LineString':
+ break;
+
+ case 'MultiPolygon':
+ c = lodash.flatten(c);
+ case 'Polygon':
+ case 'MultiLineString':
+ c = lodash.flatten(c);
+ break;
+ }
+ /* eslint-enable no-fallthrough */
+
+ return lodash.union(coords, c);
+ }, []);
+
+ if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
+ var extent$$1 = geoExtent$$1(bounds({ type: 'LineString', coordinates: coords }));
+ map.centerZoom(extent$$1.center(), map.trimmedExtentZoom(extent$$1));
+ }
+
+ return this;
+ };
+
+
+ init();
+ return drawGpx;
+}
+
+function svgIcon(name, svgklass, useklass) {
+ return function drawIcon(selection) {
+ selection.selectAll('svg')
+ .data([0])
+ .enter()
+ .append('svg')
+ .attr('class', 'icon ' + (svgklass || ''))
+ .append('use')
+ .attr('xlink:href', name)
+ .attr('class', useklass);
+ };
+}
+
+function svgLabels(projection$$1, context) {
+ var path$$1 = index$4(projection$$1),
+ detected = utilDetect(),
+ baselineHack = (detected.ie || detected.browser.toLowerCase() === 'edge'),
+ rdrawn = index$9(),
+ rskipped = index$9(),
+ textWidthCache = {},
+ entitybboxes = {};
+
+ // Listed from highest to lowest priority
+ var labelStack = [
+ ['line', 'aeroway', '*', 12],
+ ['line', 'highway', 'motorway', 12],
+ ['line', 'highway', 'trunk', 12],
+ ['line', 'highway', 'primary', 12],
+ ['line', 'highway', 'secondary', 12],
+ ['line', 'highway', 'tertiary', 12],
+ ['line', 'highway', '*', 12],
+ ['line', 'railway', '*', 12],
+ ['line', 'waterway', '*', 12],
+ ['area', 'aeroway', '*', 12],
+ ['area', 'amenity', '*', 12],
+ ['area', 'building', '*', 12],
+ ['area', 'historic', '*', 12],
+ ['area', 'leisure', '*', 12],
+ ['area', 'man_made', '*', 12],
+ ['area', 'natural', '*', 12],
+ ['area', 'shop', '*', 12],
+ ['area', 'tourism', '*', 12],
+ ['area', 'camp_site', '*', 12],
+ ['point', 'aeroway', '*', 10],
+ ['point', 'amenity', '*', 10],
+ ['point', 'building', '*', 10],
+ ['point', 'historic', '*', 10],
+ ['point', 'leisure', '*', 10],
+ ['point', 'man_made', '*', 10],
+ ['point', 'natural', '*', 10],
+ ['point', 'shop', '*', 10],
+ ['point', 'tourism', '*', 10],
+ ['point', 'camp_site', '*', 10],
+ ['line', 'name', '*', 12],
+ ['area', 'name', '*', 12],
+ ['point', 'name', '*', 10]
+ ];
+
+
+ function blacklisted(preset) {
+ var noIcons = ['building', 'landuse', 'natural'];
+ return lodash.some(noIcons, function(s) {
+ return preset.id.indexOf(s) >= 0;
+ });
+ }
+
+
+ function get(array, prop) {
+ return function(d, i) { return array[i][prop]; };
+ }
+
+
+ function textWidth(text$$1, size, elem) {
+ var c = textWidthCache[size];
+ if (!c) c = textWidthCache[size] = {};
+
+ if (c[text$$1]) {
+ return c[text$$1];
+
+ } else if (elem) {
+ c[text$$1] = elem.getComputedTextLength();
+ return c[text$$1];
+
+ } else {
+ var str = encodeURIComponent(text$$1).match(/%[CDEFcdef]/g);
+ if (str === null) {
+ return size / 3 * 2 * text$$1.length;
+ } else {
+ return size / 3 * (2 * text$$1.length + str.length);
+ }
+ }
+ }
+
+
+ function drawLinePaths(selection$$1, entities, filter, classes, labels) {
+ var paths = selection$$1.selectAll('path')
+ .filter(filter)
+ .data(entities, osmEntity$$1.key);
+
+ paths.exit()
+ .remove();
+
+ paths.enter()
+ .append('path')
+ .style('stroke-width', get(labels, 'font-size'))
+ .attr('id', function(d) { return 'labelpath-' + d.id; })
+ .attr('class', classes)
+ .merge(paths)
+ .attr('d', get(labels, 'lineString'));
+ }
+
+
+ function drawLineLabels(selection$$1, entities, filter, classes, labels) {
+ var texts = selection$$1.selectAll('text.' + classes)
+ .filter(filter)
+ .data(entities, osmEntity$$1.key);
+
+ texts.exit()
+ .remove();
+
+ texts.enter()
+ .append('text')
+ .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
+ .attr('dy', baselineHack ? '0.35em' : null)
+ .append('textPath')
+ .attr('class', 'textpath');
+
+ texts = selection$$1.selectAll('text.' + classes);
+
+ texts.selectAll('.textpath')
+ .filter(filter)
+ .data(entities, osmEntity$$1.key)
+ .attr('startOffset', '50%')
+ .attr('xlink:href', function(d) { return '#labelpath-' + d.id; })
+ .text(utilDisplayNameForPath);
+ }
+
+
+ function drawPointLabels(selection$$1, entities, filter, classes, labels) {
+ var texts = selection$$1.selectAll('text.' + classes)
+ .filter(filter)
+ .data(entities, osmEntity$$1.key);
+
+ texts.exit()
+ .remove();
+
+ texts = texts.enter()
+ .append('text')
+ .attr('class', function(d, i) {
+ return classes + ' ' + labels[i].classes + ' ' + d.id;
+ })
+ .merge(texts);
+
+ texts
+ .attr('x', get(labels, 'x'))
+ .attr('y', get(labels, 'y'))
+ .style('text-anchor', get(labels, 'textAnchor'))
+ .text(utilDisplayName)
+ .each(function(d, i) {
+ textWidth(utilDisplayName(d), labels[i].height, this);
+ });
+ }
+
+
+ function drawAreaLabels(selection$$1, entities, filter, classes, labels) {
+ entities = entities.filter(hasText);
+ labels = labels.filter(hasText);
+ drawPointLabels(selection$$1, entities, filter, classes, labels);
+
+ function hasText(d, i) {
+ return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
+ }
+ }
+
+
+ function drawAreaIcons(selection$$1, entities, filter, classes, labels) {
+ var icons = selection$$1.selectAll('use.' + classes)
+ .filter(filter)
+ .data(entities, osmEntity$$1.key);
+
+ icons.exit()
+ .remove();
+
+ icons = icons.enter()
+ .append('use')
+ .attr('class', 'icon ' + classes)
+ .attr('width', '17px')
+ .attr('height', '17px')
+ .merge(icons);
+
+ icons
+ .attr('transform', get(labels, 'transform'))
+ .attr('xlink:href', function(d) {
+ var preset = context.presets().match(d, context.graph()),
+ picon = preset && preset.icon;
+
+ if (!picon)
+ return '';
+ else {
+ var isMaki = dataFeatureIcons.indexOf(picon) !== -1;
+ return '#' + picon + (isMaki ? '-15' : '');
+ }
+ });
+ }
+
+
+ function drawCollisionBoxes(selection$$1, rtree, which) {
+ var showDebug = context.getDebug('collision'),
+ classes = 'debug ' + which + ' ' +
+ (which === 'debug-skipped' ? 'orange' : 'yellow');
+
+ var debug = selection$$1.selectAll('.layer-label-debug')
+ .data(showDebug ? [true] : []);
+
+ debug.exit()
+ .remove();
+
+ debug = debug.enter()
+ .append('g')
+ .attr('class', 'layer-label-debug')
+ .merge(debug);
+
+ if (showDebug) {
+ var gj = rtree.all().map(function(d) {
+ return { type: 'Polygon', coordinates: [[
+ [d.minX, d.minY],
+ [d.maxX, d.minY],
+ [d.maxX, d.maxY],
+ [d.minX, d.maxY],
+ [d.minX, d.minY]
+ ]]};
+ });
+
+ var debugboxes = debug.selectAll('.' + which)
+ .data(gj);
+
+ debugboxes.exit()
+ .remove();
+
+ debugboxes = debugboxes.enter()
+ .append('path')
+ .attr('class', classes)
+ .merge(debugboxes);
+
+ debugboxes
+ .attr('d', index$4());
+ }
+ }
+
+
+ function drawLabels(selection$$1, graph, entities, filter, dimensions, fullRedraw) {
+ var lowZoom = context.surface().classed('low-zoom');
+
+ var labelable = [], i, j, k, entity, geometry;
+ for (i = 0; i < labelStack.length; i++) {
+ labelable.push([]);
+ }
+
+ if (fullRedraw) {
+ rdrawn.clear();
+ rskipped.clear();
+ entitybboxes = {};
+ } else {
+ for (i = 0; i < entities.length; i++) {
+ entity = entities[i];
+ var toRemove = []
+ .concat(entitybboxes[entity.id] || [])
+ .concat(entitybboxes[entity.id + 'I'] || []);
+
+ for (j = 0; j < toRemove.length; j++) {
+ rdrawn.remove(toRemove[j]);
+ rskipped.remove(toRemove[j]);
+ }
+ }
+ }
+
+ // Split entities into groups specified by labelStack
+ for (i = 0; i < entities.length; i++) {
+ entity = entities[i];
+ geometry = entity.geometry(graph);
+ if (geometry === 'vertex') { geometry = 'point'; } // treat vertex like point
+
+ var preset = geometry === 'area' && context.presets().match(entity, graph),
+ icon = preset && !blacklisted(preset) && preset.icon;
+
+ if (!icon && !utilDisplayName(entity))
+ continue;
+
+ for (k = 0; k < labelStack.length; k++) {
+ var matchGeom = labelStack[k][0],
+ matchKey = labelStack[k][1],
+ matchVal = labelStack[k][2],
+ hasVal = entity.tags[matchKey];
+
+ if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
+ labelable[k].push(entity);
+ break;
+ }
+ }
+ }
+
+ var positions = {
+ point: [],
+ line: [],
+ area: []
+ };
+
+ var labelled = {
+ point: [],
+ line: [],
+ area: []
+ };
+
+ // Try and find a valid label for labellable entities
+ for (k = 0; k < labelable.length; k++) {
+ var fontSize = labelStack[k][3];
+ for (i = 0; i < labelable[k].length; i++) {
+ entity = labelable[k][i];
+ geometry = entity.geometry(graph);
+
+ var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName,
+ name = getName(entity),
+ width = name && textWidth(name, fontSize),
+ p;
+
+ if (geometry === 'point') {
+ p = getPointLabel(entity, width, fontSize, geometry);
+ } else if (geometry === 'vertex' && !lowZoom) {
+ // don't label vertices at low zoom because they don't have icons
+ p = getPointLabel(entity, width, fontSize, geometry);
+ } else if (geometry === 'line') {
+ p = getLineLabel(entity, width, fontSize);
+ } else if (geometry === 'area') {
+ p = getAreaLabel(entity, width, fontSize);
+ }
+
+ if (p) {
+ if (geometry === 'vertex') { geometry = 'point'; } // treat vertex like point
+ p.classes = geometry + ' tag-' + labelStack[k][1];
+ positions[geometry].push(p);
+ labelled[geometry].push(entity);
+ }
+ }
+ }
+
+
+ function getPointLabel(entity, width, height, geometry) {
+ var y = (geometry === 'point' ? -12 : 0),
+ pointOffsets = {
+ ltr: [15, y, 'start'],
+ rtl: [-15, y, 'end']
+ };
+
+ var coord = projection$$1(entity.loc),
+ margin = 2,
+ offset = pointOffsets[textDirection],
+ p = {
+ height: height,
+ width: width,
+ x: coord[0] + offset[0],
+ y: coord[1] + offset[1],
+ textAnchor: offset[2]
+ },
+ bbox;
+
+ if (textDirection === 'rtl') {
+ bbox = {
+ minX: p.x - width - margin,
+ minY: p.y - (height / 2) - margin,
+ maxX: p.x + margin,
+ maxY: p.y + (height / 2) + margin
+ };
+ } else {
+ bbox = {
+ minX: p.x - margin,
+ minY: p.y - (height / 2) - margin,
+ maxX: p.x + width + margin,
+ maxY: p.y + (height / 2) + margin
+ };
+ }
+
+ if (tryInsert([bbox], entity.id, true)) {
+ return p;
+ }
+ }
+
+
+ function getLineLabel(entity, width, height) {
+ var viewport = geoExtent$$1(context.projection.clipExtent()).polygon(),
+ nodes = lodash.map(graph.childNodes(entity), 'loc').map(projection$$1),
+ length = geoPathLength(nodes);
+
+ if (length < width + 20) return;
+
+ // % along the line to attempt to place the label
+ var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70,
+ 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
+ var margin = 3;
+
+ for (var i = 0; i < lineOffsets.length; i++) {
+ var offset = lineOffsets[i],
+ middle = offset / 100 * length,
+ start = middle - width / 2;
+
+ if (start < 0 || start + width > length) continue;
+
+ // generate subpath and ignore paths that are invalid or don't cross viewport.
+ var sub = subpath(nodes, start, start + width);
+ if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
+ continue;
+ }
+
+ var isReverse = reverse$$1(sub);
+ if (isReverse) {
+ sub = sub.reverse();
+ }
+
+ var bboxes = [],
+ boxsize = (height + 2) / 2;
+
+ for (var j = 0; j < sub.length - 1; j++) {
+ var a = sub[j];
+ var b = sub[j + 1];
+ var num = Math.max(1, Math.floor(geoEuclideanDistance(a, b) / boxsize / 2));
+
+ for (var box = 0; box < num; box++) {
+ var p = geoInterp(a, b, box / num);
+ var x0 = p[0] - boxsize - margin;
+ var y0 = p[1] - boxsize - margin;
+ var x1 = p[0] + boxsize + margin;
+ var y1 = p[1] + boxsize + margin;
+
+ bboxes.push({
+ minX: Math.min(x0, x1),
+ minY: Math.min(y0, y1),
+ maxX: Math.max(x0, x1),
+ maxY: Math.max(y0, y1)
+ });
+ }
+ }
+
+ if (tryInsert(bboxes, entity.id, false)) {
+ return {
+ 'font-size': height + 2,
+ lineString: lineString(sub),
+ startOffset: offset + '%'
+ };
+ }
+ }
+
+ function reverse$$1(p) {
+ var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
+ return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
+ }
+
+ function lineString(nodes) {
+ return 'M' + nodes.join('L');
+ }
+
+ function subpath(nodes, from, to) {
+ var sofar = 0,
+ start, end, i0, i1;
+
+ for (var i = 0; i < nodes.length - 1; i++) {
+ var a = nodes[i],
+ b = nodes[i + 1];
+ var current = geoEuclideanDistance(a, b);
+ var portion;
+ if (!start && sofar + current >= from) {
+ portion = (from - sofar) / current;
+ start = [
+ a[0] + portion * (b[0] - a[0]),
+ a[1] + portion * (b[1] - a[1])
+ ];
+ i0 = i + 1;
+ }
+ if (!end && sofar + current >= to) {
+ portion = (to - sofar) / current;
+ end = [
+ a[0] + portion * (b[0] - a[0]),
+ a[1] + portion * (b[1] - a[1])
+ ];
+ i1 = i + 1;
+ }
+ sofar += current;
+ }
+
+ var ret = nodes.slice(i0, i1);
+ ret.unshift(start);
+ ret.push(end);
+ return ret;
+ }
+ }
+
+
+ function getAreaLabel(entity, width, height) {
+ var centroid = path$$1.centroid(entity.asGeoJSON(graph, true)),
+ extent$$1 = entity.extent(graph),
+ entitywidth = projection$$1(extent$$1[1])[0] - projection$$1(extent$$1[0])[0];
+
+ if (isNaN(centroid[0]) || entitywidth < 20) return;
+
+ var iconSize = 20,
+ iconX = centroid[0] - (iconSize / 2),
+ iconY = centroid[1] - (iconSize / 2),
+ margin = 2,
+ textOffset = iconSize + margin,
+ p = { transform: 'translate(' + iconX + ',' + iconY + ')' };
+
+ var bbox = {
+ minX: iconX,
+ minY: iconY,
+ maxX: iconX + iconSize,
+ maxY: iconY + iconSize
+ };
+
+ // try to add icon
+ if (tryInsert([bbox], entity.id + 'I', true)) {
+ if (width && entitywidth >= width + 20) {
+ var labelX = centroid[0],
+ labelY = centroid[1] + textOffset;
+
+ bbox = {
+ minX: labelX - (width / 2) - margin,
+ minY: labelY - (height / 2) - margin,
+ maxX: labelX + (width / 2) + margin,
+ maxY: labelY + (height / 2) + margin
+ };
+
+ // try to add label
+ if (tryInsert([bbox], entity.id, true)) {
+ p.x = labelX;
+ p.y = labelY;
+ p.textAnchor = 'middle';
+ p.height = height;
+ }
+ }
+
+ return p;
+ }
+ }
+
+
+ function tryInsert(bboxes, id, saveSkipped) {
+ var skipped = false,
+ bbox;
+
+ for (var i = 0; i < bboxes.length; i++) {
+ bbox = bboxes[i];
+ bbox.id = id;
+
+ // Check that label is visible
+ if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
+ skipped = true;
+ break;
+ }
+ if (rdrawn.collides(bbox)) {
+ skipped = true;
+ break;
+ }
+ }
+
+ entitybboxes[id] = bboxes;
+
+ if (skipped) {
+ if (saveSkipped) {
+ rskipped.load(bboxes);
+ }
+ } else {
+ rdrawn.load(bboxes);
+ }
+
+ return !skipped;
+ }
+
+
+ var label = selection$$1.selectAll('.layer-label'),
+ halo = selection$$1.selectAll('.layer-halo');
+
+ // points
+ drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
+ drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
+
+ // lines
+ drawLinePaths(halo, labelled.line, filter, '', positions.line);
+ drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
+ drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
+
+ // areas
+ drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
+ drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
+ drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
+ drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area);
+
+ // debug
+ drawCollisionBoxes(label, rskipped, 'debug-skipped');
+ drawCollisionBoxes(label, rdrawn, 'debug-drawn');
+
+ selection$$1.call(filterLabels);
+ }
+
+
+ function filterLabels(selection$$1) {
+ var layers = selection$$1
+ .selectAll('.layer-label, .layer-halo');
+
+ layers.selectAll('.proximate')
+ .classed('proximate', false);
+
+ var mouse$$1 = context.mouse(),
+ graph = context.graph(),
+ selectedIDs = context.selectedIDs(),
+ ids = [],
+ pad, bbox;
+
+ // hide labels near the mouse
+ if (mouse$$1) {
+ pad = 20;
+ bbox = { minX: mouse$$1[0] - pad, minY: mouse$$1[1] - pad, maxX: mouse$$1[0] + pad, maxY: mouse$$1[1] + pad };
+ ids.push.apply(ids, lodash.map(rdrawn.search(bbox), 'id'));
+ }
+
+ // hide labels along selected ways, or near selected vertices
+ for (var i = 0; i < selectedIDs.length; i++) {
+ var entity = graph.hasEntity(selectedIDs[i]);
+ if (!entity) continue;
+ var geometry = entity.geometry(graph);
+
+ if (geometry === 'line') {
+ ids.push(selectedIDs[i]);
+ } else if (geometry === 'vertex') {
+ var point = context.projection(entity.loc);
+ pad = 10;
+ bbox = { minX: point[0] - pad, minY: point[1] - pad, maxX: point[0] + pad, maxY: point[1] + pad };
+ ids.push.apply(ids, lodash.map(rdrawn.search(bbox), 'id'));
+ }
+ }
+
+ layers.selectAll(utilEntitySelector(ids))
+ .classed('proximate', true);
+ }
+
+
+ var throttleFilterLabels = lodash.throttle(filterLabels, 100);
+
+
+ drawLabels.observe = function(selection$$1) {
+ var listener = function() { throttleFilterLabels(selection$$1); };
+ selection$$1.on('mousemove.hidelabels', listener);
+ context.on('enter.hidelabels', listener);
+ };
+
+
+ drawLabels.off = function(selection$$1) {
+ throttleFilterLabels.cancel();
+ selection$$1.on('mousemove.hidelabels', null);
+ context.on('enter.hidelabels', null);
+ };
+
+
+ return drawLabels;
+}
+
+function refresh(selection, node) {
+ var cr = node.getBoundingClientRect();
+ var prop = [cr.width, cr.height];
+ selection.property('__dimensions__', prop);
+ return prop;
+}
+
+function utilGetDimensions(selection, force) {
+ if (!selection || selection.empty()) {
+ return [0, 0];
+ }
+ var node = selection.node(),
+ cached = selection.property('__dimensions__');
+ return (!cached || force) ? refresh(selection, node) : cached;
+}
+
+
+function utilSetDimensions(selection, dimensions) {
+ if (!selection || selection.empty()) {
+ return selection;
+ }
+ var node = selection.node();
+ if (dimensions === null) {
+ refresh(selection, node);
+ return selection;
+ }
+ return selection
+ .property('__dimensions__', [dimensions[0], dimensions[1]])
+ .attr('width', dimensions[0])
+ .attr('height', dimensions[1]);
+}
+
+function svgPointTransform(projection) {
+ return function(entity) {
+ // http://jsperf.com/short-array-join
+ var pt = projection(entity.loc);
+ return 'translate(' + pt[0] + ',' + pt[1] + ')';
+ };
+}
+
+function d3geoTile() {
+ var size = [960, 500],
+ scale = 256,
+ scaleExtent = [0, 20],
+ translate = [size[0] / 2, size[1] / 2],
+ zoomDelta = 0;
+
+ function bound(_) {
+ return Math.min(scaleExtent[1], Math.max(scaleExtent[0], _));
+ }
+
+ function tile() {
+ var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0),
+ z0 = bound(Math.round(z + zoomDelta)),
+ k = Math.pow(2, z - z0 + 8),
+ origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k],
+ tiles = [],
+ cols = sequence(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))),
+ rows = sequence(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1])));
+
+ rows.forEach(function(y) {
+ cols.forEach(function(x) {
+ tiles.push([x, y, z0]);
+ });
+ });
+
+ tiles.translate = origin;
+ tiles.scale = k;
+
+ return tiles;
+ }
+
+ tile.scaleExtent = function(_) {
+ if (!arguments.length) return scaleExtent;
+ scaleExtent = _;
+ return tile;
+ };
+
+ tile.size = function(_) {
+ if (!arguments.length) return size;
+ size = _;
+ return tile;
+ };
+
+ tile.scale = function(_) {
+ if (!arguments.length) return scale;
+ scale = _;
+ return tile;
+ };
+
+ tile.translate = function(_) {
+ if (!arguments.length) return translate;
+ translate = _;
+ return tile;
+ };
+
+ tile.zoomDelta = function(_) {
+ if (!arguments.length) return zoomDelta;
+ zoomDelta = +_;
+ return tile;
+ };
+
+ return tile;
+}
+
+/* global Mapillary:false */
+var apibase = 'https://a.mapillary.com/v3/';
+var viewercss = 'mapillary-js/mapillary.min.css';
+var viewerjs = 'mapillary-js/mapillary.min.js';
+var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
+var maxResults = 1000;
+var tileZoom = 14;
+var dispatch$2 = dispatch('loadedImages', 'loadedSigns');
+var mapillaryCache;
+var mapillaryClicks;
+var mapillaryImage;
+var mapillarySignDefs;
+var mapillarySignSprite;
+var mapillaryViewer;
+
+
+function abortRequest(i) {
+ i.abort();
+}
+
+
+function nearNullIsland(x, y, z) {
+ if (z >= 7) {
+ var center = Math.pow(2, z - 1),
+ width = Math.pow(2, z - 6),
+ min$$1 = center - (width / 2),
+ max$$1 = center + (width / 2) - 1;
+ return x >= min$$1 && x <= max$$1 && y >= min$$1 && y <= max$$1;
+ }
+ return false;
+}
+
+
+function maxPageAtZoom(z) {
+ if (z < 15) return 2;
+ if (z === 15) return 5;
+ if (z === 16) return 10;
+ if (z === 17) return 20;
+ if (z === 18) return 40;
+ if (z > 18) return 80;
+}
+
+
+function getTiles(projection$$1) {
+ var s = projection$$1.scale() * 2 * Math.PI,
+ z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
+ ts = 256 * Math.pow(2, z - tileZoom),
+ origin = [
+ s / 2 - projection$$1.translate()[0],
+ s / 2 - projection$$1.translate()[1]];
+
+ return d3geoTile()
+ .scaleExtent([tileZoom, tileZoom])
+ .scale(s)
+ .size(projection$$1.clipExtent()[1])
+ .translate(projection$$1.translate())()
+ .map(function(tile) {
+ var x = tile[0] * ts - origin[0],
+ y = tile[1] * ts - origin[1];
+
+ return {
+ id: tile.toString(),
+ xyz: tile,
+ extent: geoExtent$$1(
+ projection$$1.invert([x, y + ts]),
+ projection$$1.invert([x + ts, y])
+ )
+ };
+ });
+}
+
+
+function loadTiles(which, url, projection$$1) {
+ var s = projection$$1.scale() * 2 * Math.PI,
+ currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0));
+
+ var tiles = getTiles(projection$$1).filter(function(t) {
+ return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]);
+ });
+
+ lodash.filter(which.inflight, function(v, k) {
+ var wanted = lodash.find(tiles, function(tile) { return k === (tile.id + ',0'); });
+ if (!wanted) delete which.inflight[k];
+ return !wanted;
+ }).map(abortRequest);
+
+ tiles.forEach(function(tile) {
+ loadNextTilePage(which, currZoom, url, tile);
+ });
+}
+
+
+function loadNextTilePage(which, currZoom, url, tile) {
+ var cache = mapillaryCache[which],
+ rect = tile.extent.rectangle(),
+ maxPages = maxPageAtZoom(currZoom),
+ nextPage = cache.nextPage[tile.id] || 0,
+ nextURL = cache.nextURL[tile.id] || url +
+ utilQsString({
+ per_page: maxResults,
+ page: nextPage,
+ client_id: clientId,
+ bbox: [rect[0], rect[1], rect[2], rect[3]].join(','),
+ });
+
+ if (nextPage > maxPages) return;
+
+ var id = tile.id + ',' + String(nextPage);
+ if (cache.loaded[id] || cache.inflight[id]) return;
+ cache.inflight[id] = request(nextURL)
+ .mimeType('application/json')
+ .response(function(xhr) {
+ var linkHeader = xhr.getResponseHeader('Link');
+ if (linkHeader) {
+ var pagination = parsePagination(xhr.getResponseHeader('Link'));
+ if (pagination.next) {
+ cache.nextURL[tile.id] = pagination.next;
+ }
+ }
+
+ return JSON.parse(xhr.responseText); })
+ .get(function(err, data) {
+ cache.loaded[id] = true;
+ delete cache.inflight[id];
+ if (err || !data.features || !data.features.length) return;
+
+ var features = [],
+ feature, loc, d;
+
+ for (var i = 0; i < data.features.length; i++) {
+ feature = data.features[i];
+ loc = feature.geometry.coordinates;
+ d = { key: feature.properties.key, loc: loc };
+ if (which === 'images') d = { ca: feature.properties.ca, key: feature.properties.key, loc: loc };
+ if (which === 'signs') d = { key: feature.properties.detections[0].image_key, loc: loc, value: feature.properties.value };
+
+ features.push({minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d});
+ }
+
+ cache.rtree.load(features);
+
+ if (which === 'images') dispatch$2.call('loadedImages');
+ if (which === 'signs') dispatch$2.call('loadedSigns');
+ if (data.features.length === maxResults) { // more pages to load
+ cache.nextPage[tile.id] = nextPage + 1;
+ loadNextTilePage(which, currZoom, url, tile);
+ } else {
+ cache.nextPage[tile.id] = Infinity; // no more pages to load
+ }
+ });
+}
+
+// extract links to pages of API results
+function parsePagination(links) {
+ return links.split(',').map(function(rel) {
+ var elements = rel.split(';');
+ if (elements.length === 2) {
+ return [
+ /<(.+)>/.exec(elements[0])[1],
+ /rel="(.+)"/.exec(elements[1])[1]
+ ];
+ } else {
+ return ['',''];
+ }
+ }).reduce(function(pagination, val) {
+ pagination[val[1]] = val[0];
+ return pagination;
+ }, {});
+}
+
+
+// partition viewport into `psize` x `psize` regions
+function partitionViewport(psize, projection$$1) {
+ var dimensions = projection$$1.clipExtent()[1];
+ psize = psize || 16;
+ var cols = sequence(0, dimensions[0], psize),
+ rows = sequence(0, dimensions[1], psize),
+ partitions = [];
+
+ rows.forEach(function(y) {
+ cols.forEach(function(x) {
+ var min$$1 = [x, y + psize],
+ max$$1 = [x + psize, y];
+ partitions.push(
+ geoExtent$$1(projection$$1.invert(min$$1), projection$$1.invert(max$$1)));
+ });
+ });
+
+ return partitions;
+}
+
+
+// no more than `limit` results per partition.
+function searchLimited(psize, limit, projection$$1, rtree) {
+ limit = limit || 3;
+
+ var partitions = partitionViewport(psize, projection$$1);
+ return lodash.flatten(lodash.compact(lodash.map(partitions, function(extent$$1) {
+ return rtree.search(extent$$1.bbox())
+ .slice(0, limit)
+ .map(function(d) { return d.data; });
+ })));
+}
+
+
+
+var serviceMapillary = {
+
+ init: function() {
+ if (!mapillaryCache) {
+ this.reset();
+ }
+
+ this.event = utilRebind(this, dispatch$2, 'on');
+ },
+
+ reset: function() {
+ var cache = mapillaryCache;
+
+ if (cache) {
+ if (cache.images && cache.images.inflight) {
+ lodash.forEach(cache.images.inflight, abortRequest);
+ }
+ if (cache.signs && cache.signs.inflight) {
+ lodash.forEach(cache.signs.inflight, abortRequest);
+ }
+ }
+
+ mapillaryCache = {
+ images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: index$9() },
+ signs: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: index$9() }
+ };
+
+ mapillaryImage = null;
+ mapillaryClicks = [];
+ },
+
+
+ images: function(projection$$1) {
+ var psize = 16, limit = 3;
+ return searchLimited(psize, limit, projection$$1, mapillaryCache.images.rtree);
+ },
+
+
+ signs: function(projection$$1) {
+ var psize = 32, limit = 3;
+ return searchLimited(psize, limit, projection$$1, mapillaryCache.signs.rtree);
+ },
+
+
+ signsSupported: function() {
+ var detected = utilDetect();
+ if (detected.ie) return false;
+ if ((detected.browser.toLowerCase() === 'safari') && (parseFloat(detected.version) < 10)) return false;
+ return true;
+ },
+
+
+ signHTML: function(d) {
+ if (!mapillarySignDefs || !mapillarySignSprite) return;
+ var position = mapillarySignDefs[d.value];
+ if (!position) return '<div></div>';
+ var iconStyle = [
+ 'background-image:url(' + mapillarySignSprite + ')',
+ 'background-repeat:no-repeat',
+ 'height:' + position.height + 'px',
+ 'width:' + position.width + 'px',
+ 'background-position-x:-' + position.x + 'px',
+ 'background-position-y:-' + position.y + 'px',
+ ];
+
+ return '<div style="' + iconStyle.join(';') +'"></div>';
+ },
+
+
+ loadImages: function(projection$$1) {
+ var url = apibase + 'images?';
+ loadTiles('images', url, projection$$1);
+ },
+
+
+ loadSigns: function(context, projection$$1) {
+ var url = apibase + 'objects?';
+ loadTiles('signs', url, projection$$1);
+
+ // load traffic sign defs
+ if (!mapillarySignDefs) {
+ mapillarySignSprite = context.asset('img/traffic-signs/traffic-signs.png');
+ mapillarySignDefs = {};
+ json(context.asset('img/traffic-signs/traffic-signs.json'), function(err, data) {
+ if (err) return;
+ mapillarySignDefs = data;
+ });
+ }
+ },
+
+
+ loadViewer: function(context) {
+ var that = this;
+ var wrap = select('#content').selectAll('.mapillary-wrap')
+ .data([0]);
+
+ var enter = wrap.enter()
+ .append('div')
+ .attr('class', 'mapillary-wrap')
+ .classed('al', true) // 'al'=left, 'ar'=right
+ .classed('hidden', true);
+
+ enter
+ .append('button')
+ .attr('class', 'thumb-hide')
+ .on('click', function () { that.hideViewer(); })
+ .append('div')
+ .call(svgIcon('#icon-close'));
+
+ enter
+ .append('div')
+ .attr('id', 'mly')
+ .attr('class', 'mly-wrapper')
+ .classed('active', false);
+
+ // load mapillary-viewercss
+ select('head').selectAll('#mapillary-viewercss')
+ .data([0])
+ .enter()
+ .append('link')
+ .attr('id', 'mapillary-viewercss')
+ .attr('rel', 'stylesheet')
+ .attr('href', context.asset(viewercss));
+
+ // load mapillary-viewerjs
+ select('head').selectAll('#mapillary-viewerjs')
+ .data([0])
+ .enter()
+ .append('script')
+ .attr('id', 'mapillary-viewerjs')
+ .attr('src', context.asset(viewerjs));
+ },
+
+
+ showViewer: function() {
+ select('#content')
+ .selectAll('.mapillary-wrap')
+ .classed('hidden', false)
+ .selectAll('.mly-wrapper')
+ .classed('active', true);
+
+ return this;
+ },
+
+
+ hideViewer: function() {
+ select('#content')
+ .selectAll('.mapillary-wrap')
+ .classed('hidden', true)
+ .selectAll('.mly-wrapper')
+ .classed('active', false);
+
+ selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
+ .classed('selected', false);
+
+ mapillaryImage = null;
+ return this;
+ },
+
+
+ parsePagination: parsePagination,
+
+
+ updateViewer: function(imageKey, context) {
+ if (!imageKey) return;
+
+ if (!mapillaryViewer) {
+ this.initViewer(imageKey, context);
+ } else {
+ mapillaryViewer.moveToKey(imageKey);
+ }
+
+ return this;
+ },
+
+
+ initViewer: function(imageKey, context) {
+ var that = this;
+ if (Mapillary && imageKey) {
+ var opts = {
+ baseImageSize: 320,
+ component: {
+ cover: false,
+ keyboard: false
+ }
+ };
+
+ mapillaryViewer = new Mapillary.Viewer('mly', clientId, imageKey, opts);
+ mapillaryViewer.on('nodechanged', nodeChanged);
+ }
+
+ function nodeChanged(node) {
+ var clicks = mapillaryClicks;
+ var index = clicks.indexOf(node.key);
+ if (index > -1) { // nodechange initiated from clicking on a marker..
+ clicks.splice(index, 1);
+ } else { // nodechange initiated from the Mapillary viewer controls..
+ var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
+ context.map().centerEase(loc);
+ that.selectedImage(node.key, false);
+ }
+ }
+ },
+
+
+ selectedImage: function(imageKey, fromClick) {
+ if (!arguments.length) return mapillaryImage;
+
+ mapillaryImage = imageKey;
+ if (fromClick) {
+ mapillaryClicks.push(imageKey);
+ }
+
+ selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
+ .classed('selected', function(d) { return d.key === imageKey; });
+
+ return this;
+ },
+
+
+ cache: function(_) {
+ if (!arguments.length) return mapillaryCache;
+ mapillaryCache = _;
+ return this;
+ },
+
+
+ signDefs: function(_) {
+ if (!arguments.length) return mapillarySignDefs;
+ mapillarySignDefs = _;
+ return this;
+ }
+
+};
+
+var apibase$1 = 'https://nominatim.openstreetmap.org/';
+var inflight = {};
+var nominatimCache;
+
+
+var serviceNominatim = {
+
+ init: function() {
+ inflight = {};
+ nominatimCache = index$9();
+ },
+
+ reset: function() {
+ lodash.forEach(inflight, function(req) { req.abort(); });
+ inflight = {};
+ nominatimCache = index$9();
+ },
+
+
+ countryCode: function (location, callback) {
+ this.reverse(location, function(err, result) {
+ if (err) {
+ return callback(err);
+ } else if (result.address) {
+ return callback(null, result.address.country_code);
+ } else {
+ return callback('Unable to geocode', null);
+ }
+ });
+ },
+
+
+ reverse: function (location, callback) {
+ var cached = nominatimCache.search(
+ { minX: location[0], minY: location[1], maxX: location[0], maxY: location[1] }
+ );
+
+ if (cached.length > 0) {
+ return callback(null, cached[0].data);
+ }
+
+ var params = { zoom: 13, format: 'json', addressdetails: 1, lat: location[1], lon: location[0] };
+ var url = apibase$1 + 'reverse?' + utilQsString(params);
+ if (inflight[url]) return;
+
+ inflight[url] = json(url, function(err, result) {
+ delete inflight[url];
+
+ if (err) {
+ return callback(err);
+ } else if (result && result.error) {
+ return callback(result.error);
+ }
+
+ var extent$$1 = geoExtent$$1(location).padByMeters(200);
+ nominatimCache.insert(lodash.assign(extent$$1.bbox(), {data: result}));
+
+ callback(null, result);
+ });
+ },
+
+
+ search: function (val, callback) {
+ var searchVal = encodeURIComponent(val);
+ var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
+ if (inflight[url]) return;
+
+ inflight[url] = json(url, function(err, result) {
+ delete inflight[url];
+ callback(err, result);
+ });
+ }
+
+};
+
+var hashes = createCommonjsModule(function (module, exports) {
+/**
+ * jshashes - https://github.com/h2non/jshashes
+ * Released under the "New BSD" license
+ *
+ * Algorithms specification:
+ *
+ * MD5 - http://www.ietf.org/rfc/rfc1321.txt
+ * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
+ * SHA1 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
+ * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
+ * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
+ * HMAC - http://www.ietf.org/rfc/rfc2104.txt
+ */
+(function() {
+ var Hashes;
+
+ function utf8Encode(str) {
+ var x, y, output = '',
+ i = -1,
+ l;
+
+ if (str && str.length) {
+ l = str.length;
+ while ((i += 1) < l) {
+ /* Decode utf-16 surrogate pairs */
+ x = str.charCodeAt(i);
+ y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
+ if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
+ x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
+ i += 1;
+ }
+ /* Encode output as utf-8 */
+ if (x <= 0x7F) {
+ output += String.fromCharCode(x);
+ } else if (x <= 0x7FF) {
+ output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
+ 0x80 | (x & 0x3F));
+ } else if (x <= 0xFFFF) {
+ output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
+ 0x80 | ((x >>> 6) & 0x3F),
+ 0x80 | (x & 0x3F));
+ } else if (x <= 0x1FFFFF) {
+ output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
+ 0x80 | ((x >>> 12) & 0x3F),
+ 0x80 | ((x >>> 6) & 0x3F),
+ 0x80 | (x & 0x3F));
+ }
+ }
+ }
+ return output;
+ }
+
+ function utf8Decode(str) {
+ var i, ac, c1, c2, c3, arr = [],
+ l;
+ i = ac = c1 = c2 = c3 = 0;
+
+ if (str && str.length) {
+ l = str.length;
+ str += '';
+
+ while (i < l) {
+ c1 = str.charCodeAt(i);
+ ac += 1;
+ if (c1 < 128) {
+ arr[ac] = String.fromCharCode(c1);
+ i += 1;
+ } else if (c1 > 191 && c1 < 224) {
+ c2 = str.charCodeAt(i + 1);
+ arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
+ i += 2;
+ } else {
+ c2 = str.charCodeAt(i + 1);
+ c3 = str.charCodeAt(i + 2);
+ arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ i += 3;
+ }
+ }
+ }
+ return arr.join('');
+ }
+
+ /**
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+
+ function safe_add(x, y) {
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF),
+ msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+ }
+
+ /**
+ * Bitwise rotate a 32-bit number to the left.
+ */
+
+ function bit_rol(num, cnt) {
+ return (num << cnt) | (num >>> (32 - cnt));
+ }
+
+ /**
+ * Convert a raw string to a hex string
+ */
+
+ function rstr2hex(input, hexcase) {
+ var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
+ output = '',
+ x, i = 0,
+ l = input.length;
+ for (; i < l; i += 1) {
+ x = input.charCodeAt(i);
+ output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
+ }
+ return output;
+ }
+
+ /**
+ * Encode a string as utf-16
+ */
+
+ function binb2rstr(input) {
+ var i, l = input.length * 32,
+ output = '';
+ for (i = 0; i < l; i += 8) {
+ output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF);
+ }
+ return output;
+ }
+
+ /**
+ * Convert an array of little-endian words to a string
+ */
+
+ function binl2rstr(input) {
+ var i, l = input.length * 32,
+ output = '';
+ for (i = 0; i < l; i += 8) {
+ output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
+ }
+ return output;
+ }
+
+ /**
+ * Convert a raw string to an array of little-endian words
+ * Characters >255 have their high-byte silently ignored.
+ */
+
+ function rstr2binl(input) {
+ var i, l = input.length * 8,
+ output = Array(input.length >> 2),
+ lo = output.length;
+ for (i = 0; i < lo; i += 1) {
+ output[i] = 0;
+ }
+ for (i = 0; i < l; i += 8) {
+ output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
+ }
+ return output;
+ }
+
+ /**
+ * Convert a raw string to an array of big-endian words
+ * Characters >255 have their high-byte silently ignored.
+ */
+
+ function rstr2binb(input) {
+ var i, l = input.length * 8,
+ output = Array(input.length >> 2),
+ lo = output.length;
+ for (i = 0; i < lo; i += 1) {
+ output[i] = 0;
+ }
+ for (i = 0; i < l; i += 8) {
+ output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
+ }
+ return output;
+ }
+
+ /**
+ * Convert a raw string to an arbitrary string encoding
+ */
+
+ function rstr2any(input, encoding) {
+ var divisor = encoding.length,
+ remainders = Array(),
+ i, q, x, ld, quotient, dividend, output, full_length;
+
+ /* Convert to an array of 16-bit big-endian values, forming the dividend */
+ dividend = Array(Math.ceil(input.length / 2));
+ ld = dividend.length;
+ for (i = 0; i < ld; i += 1) {
+ dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
+ }
+
+ /**
+ * Repeatedly perform a long division. The binary array forms the dividend,
+ * the length of the encoding is the divisor. Once computed, the quotient
+ * forms the dividend for the next step. We stop when the dividend is zerHashes.
+ * All remainders are stored for later use.
+ */
+ while (dividend.length > 0) {
+ quotient = Array();
+ x = 0;
+ for (i = 0; i < dividend.length; i += 1) {
+ x = (x << 16) + dividend[i];
+ q = Math.floor(x / divisor);
+ x -= q * divisor;
+ if (quotient.length > 0 || q > 0) {
+ quotient[quotient.length] = q;
+ }
+ }
+ remainders[remainders.length] = x;
+ dividend = quotient;
+ }
+
+ /* Convert the remainders to the output string */
+ output = '';
+ for (i = remainders.length - 1; i >= 0; i--) {
+ output += encoding.charAt(remainders[i]);
+ }
+
+ /* Append leading zero equivalents */
+ full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
+ for (i = output.length; i < full_length; i += 1) {
+ output = encoding[0] + output;
+ }
+ return output;
+ }
+
+ /**
+ * Convert a raw string to a base-64 string
+ */
+
+ function rstr2b64(input, b64pad) {
+ var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
+ output = '',
+ len = input.length,
+ i, j, triplet;
+ b64pad = b64pad || '=';
+ for (i = 0; i < len; i += 3) {
+ triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
+ for (j = 0; j < 4; j += 1) {
+ if (i * 8 + j * 6 > input.length * 8) {
+ output += b64pad;
+ } else {
+ output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
+ }
+ }
+ }
+ return output;
+ }
+
+ Hashes = {
+ /**
+ * @property {String} version
+ * @readonly
+ */
+ VERSION: '1.0.6',
+ /**
+ * @member Hashes
+ * @class Base64
+ * @constructor
+ */
+ Base64: function() {
+ // private properties
+ var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
+ pad = '=', // default pad according with the RFC standard
+ url = false, // URL encoding support @todo
+ utf8 = true; // by default enable UTF-8 support encoding
+
+ // public method for encoding
+ this.encode = function(input) {
+ var i, j, triplet,
+ output = '',
+ len = input.length;
+
+ pad = pad || '=';
+ input = (utf8) ? utf8Encode(input) : input;
+
+ for (i = 0; i < len; i += 3) {
+ triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
+ for (j = 0; j < 4; j += 1) {
+ if (i * 8 + j * 6 > len * 8) {
+ output += pad;
+ } else {
+ output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
+ }
+ }
+ }
+ return output;
+ };
+
+ // public method for decoding
+ this.decode = function(input) {
+ // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+ var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
+ dec = '',
+ arr = [];
+ if (!input) {
+ return input;
+ }
+
+ i = ac = 0;
+ input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
+ //input += '';
+
+ do { // unpack four hexets into three octets using index points in b64
+ h1 = tab.indexOf(input.charAt(i += 1));
+ h2 = tab.indexOf(input.charAt(i += 1));
+ h3 = tab.indexOf(input.charAt(i += 1));
+ h4 = tab.indexOf(input.charAt(i += 1));
+
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
+
+ o1 = bits >> 16 & 0xff;
+ o2 = bits >> 8 & 0xff;
+ o3 = bits & 0xff;
+ ac += 1;
+
+ if (h3 === 64) {
+ arr[ac] = String.fromCharCode(o1);
+ } else if (h4 === 64) {
+ arr[ac] = String.fromCharCode(o1, o2);
+ } else {
+ arr[ac] = String.fromCharCode(o1, o2, o3);
+ }
+ } while (i < input.length);
+
+ dec = arr.join('');
+ dec = (utf8) ? utf8Decode(dec) : dec;
+
+ return dec;
+ };
+
+ // set custom pad string
+ this.setPad = function(str) {
+ pad = str || pad;
+ return this;
+ };
+ // set custom tab string characters
+ this.setTab = function(str) {
+ tab = str || tab;
+ return this;
+ };
+ this.setUTF8 = function(bool) {
+ if (typeof bool === 'boolean') {
+ utf8 = bool;
+ }
+ return this;
+ };
+ },
+
+ /**
+ * CRC-32 calculation
+ * @member Hashes
+ * @method CRC32
+ * @static
+ * @param {String} str Input String
+ * @return {String}
+ */
+ CRC32: function(str) {
+ var crc = 0,
+ x = 0,
+ y = 0,
+ table, i, iTop;
+ str = utf8Encode(str);
+
+ table = [
+ '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
+ '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
+ '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
+ '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
+ 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
+ '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
+ 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
+ '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
+ 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
+ '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
+ 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
+ '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
+ 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
+ '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
+ '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
+ '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
+ '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
+ 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
+ '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
+ 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
+ '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
+ 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
+ '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
+ 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
+ '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
+ 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
+ ].join('');
+
+ crc = crc ^ (-1);
+ for (i = 0, iTop = str.length; i < iTop; i += 1) {
+ y = (crc ^ str.charCodeAt(i)) & 0xFF;
+ x = '0x' + table.substr(y * 9, 8);
+ crc = (crc >>> 8) ^ x;
+ }
+ // always return a positive number (that's what >>> 0 does)
+ return (crc ^ (-1)) >>> 0;
+ },
+ /**
+ * @member Hashes
+ * @class MD5
+ * @constructor
+ * @param {Object} [config]
+ *
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
+ */
+ MD5: function(options) {
+ /**
+ * Private config properties. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
+ */
+ var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
+ b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
+ utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
+
+ // privileged (public) methods
+ this.hex = function(s) {
+ return rstr2hex(rstr(s, utf8), hexcase);
+ };
+ this.b64 = function(s) {
+ return rstr2b64(rstr(s), b64pad);
+ };
+ this.any = function(s, e) {
+ return rstr2any(rstr(s, utf8), e);
+ };
+ this.raw = function(s) {
+ return rstr(s, utf8);
+ };
+ this.hex_hmac = function(k, d) {
+ return rstr2hex(rstr_hmac(k, d), hexcase);
+ };
+ this.b64_hmac = function(k, d) {
+ return rstr2b64(rstr_hmac(k, d), b64pad);
+ };
+ this.any_hmac = function(k, d, e) {
+ return rstr2any(rstr_hmac(k, d), e);
+ };
+ /**
+ * Perform a simple self-test to see if the VM is working
+ * @return {String} Hexadecimal hash sample
+ */
+ this.vm_test = function() {
+ return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
+ };
+ /**
+ * Enable/disable uppercase hexadecimal returned string
+ * @param {Boolean}
+ * @return {Object} this
+ */
+ this.setUpperCase = function(a) {
+ if (typeof a === 'boolean') {
+ hexcase = a;
+ }
+ return this;
+ };
+ /**
+ * Defines a base64 pad string
+ * @param {String} Pad
+ * @return {Object} this
+ */
+ this.setPad = function(a) {
+ b64pad = a || b64pad;
+ return this;
+ };
+ /**
+ * Defines a base64 pad string
+ * @param {Boolean}
+ * @return {Object} [this]
+ */
+ this.setUTF8 = function(a) {
+ if (typeof a === 'boolean') {
+ utf8 = a;
+ }
+ return this;
+ };
+
+ // private methods
+
+ /**
+ * Calculate the MD5 of a raw string
+ */
+
+ function rstr(s) {
+ s = (utf8) ? utf8Encode(s) : s;
+ return binl2rstr(binl(rstr2binl(s), s.length * 8));
+ }
+
+ /**
+ * Calculate the HMAC-MD5, of a key and some data (raw strings)
+ */
+
+ function rstr_hmac(key, data) {
+ var bkey, ipad, opad, hash, i;
+
+ key = (utf8) ? utf8Encode(key) : key;
+ data = (utf8) ? utf8Encode(data) : data;
+ bkey = rstr2binl(key);
+ if (bkey.length > 16) {
+ bkey = binl(bkey, key.length * 8);
+ }
+
+ ipad = Array(16), opad = Array(16);
+ for (i = 0; i < 16; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+ hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
+ return binl2rstr(binl(opad.concat(hash), 512 + 128));
+ }
+
+ /**
+ * Calculate the MD5 of an array of little-endian words, and a bit length.
+ */
+
+ function binl(x, len) {
+ var i, olda, oldb, oldc, oldd,
+ a = 1732584193,
+ b = -271733879,
+ c = -1732584194,
+ d = 271733878;
+
+ /* append padding */
+ x[len >> 5] |= 0x80 << ((len) % 32);
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+ for (i = 0; i < x.length; i += 16) {
+ olda = a;
+ oldb = b;
+ oldc = c;
+ oldd = d;
+
+ a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
+ d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
+ c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
+ b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
+ a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
+ d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
+ c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
+ b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
+ a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
+ d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
+ c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
+ b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
+ a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
+ d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
+ c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
+ b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+ a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
+ d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
+ c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
+ b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
+ a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
+ d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
+ c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
+ b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
+ a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
+ d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
+ c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
+ b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
+ a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
+ d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
+ c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
+ b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+ a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
+ d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
+ c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
+ b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
+ a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
+ d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
+ c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
+ b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
+ a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
+ d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
+ c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
+ b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
+ a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
+ d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
+ c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
+ b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+ a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
+ d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
+ c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
+ b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
+ a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
+ d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
+ c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
+ b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
+ a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
+ d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
+ c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
+ b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
+ a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
+ d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
+ c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
+ b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+ a = safe_add(a, olda);
+ b = safe_add(b, oldb);
+ c = safe_add(c, oldc);
+ d = safe_add(d, oldd);
+ }
+ return Array(a, b, c, d);
+ }
+
+ /**
+ * These functions implement the four basic operations the algorithm uses.
+ */
+
+ function md5_cmn(q, a, b, x, s, t) {
+ return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
+ }
+
+ function md5_ff(a, b, c, d, x, s, t) {
+ return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+ }
+
+ function md5_gg(a, b, c, d, x, s, t) {
+ return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+ }
+
+ function md5_hh(a, b, c, d, x, s, t) {
+ return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+ }
+
+ function md5_ii(a, b, c, d, x, s, t) {
+ return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+ }
+ },
+ /**
+ * @member Hashes
+ * @class Hashes.SHA1
+ * @param {Object} [config]
+ * @constructor
+ *
+ * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
+ * Version 2.2 Copyright Paul Johnston 2000 - 2009.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ */
+ SHA1: function(options) {
+ /**
+ * Private config properties. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
+ */
+ var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
+ b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
+ utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
+
+ // public methods
+ this.hex = function(s) {
+ return rstr2hex(rstr(s, utf8), hexcase);
+ };
+ this.b64 = function(s) {
+ return rstr2b64(rstr(s, utf8), b64pad);
+ };
+ this.any = function(s, e) {
+ return rstr2any(rstr(s, utf8), e);
+ };
+ this.raw = function(s) {
+ return rstr(s, utf8);
+ };
+ this.hex_hmac = function(k, d) {
+ return rstr2hex(rstr_hmac(k, d));
+ };
+ this.b64_hmac = function(k, d) {
+ return rstr2b64(rstr_hmac(k, d), b64pad);
+ };
+ this.any_hmac = function(k, d, e) {
+ return rstr2any(rstr_hmac(k, d), e);
+ };
+ /**
+ * Perform a simple self-test to see if the VM is working
+ * @return {String} Hexadecimal hash sample
+ * @public
+ */
+ this.vm_test = function() {
+ return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
+ };
+ /**
+ * @description Enable/disable uppercase hexadecimal returned string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUpperCase = function(a) {
+ if (typeof a === 'boolean') {
+ hexcase = a;
+ }
+ return this;
+ };
+ /**
+ * @description Defines a base64 pad string
+ * @param {string} Pad
+ * @return {Object} this
+ * @public
+ */
+ this.setPad = function(a) {
+ b64pad = a || b64pad;
+ return this;
+ };
+ /**
+ * @description Defines a base64 pad string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUTF8 = function(a) {
+ if (typeof a === 'boolean') {
+ utf8 = a;
+ }
+ return this;
+ };
+
+ // private methods
+
+ /**
+ * Calculate the SHA-512 of a raw string
+ */
+
+ function rstr(s) {
+ s = (utf8) ? utf8Encode(s) : s;
+ return binb2rstr(binb(rstr2binb(s), s.length * 8));
+ }
+
+ /**
+ * Calculate the HMAC-SHA1 of a key and some data (raw strings)
+ */
+
+ function rstr_hmac(key, data) {
+ var bkey, ipad, opad, i, hash;
+ key = (utf8) ? utf8Encode(key) : key;
+ data = (utf8) ? utf8Encode(data) : data;
+ bkey = rstr2binb(key);
+
+ if (bkey.length > 16) {
+ bkey = binb(bkey, key.length * 8);
+ }
+ ipad = Array(16), opad = Array(16);
+ for (i = 0; i < 16; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+ hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
+ return binb2rstr(binb(opad.concat(hash), 512 + 160));
+ }
+
+ /**
+ * Calculate the SHA-1 of an array of big-endian words, and a bit length
+ */
+
+ function binb(x, len) {
+ var i, j, t, olda, oldb, oldc, oldd, olde,
+ w = Array(80),
+ a = 1732584193,
+ b = -271733879,
+ c = -1732584194,
+ d = 271733878,
+ e = -1009589776;
+
+ /* append padding */
+ x[len >> 5] |= 0x80 << (24 - len % 32);
+ x[((len + 64 >> 9) << 4) + 15] = len;
+
+ for (i = 0; i < x.length; i += 16) {
+ olda = a,
+ oldb = b;
+ oldc = c;
+ oldd = d;
+ olde = e;
+
+ for (j = 0; j < 80; j += 1) {
+ if (j < 16) {
+ w[j] = x[i + j];
+ } else {
+ w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
+ }
+ t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
+ safe_add(safe_add(e, w[j]), sha1_kt(j)));
+ e = d;
+ d = c;
+ c = bit_rol(b, 30);
+ b = a;
+ a = t;
+ }
+
+ a = safe_add(a, olda);
+ b = safe_add(b, oldb);
+ c = safe_add(c, oldc);
+ d = safe_add(d, oldd);
+ e = safe_add(e, olde);
+ }
+ return Array(a, b, c, d, e);
+ }
+
+ /**
+ * Perform the appropriate triplet combination function for the current
+ * iteration
+ */
+
+ function sha1_ft(t, b, c, d) {
+ if (t < 20) {
+ return (b & c) | ((~b) & d);
+ }
+ if (t < 40) {
+ return b ^ c ^ d;
+ }
+ if (t < 60) {
+ return (b & c) | (b & d) | (c & d);
+ }
+ return b ^ c ^ d;
+ }
+
+ /**
+ * Determine the appropriate additive constant for the current iteration
+ */
+
+ function sha1_kt(t) {
+ return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
+ (t < 60) ? -1894007588 : -899497514;
+ }
+ },
+ /**
+ * @class Hashes.SHA256
+ * @param {config}
+ *
+ * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
+ * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ * Also http://anmar.eu.org/projects/jssha2/
+ */
+ SHA256: function(options) {
+ /**
+ * Private properties configuration variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ * @see this.setUpperCase() method
+ * @see this.setPad() method
+ */
+ var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase */
+ b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
+ /* base-64 pad character. Default '=' for strict RFC compliance */
+ utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
+ /* enable/disable utf8 encoding */
+ sha256_K;
+
+ /* privileged (public) methods */
+ this.hex = function(s) {
+ return rstr2hex(rstr(s, utf8));
+ };
+ this.b64 = function(s) {
+ return rstr2b64(rstr(s, utf8), b64pad);
+ };
+ this.any = function(s, e) {
+ return rstr2any(rstr(s, utf8), e);
+ };
+ this.raw = function(s) {
+ return rstr(s, utf8);
+ };
+ this.hex_hmac = function(k, d) {
+ return rstr2hex(rstr_hmac(k, d));
+ };
+ this.b64_hmac = function(k, d) {
+ return rstr2b64(rstr_hmac(k, d), b64pad);
+ };
+ this.any_hmac = function(k, d, e) {
+ return rstr2any(rstr_hmac(k, d), e);
+ };
+ /**
+ * Perform a simple self-test to see if the VM is working
+ * @return {String} Hexadecimal hash sample
+ * @public
+ */
+ this.vm_test = function() {
+ return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
+ };
+ /**
+ * Enable/disable uppercase hexadecimal returned string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUpperCase = function(a) {
+ if (typeof a === 'boolean') {
+ hexcase = a;
+ }
+ return this;
+ };
+ /**
+ * @description Defines a base64 pad string
+ * @param {string} Pad
+ * @return {Object} this
+ * @public
+ */
+ this.setPad = function(a) {
+ b64pad = a || b64pad;
+ return this;
+ };
+ /**
+ * Defines a base64 pad string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUTF8 = function(a) {
+ if (typeof a === 'boolean') {
+ utf8 = a;
+ }
+ return this;
+ };
+
+ // private methods
+
+ /**
+ * Calculate the SHA-512 of a raw string
+ */
+
+ function rstr(s, utf8) {
+ s = (utf8) ? utf8Encode(s) : s;
+ return binb2rstr(binb(rstr2binb(s), s.length * 8));
+ }
+
+ /**
+ * Calculate the HMAC-sha256 of a key and some data (raw strings)
+ */
+
+ function rstr_hmac(key, data) {
+ key = (utf8) ? utf8Encode(key) : key;
+ data = (utf8) ? utf8Encode(data) : data;
+ var hash, i = 0,
+ bkey = rstr2binb(key),
+ ipad = Array(16),
+ opad = Array(16);
+
+ if (bkey.length > 16) {
+ bkey = binb(bkey, key.length * 8);
+ }
+
+ for (; i < 16; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+
+ hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
+ return binb2rstr(binb(opad.concat(hash), 512 + 256));
+ }
+
+ /*
+ * Main sha256 function, with its support functions
+ */
+
+ function sha256_S(X, n) {
+ return (X >>> n) | (X << (32 - n));
+ }
+
+ function sha256_R(X, n) {
+ return (X >>> n);
+ }
+
+ function sha256_Ch(x, y, z) {
+ return ((x & y) ^ ((~x) & z));
+ }
+
+ function sha256_Maj(x, y, z) {
+ return ((x & y) ^ (x & z) ^ (y & z));
+ }
+
+ function sha256_Sigma0256(x) {
+ return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));
+ }
+
+ function sha256_Sigma1256(x) {
+ return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));
+ }
+
+ function sha256_Gamma0256(x) {
+ return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));
+ }
+
+ function sha256_Gamma1256(x) {
+ return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));
+ }
+
+ sha256_K = [
+ 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
+ 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
+ 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
+ 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
+ 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
+ 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
+ 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998
+ ];
+
+ function binb(m, l) {
+ var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
+ 1359893119, -1694144372, 528734635, 1541459225
+ ];
+ var W = new Array(64);
+ var a, b, c, d, e, f, g, h;
+ var i, j, T1, T2;
+
+ /* append padding */
+ m[l >> 5] |= 0x80 << (24 - l % 32);
+ m[((l + 64 >> 9) << 4) + 15] = l;
+
+ for (i = 0; i < m.length; i += 16) {
+ a = HASH[0];
+ b = HASH[1];
+ c = HASH[2];
+ d = HASH[3];
+ e = HASH[4];
+ f = HASH[5];
+ g = HASH[6];
+ h = HASH[7];
+
+ for (j = 0; j < 64; j += 1) {
+ if (j < 16) {
+ W[j] = m[j + i];
+ } else {
+ W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
+ sha256_Gamma0256(W[j - 15])), W[j - 16]);
+ }
+
+ T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
+ sha256_K[j]), W[j]);
+ T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
+ h = g;
+ g = f;
+ f = e;
+ e = safe_add(d, T1);
+ d = c;
+ c = b;
+ b = a;
+ a = safe_add(T1, T2);
+ }
+
+ HASH[0] = safe_add(a, HASH[0]);
+ HASH[1] = safe_add(b, HASH[1]);
+ HASH[2] = safe_add(c, HASH[2]);
+ HASH[3] = safe_add(d, HASH[3]);
+ HASH[4] = safe_add(e, HASH[4]);
+ HASH[5] = safe_add(f, HASH[5]);
+ HASH[6] = safe_add(g, HASH[6]);
+ HASH[7] = safe_add(h, HASH[7]);
+ }
+ return HASH;
+ }
+
+ },
+
+ /**
+ * @class Hashes.SHA512
+ * @param {config}
+ *
+ * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
+ * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ */
+ SHA512: function(options) {
+ /**
+ * Private properties configuration variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ * @see this.setUpperCase() method
+ * @see this.setPad() method
+ */
+ var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
+ /* hexadecimal output case format. false - lowercase; true - uppercase */
+ b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
+ /* base-64 pad character. Default '=' for strict RFC compliance */
+ utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
+ /* enable/disable utf8 encoding */
+ sha512_k;
+
+ /* privileged (public) methods */
+ this.hex = function(s) {
+ return rstr2hex(rstr(s));
+ };
+ this.b64 = function(s) {
+ return rstr2b64(rstr(s), b64pad);
+ };
+ this.any = function(s, e) {
+ return rstr2any(rstr(s), e);
+ };
+ this.raw = function(s) {
+ return rstr(s, utf8);
+ };
+ this.hex_hmac = function(k, d) {
+ return rstr2hex(rstr_hmac(k, d));
+ };
+ this.b64_hmac = function(k, d) {
+ return rstr2b64(rstr_hmac(k, d), b64pad);
+ };
+ this.any_hmac = function(k, d, e) {
+ return rstr2any(rstr_hmac(k, d), e);
+ };
+ /**
+ * Perform a simple self-test to see if the VM is working
+ * @return {String} Hexadecimal hash sample
+ * @public
+ */
+ this.vm_test = function() {
+ return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
+ };
+ /**
+ * @description Enable/disable uppercase hexadecimal returned string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUpperCase = function(a) {
+ if (typeof a === 'boolean') {
+ hexcase = a;
+ }
+ return this;
+ };
+ /**
+ * @description Defines a base64 pad string
+ * @param {string} Pad
+ * @return {Object} this
+ * @public
+ */
+ this.setPad = function(a) {
+ b64pad = a || b64pad;
+ return this;
+ };
+ /**
+ * @description Defines a base64 pad string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUTF8 = function(a) {
+ if (typeof a === 'boolean') {
+ utf8 = a;
+ }
+ return this;
+ };
+
+ /* private methods */
+
+ /**
+ * Calculate the SHA-512 of a raw string
+ */
+
+ function rstr(s) {
+ s = (utf8) ? utf8Encode(s) : s;
+ return binb2rstr(binb(rstr2binb(s), s.length * 8));
+ }
+ /*
+ * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
+ */
+
+ function rstr_hmac(key, data) {
+ key = (utf8) ? utf8Encode(key) : key;
+ data = (utf8) ? utf8Encode(data) : data;
+
+ var hash, i = 0,
+ bkey = rstr2binb(key),
+ ipad = Array(32),
+ opad = Array(32);
+
+ if (bkey.length > 32) {
+ bkey = binb(bkey, key.length * 8);
+ }
+
+ for (; i < 32; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+
+ hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
+ return binb2rstr(binb(opad.concat(hash), 1024 + 512));
+ }
+
+ /**
+ * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
+ */
+
+ function binb(x, len) {
+ var j, i, l,
+ W = new Array(80),
+ hash = new Array(16),
+ //Initial hash values
+ H = [
+ new int64(0x6a09e667, -205731576),
+ new int64(-1150833019, -2067093701),
+ new int64(0x3c6ef372, -23791573),
+ new int64(-1521486534, 0x5f1d36f1),
+ new int64(0x510e527f, -1377402159),
+ new int64(-1694144372, 0x2b3e6c1f),
+ new int64(0x1f83d9ab, -79577749),
+ new int64(0x5be0cd19, 0x137e2179)
+ ],
+ T1 = new int64(0, 0),
+ T2 = new int64(0, 0),
+ a = new int64(0, 0),
+ b = new int64(0, 0),
+ c = new int64(0, 0),
+ d = new int64(0, 0),
+ e = new int64(0, 0),
+ f = new int64(0, 0),
+ g = new int64(0, 0),
+ h = new int64(0, 0),
+ //Temporary variables not specified by the document
+ s0 = new int64(0, 0),
+ s1 = new int64(0, 0),
+ Ch = new int64(0, 0),
+ Maj = new int64(0, 0),
+ r1 = new int64(0, 0),
+ r2 = new int64(0, 0),
+ r3 = new int64(0, 0);
+
+ if (sha512_k === undefined) {
+ //SHA512 constants
+ sha512_k = [
+ new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
+ new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
+ new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
+ new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
+ new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
+ new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
+ new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
+ new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
+ new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
+ new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
+ new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
+ new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
+ new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
+ new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
+ new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
+ new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
+ new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
+ new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
+ new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
+ new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
+ new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
+ new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
+ new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
+ new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
+ new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
+ new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
+ new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
+ new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
+ new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
+ new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
+ new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
+ new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
+ new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
+ new int64(-354779690, -840897762), new int64(-176337025, -294727304),
+ new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
+ new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
+ new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
+ new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
+ new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
+ new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
+ ];
+ }
+
+ for (i = 0; i < 80; i += 1) {
+ W[i] = new int64(0, 0);
+ }
+
+ // append padding to the source string. The format is described in the FIPS.
+ x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
+ x[((len + 128 >> 10) << 5) + 31] = len;
+ l = x.length;
+ for (i = 0; i < l; i += 32) { //32 dwords is the block size
+ int64copy(a, H[0]);
+ int64copy(b, H[1]);
+ int64copy(c, H[2]);
+ int64copy(d, H[3]);
+ int64copy(e, H[4]);
+ int64copy(f, H[5]);
+ int64copy(g, H[6]);
+ int64copy(h, H[7]);
+
+ for (j = 0; j < 16; j += 1) {
+ W[j].h = x[i + 2 * j];
+ W[j].l = x[i + 2 * j + 1];
+ }
+
+ for (j = 16; j < 80; j += 1) {
+ //sigma1
+ int64rrot(r1, W[j - 2], 19);
+ int64revrrot(r2, W[j - 2], 29);
+ int64shr(r3, W[j - 2], 6);
+ s1.l = r1.l ^ r2.l ^ r3.l;
+ s1.h = r1.h ^ r2.h ^ r3.h;
+ //sigma0
+ int64rrot(r1, W[j - 15], 1);
+ int64rrot(r2, W[j - 15], 8);
+ int64shr(r3, W[j - 15], 7);
+ s0.l = r1.l ^ r2.l ^ r3.l;
+ s0.h = r1.h ^ r2.h ^ r3.h;
+
+ int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
+ }
+
+ for (j = 0; j < 80; j += 1) {
+ //Ch
+ Ch.l = (e.l & f.l) ^ (~e.l & g.l);
+ Ch.h = (e.h & f.h) ^ (~e.h & g.h);
+
+ //Sigma1
+ int64rrot(r1, e, 14);
+ int64rrot(r2, e, 18);
+ int64revrrot(r3, e, 9);
+ s1.l = r1.l ^ r2.l ^ r3.l;
+ s1.h = r1.h ^ r2.h ^ r3.h;
+
+ //Sigma0
+ int64rrot(r1, a, 28);
+ int64revrrot(r2, a, 2);
+ int64revrrot(r3, a, 7);
+ s0.l = r1.l ^ r2.l ^ r3.l;
+ s0.h = r1.h ^ r2.h ^ r3.h;
+
+ //Maj
+ Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
+ Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
+
+ int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
+ int64add(T2, s0, Maj);
+
+ int64copy(h, g);
+ int64copy(g, f);
+ int64copy(f, e);
+ int64add(e, d, T1);
+ int64copy(d, c);
+ int64copy(c, b);
+ int64copy(b, a);
+ int64add(a, T1, T2);
+ }
+ int64add(H[0], H[0], a);
+ int64add(H[1], H[1], b);
+ int64add(H[2], H[2], c);
+ int64add(H[3], H[3], d);
+ int64add(H[4], H[4], e);
+ int64add(H[5], H[5], f);
+ int64add(H[6], H[6], g);
+ int64add(H[7], H[7], h);
+ }
+
+ //represent the hash as an array of 32-bit dwords
+ for (i = 0; i < 8; i += 1) {
+ hash[2 * i] = H[i].h;
+ hash[2 * i + 1] = H[i].l;
+ }
+ return hash;
+ }
+
+ //A constructor for 64-bit numbers
+
+ function int64(h, l) {
+ this.h = h;
+ this.l = l;
+ //this.toString = int64toString;
+ }
+
+ //Copies src into dst, assuming both are 64-bit numbers
+
+ function int64copy(dst, src) {
+ dst.h = src.h;
+ dst.l = src.l;
+ }
+
+ //Right-rotates a 64-bit number by shift
+ //Won't handle cases of shift>=32
+ //The function revrrot() is for that
+
+ function int64rrot(dst, x, shift) {
+ dst.l = (x.l >>> shift) | (x.h << (32 - shift));
+ dst.h = (x.h >>> shift) | (x.l << (32 - shift));
+ }
+
+ //Reverses the dwords of the source and then rotates right by shift.
+ //This is equivalent to rotation by 32+shift
+
+ function int64revrrot(dst, x, shift) {
+ dst.l = (x.h >>> shift) | (x.l << (32 - shift));
+ dst.h = (x.l >>> shift) | (x.h << (32 - shift));
+ }
+
+ //Bitwise-shifts right a 64-bit number by shift
+ //Won't handle shift>=32, but it's never needed in SHA512
+
+ function int64shr(dst, x, shift) {
+ dst.l = (x.l >>> shift) | (x.h << (32 - shift));
+ dst.h = (x.h >>> shift);
+ }
+
+ //Adds two 64-bit numbers
+ //Like the original implementation, does not rely on 32-bit operations
+
+ function int64add(dst, x, y) {
+ var w0 = (x.l & 0xffff) + (y.l & 0xffff);
+ var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
+ var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
+ var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
+ dst.l = (w0 & 0xffff) | (w1 << 16);
+ dst.h = (w2 & 0xffff) | (w3 << 16);
+ }
+
+ //Same, except with 4 addends. Works faster than adding them one by one.
+
+ function int64add4(dst, a, b, c, d) {
+ var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
+ var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
+ var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
+ var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
+ dst.l = (w0 & 0xffff) | (w1 << 16);
+ dst.h = (w2 & 0xffff) | (w3 << 16);
+ }
+
+ //Same, except with 5 addends
+
+ function int64add5(dst, a, b, c, d, e) {
+ var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
+ w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
+ w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
+ w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
+ dst.l = (w0 & 0xffff) | (w1 << 16);
+ dst.h = (w2 & 0xffff) | (w3 << 16);
+ }
+ },
+ /**
+ * @class Hashes.RMD160
+ * @constructor
+ * @param {Object} [config]
+ *
+ * A JavaScript implementation of the RIPEMD-160 Algorithm
+ * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
+ */
+ RMD160: function(options) {
+ /**
+ * Private properties configuration variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ * @see this.setUpperCase() method
+ * @see this.setPad() method
+ */
+ var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
+ /* hexadecimal output case format. false - lowercase; true - uppercase */
+ b64pad = (options && typeof options.pad === 'string') ? options.pa : '=',
+ /* base-64 pad character. Default '=' for strict RFC compliance */
+ utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
+ /* enable/disable utf8 encoding */
+ rmd160_r1 = [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
+ 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
+ 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
+ 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
+ ],
+ rmd160_r2 = [
+ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
+ 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
+ 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
+ 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
+ 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
+ ],
+ rmd160_s1 = [
+ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
+ 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
+ 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
+ 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
+ 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
+ ],
+ rmd160_s2 = [
+ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
+ 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
+ 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
+ 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
+ 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
+ ];
+
+ /* privileged (public) methods */
+ this.hex = function(s) {
+ return rstr2hex(rstr(s, utf8));
+ };
+ this.b64 = function(s) {
+ return rstr2b64(rstr(s, utf8), b64pad);
+ };
+ this.any = function(s, e) {
+ return rstr2any(rstr(s, utf8), e);
+ };
+ this.raw = function(s) {
+ return rstr(s, utf8);
+ };
+ this.hex_hmac = function(k, d) {
+ return rstr2hex(rstr_hmac(k, d));
+ };
+ this.b64_hmac = function(k, d) {
+ return rstr2b64(rstr_hmac(k, d), b64pad);
+ };
+ this.any_hmac = function(k, d, e) {
+ return rstr2any(rstr_hmac(k, d), e);
+ };
+ /**
+ * Perform a simple self-test to see if the VM is working
+ * @return {String} Hexadecimal hash sample
+ * @public
+ */
+ this.vm_test = function() {
+ return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
+ };
+ /**
+ * @description Enable/disable uppercase hexadecimal returned string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUpperCase = function(a) {
+ if (typeof a === 'boolean') {
+ hexcase = a;
+ }
+ return this;
+ };
+ /**
+ * @description Defines a base64 pad string
+ * @param {string} Pad
+ * @return {Object} this
+ * @public
+ */
+ this.setPad = function(a) {
+ if (typeof a !== 'undefined') {
+ b64pad = a;
+ }
+ return this;
+ };
+ /**
+ * @description Defines a base64 pad string
+ * @param {boolean}
+ * @return {Object} this
+ * @public
+ */
+ this.setUTF8 = function(a) {
+ if (typeof a === 'boolean') {
+ utf8 = a;
+ }
+ return this;
+ };
+
+ /* private methods */
+
+ /**
+ * Calculate the rmd160 of a raw string
+ */
+
+ function rstr(s) {
+ s = (utf8) ? utf8Encode(s) : s;
+ return binl2rstr(binl(rstr2binl(s), s.length * 8));
+ }
+
+ /**
+ * Calculate the HMAC-rmd160 of a key and some data (raw strings)
+ */
+
+ function rstr_hmac(key, data) {
+ key = (utf8) ? utf8Encode(key) : key;
+ data = (utf8) ? utf8Encode(data) : data;
+ var i, hash,
+ bkey = rstr2binl(key),
+ ipad = Array(16),
+ opad = Array(16);
+
+ if (bkey.length > 16) {
+ bkey = binl(bkey, key.length * 8);
+ }
+
+ for (i = 0; i < 16; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+ hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
+ return binl2rstr(binl(opad.concat(hash), 512 + 160));
+ }
+
+ /**
+ * Convert an array of little-endian words to a string
+ */
+
+ function binl2rstr(input) {
+ var i, output = '',
+ l = input.length * 32;
+ for (i = 0; i < l; i += 8) {
+ output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
+ }
+ return output;
+ }
+
+ /**
+ * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
+ */
+
+ function binl(x, len) {
+ var T, j, i, l,
+ h0 = 0x67452301,
+ h1 = 0xefcdab89,
+ h2 = 0x98badcfe,
+ h3 = 0x10325476,
+ h4 = 0xc3d2e1f0,
+ A1, B1, C1, D1, E1,
+ A2, B2, C2, D2, E2;
+
+ /* append padding */
+ x[len >> 5] |= 0x80 << (len % 32);
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
+ l = x.length;
+
+ for (i = 0; i < l; i += 16) {
+ A1 = A2 = h0;
+ B1 = B2 = h1;
+ C1 = C2 = h2;
+ D1 = D2 = h3;
+ E1 = E2 = h4;
+ for (j = 0; j <= 79; j += 1) {
+ T = safe_add(A1, rmd160_f(j, B1, C1, D1));
+ T = safe_add(T, x[i + rmd160_r1[j]]);
+ T = safe_add(T, rmd160_K1(j));
+ T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
+ A1 = E1;
+ E1 = D1;
+ D1 = bit_rol(C1, 10);
+ C1 = B1;
+ B1 = T;
+ T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
+ T = safe_add(T, x[i + rmd160_r2[j]]);
+ T = safe_add(T, rmd160_K2(j));
+ T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
+ A2 = E2;
+ E2 = D2;
+ D2 = bit_rol(C2, 10);
+ C2 = B2;
+ B2 = T;
+ }
+
+ T = safe_add(h1, safe_add(C1, D2));
+ h1 = safe_add(h2, safe_add(D1, E2));
+ h2 = safe_add(h3, safe_add(E1, A2));
+ h3 = safe_add(h4, safe_add(A1, B2));
+ h4 = safe_add(h0, safe_add(B1, C2));
+ h0 = T;
+ }
+ return [h0, h1, h2, h3, h4];
+ }
+
+ // specific algorithm methods
+
+ function rmd160_f(j, x, y, z) {
+ return (0 <= j && j <= 15) ? (x ^ y ^ z) :
+ (16 <= j && j <= 31) ? (x & y) | (~x & z) :
+ (32 <= j && j <= 47) ? (x | ~y) ^ z :
+ (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
+ (64 <= j && j <= 79) ? x ^ (y | ~z) :
+ 'rmd160_f: j out of range';
+ }
+
+ function rmd160_K1(j) {
+ return (0 <= j && j <= 15) ? 0x00000000 :
+ (16 <= j && j <= 31) ? 0x5a827999 :
+ (32 <= j && j <= 47) ? 0x6ed9eba1 :
+ (48 <= j && j <= 63) ? 0x8f1bbcdc :
+ (64 <= j && j <= 79) ? 0xa953fd4e :
+ 'rmd160_K1: j out of range';
+ }
+
+ function rmd160_K2(j) {
+ return (0 <= j && j <= 15) ? 0x50a28be6 :
+ (16 <= j && j <= 31) ? 0x5c4dd124 :
+ (32 <= j && j <= 47) ? 0x6d703ef3 :
+ (48 <= j && j <= 63) ? 0x7a6d76e9 :
+ (64 <= j && j <= 79) ? 0x00000000 :
+ 'rmd160_K2: j out of range';
+ }
+ }
+ };
+
+ // exposes Hashes
+ (function(window, undefined) {
+ var freeExports = false;
+ {
+ freeExports = exports;
+ if (exports && typeof commonjsGlobal === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
+ window = commonjsGlobal;
+ }
+ }
+
+ if (typeof undefined === 'function' && typeof undefined.amd === 'object' && undefined.amd) {
+ // define as an anonymous module, so, through path mapping, it can be aliased
+ undefined(function() {
+ return Hashes;
+ });
+ } else if (freeExports) {
+ // in Node.js or RingoJS v0.8.0+
+ if ('object' === 'object' && module && module.exports === freeExports) {
+ module.exports = Hashes;
+ }
+ // in Narwhal or RingoJS v0.7.0-
+ else {
+ freeExports.Hashes = Hashes;
+ }
+ } else {
+ // in a browser or Rhino
+ window.Hashes = Hashes;
+ }
+ }(this));
+}()); // IIFE
+});
+
+var immutable = extend$2;
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+function extend$2() {
+ var target = {};
+
+ for (var i = 0; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target
+}
+
+var sha1 = new hashes.SHA1();
+
+var ohauth = {};
+
+ohauth.qsString = function(obj) {
+ return Object.keys(obj).sort().map(function(key) {
+ return ohauth.percentEncode(key) + '=' +
+ ohauth.percentEncode(obj[key]);
+ }).join('&');
+};
+
+ohauth.stringQs = function(str) {
+ return str.split('&').filter(function (pair) {
+ return pair !== '';
+ }).reduce(function(obj, pair){
+ var parts = pair.split('=');
+ obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
+ '' : decodeURIComponent(parts[1]);
+ return obj;
+ }, {});
+};
+
+ohauth.rawxhr = function(method, url, data, headers, callback) {
+ var xhr = new XMLHttpRequest(),
+ twoHundred = /^20\d$/;
+ xhr.onreadystatechange = function() {
+ if (4 === xhr.readyState && 0 !== xhr.status) {
+ if (twoHundred.test(xhr.status)) callback(null, xhr);
+ else return callback(xhr, null);
+ }
+ };
+ xhr.onerror = function(e) { return callback(e, null); };
+ xhr.open(method, url, true);
+ for (var h in headers) xhr.setRequestHeader(h, headers[h]);
+ xhr.send(data);
+ return xhr;
+};
+
+ohauth.xhr = function(method, url, auth, data, options, callback) {
+ var headers = (options && options.header) || {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ };
+ headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
+ return ohauth.rawxhr(method, url, data, headers, callback);
+};
+
+ohauth.nonce = function() {
+ for (var o = ''; o.length < 6;) {
+ o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
+ }
+ return o;
+};
+
+ohauth.authHeader = function(obj) {
+ return Object.keys(obj).sort().map(function(key) {
+ return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
+ }).join(', ');
+};
+
+ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
+
+ohauth.percentEncode = function(s) {
+ return encodeURIComponent(s)
+ .replace(/\!/g, '%21').replace(/\'/g, '%27')
+ .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
+};
+
+ohauth.baseString = function(method, url, params) {
+ if (params.oauth_signature) delete params.oauth_signature;
+ return [
+ method,
+ ohauth.percentEncode(url),
+ ohauth.percentEncode(ohauth.qsString(params))].join('&');
+};
+
+ohauth.signature = function(oauth_secret, token_secret, baseString) {
+ return sha1.b64_hmac(
+ ohauth.percentEncode(oauth_secret) + '&' +
+ ohauth.percentEncode(token_secret),
+ baseString);
+};
+
+/**
+ * Takes an options object for configuration (consumer_key,
+ * consumer_secret, version, signature_method, token, token_secret)
+ * and returns a function that generates the Authorization header
+ * for given data.
+ *
+ * The returned function takes these parameters:
+ * - method: GET/POST/...
+ * - uri: full URI with protocol, port, path and query string
+ * - extra_params: any extra parameters (that are passed in the POST data),
+ * can be an object or a from-urlencoded string.
+ *
+ * Returned function returns full OAuth header with "OAuth" string in it.
+ */
+
+ohauth.headerGenerator = function(options) {
+ options = options || {};
+ var consumer_key = options.consumer_key || '',
+ consumer_secret = options.consumer_secret || '',
+ signature_method = options.signature_method || 'HMAC-SHA1',
+ version = options.version || '1.0',
+ token = options.token || '',
+ token_secret = options.token_secret || '';
+
+ return function(method, uri, extra_params) {
+ method = method.toUpperCase();
+ if (typeof extra_params === 'string' && extra_params.length > 0) {
+ extra_params = ohauth.stringQs(extra_params);
+ }
+
+ var uri_parts = uri.split('?', 2),
+ base_uri = uri_parts[0];
+
+ var query_params = uri_parts.length === 2 ?
+ ohauth.stringQs(uri_parts[1]) : {};
+
+ var oauth_params = {
+ oauth_consumer_key: consumer_key,
+ oauth_signature_method: signature_method,
+ oauth_version: version,
+ oauth_timestamp: ohauth.timestamp(),
+ oauth_nonce: ohauth.nonce()
+ };
+
+ if (token) oauth_params.oauth_token = token;
+
+ var all_params = immutable({}, oauth_params, query_params, extra_params),
+ base_str = ohauth.baseString(method, base_uri, all_params);
+
+ oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
+
+ return 'OAuth ' + ohauth.authHeader(oauth_params);
+ };
+};
+
+var index$13 = ohauth;
+
+var resolveUrl = createCommonjsModule(function (module, exports) {
+// Copyright 2014 Simon Lydell
+// X11 (“MIT”) Licensed. (See LICENSE.)
+
+void (function(root, factory) {
+ if (typeof undefined === "function" && undefined.amd) {
+ undefined(factory);
+ } else {
+ module.exports = factory();
+ }
+}(commonjsGlobal, function() {
+
+ function resolveUrl(/* ...urls */) {
+ var numUrls = arguments.length;
+
+ if (numUrls === 0) {
+ throw new Error("resolveUrl requires at least one argument; got none.")
+ }
+
+ var base = document.createElement("base");
+ base.href = arguments[0];
+
+ if (numUrls === 1) {
+ return base.href
+ }
+
+ var head = document.getElementsByTagName("head")[0];
+ head.insertBefore(base, head.firstChild);
+
+ var a = document.createElement("a");
+ var resolved;
+
+ for (var index = 1; index < numUrls; index++) {
+ a.href = arguments[index];
+ resolved = a.href;
+ base.href = resolved;
+ }
+
+ head.removeChild(base);
+
+ return resolved
+ }
+
+ return resolveUrl
+
+}));
+});
+
+var assign = make_assign();
+var create$2 = make_create();
+var trim = make_trim();
+var Global = (typeof window !== 'undefined' ? window : commonjsGlobal);
+
+var util = {
+ assign: assign,
+ create: create$2,
+ trim: trim,
+ bind: bind$1,
+ slice: slice$7,
+ each: each$1,
+ map: map$4,
+ pluck: pluck$1,
+ isList: isList$1,
+ isFunction: isFunction$1,
+ isObject: isObject$1,
+ Global: Global
+};
+
+function make_assign() {
+ if (Object.assign) {
+ return Object.assign
+ } else {
+ return function shimAssign(obj, props1, props2, etc) {
+ for (var i = 1; i < arguments.length; i++) {
+ each$1(Object(arguments[i]), function(val, key) {
+ obj[key] = val;
+ });
+ }
+ return obj
+ }
+ }
+}
+
+function make_create() {
+ if (Object.create) {
+ return function create(obj, assignProps1, assignProps2, etc) {
+ var assignArgsList = slice$7(arguments, 1);
+ return assign.apply(this, [Object.create(obj)].concat(assignArgsList))
+ }
+ } else {
+ function F() {} // eslint-disable-line no-inner-declarations
+ return function create(obj, assignProps1, assignProps2, etc) {
+ var assignArgsList = slice$7(arguments, 1);
+ F.prototype = obj;
+ return assign.apply(this, [new F()].concat(assignArgsList))
+ }
+ }
+}
+
+function make_trim() {
+ if (String.prototype.trim) {
+ return function trim(str) {
+ return String.prototype.trim.call(str)
+ }
+ } else {
+ return function trim(str) {
+ return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
+ }
+ }
+}
+
+function bind$1(obj, fn) {
+ return function() {
+ return fn.apply(obj, Array.prototype.slice.call(arguments, 0))
+ }
+}
+
+function slice$7(arr, index) {
+ return Array.prototype.slice.call(arr, index || 0)
+}
+
+function each$1(obj, fn) {
+ pluck$1(obj, function(val, key) {
+ fn(val, key);
+ return false
+ });
+}
+
+function map$4(obj, fn) {
+ var res = (isList$1(obj) ? [] : {});
+ pluck$1(obj, function(v, k) {
+ res[k] = fn(v, k);
+ return false
+ });
+ return res
+}
+
+function pluck$1(obj, fn) {
+ if (isList$1(obj)) {
+ for (var i=0; i<obj.length; i++) {
+ if (fn(obj[i], i)) {
+ return obj[i]
+ }
+ }
+ } else {
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ if (fn(obj[key], key)) {
+ return obj[key]
+ }
+ }
+ }
+ }
+}
+
+function isList$1(val) {
+ return (val != null && typeof val != 'function' && typeof val.length == 'number')
+}
+
+function isFunction$1(val) {
+ return val && {}.toString.call(val) === '[object Function]'
+}
+
+function isObject$1(val) {
+ return val && {}.toString.call(val) === '[object Object]'
+}
+
+var slice$6 = util.slice;
+var pluck = util.pluck;
+var each = util.each;
+var bind = util.bind;
+var create$1 = util.create;
+var isList = util.isList;
+var isFunction = util.isFunction;
+var isObject = util.isObject;
+
+var storeEngine = {
+ createStore: createStore
+};
+
+var storeAPI = {
+ version: '2.0.12',
+ enabled: false,
+
+ // get returns the value of the given key. If that value
+ // is undefined, it returns optionalDefaultValue instead.
+ get: function(key, optionalDefaultValue) {
+ var data = this.storage.read(this._namespacePrefix + key);
+ return this._deserialize(data, optionalDefaultValue)
+ },
+
+ // set will store the given value at key and returns value.
+ // Calling set with value === undefined is equivalent to calling remove.
+ set: function(key, value) {
+ if (value === undefined) {
+ return this.remove(key)
+ }
+ this.storage.write(this._namespacePrefix + key, this._serialize(value));
+ return value
+ },
+
+ // remove deletes the key and value stored at the given key.
+ remove: function(key) {
+ this.storage.remove(this._namespacePrefix + key);
+ },
+
+ // each will call the given callback once for each key-value pair
+ // in this store.
+ each: function(callback) {
+ var self = this;
+ this.storage.each(function(val, namespacedKey) {
+ callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
+ });
+ },
+
+ // clearAll will remove all the stored key-value pairs in this store.
+ clearAll: function() {
+ this.storage.clearAll();
+ },
+
+ // additional functionality that can't live in plugins
+ // ---------------------------------------------------
+
+ // hasNamespace returns true if this store instance has the given namespace.
+ hasNamespace: function(namespace) {
+ return (this._namespacePrefix == '__storejs_'+namespace+'_')
+ },
+
+ // createStore creates a store.js instance with the first
+ // functioning storage in the list of storage candidates,
+ // and applies the the given mixins to the instance.
+ createStore: function() {
+ return createStore.apply(this, arguments)
+ },
+
+ addPlugin: function(plugin) {
+ this._addPlugin(plugin);
+ },
+
+ namespace: function(namespace) {
+ return createStore(this.storage, this.plugins, namespace)
+ }
+};
+
+function _warn() {
+ var _console = (typeof console == 'undefined' ? null : console);
+ if (!_console) { return }
+ var fn = (_console.warn ? _console.warn : _console.log);
+ fn.apply(_console, arguments);
+}
+
+function createStore(storages, plugins, namespace) {
+ if (!namespace) {
+ namespace = '';
+ }
+ if (storages && !isList(storages)) {
+ storages = [storages];
+ }
+ if (plugins && !isList(plugins)) {
+ plugins = [plugins];
+ }
+
+ var namespacePrefix = (namespace ? '__storejs_'+namespace+'_' : '');
+ var namespaceRegexp = (namespace ? new RegExp('^'+namespacePrefix) : null);
+ var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
+ if (!legalNamespaces.test(namespace)) {
+ throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes')
+ }
+
+ var _privateStoreProps = {
+ _namespacePrefix: namespacePrefix,
+ _namespaceRegexp: namespaceRegexp,
+
+ _testStorage: function(storage) {
+ try {
+ var testStr = '__storejs__test__';
+ storage.write(testStr, testStr);
+ var ok = (storage.read(testStr) === testStr);
+ storage.remove(testStr);
+ return ok
+ } catch(e) {
+ return false
+ }
+ },
+
+ _assignPluginFnProp: function(pluginFnProp, propName) {
+ var oldFn = this[propName];
+ this[propName] = function pluginFn() {
+ var args = slice$6(arguments, 0);
+ var self = this;
+
+ // super_fn calls the old function which was overwritten by
+ // this mixin.
+ function super_fn() {
+ if (!oldFn) { return }
+ each(arguments, function(arg, i) {
+ args[i] = arg;
+ });
+ return oldFn.apply(self, args)
+ }
+
+ // Give mixing function access to super_fn by prefixing all mixin function
+ // arguments with super_fn.
+ var newFnArgs = [super_fn].concat(args);
+
+ return pluginFnProp.apply(self, newFnArgs)
+ };
+ },
+
+ _serialize: function(obj) {
+ return JSON.stringify(obj)
+ },
+
+ _deserialize: function(strVal, defaultVal) {
+ if (!strVal) { return defaultVal }
+ // It is possible that a raw string value has been previously stored
+ // in a storage without using store.js, meaning it will be a raw
+ // string value instead of a JSON serialized string. By defaulting
+ // to the raw string value in case of a JSON parse error, we allow
+ // for past stored values to be forwards-compatible with store.js
+ var val = '';
+ try { val = JSON.parse(strVal); }
+ catch(e) { val = strVal; }
+
+ return (val !== undefined ? val : defaultVal)
+ },
+
+ _addStorage: function(storage) {
+ if (this.enabled) { return }
+ if (this._testStorage(storage)) {
+ this.storage = storage;
+ this.enabled = true;
+ }
+ },
+
+ _addPlugin: function(plugin) {
+ var self = this;
+
+ // If the plugin is an array, then add all plugins in the array.
+ // This allows for a plugin to depend on other plugins.
+ if (isList(plugin)) {
+ each(plugin, function(plugin) {
+ self._addPlugin(plugin);
+ });
+ return
+ }
+
+ // Keep track of all plugins we've seen so far, so that we
+ // don't add any of them twice.
+ var seenPlugin = pluck(this.plugins, function(seenPlugin) {
+ return (plugin === seenPlugin)
+ });
+ if (seenPlugin) {
+ return
+ }
+ this.plugins.push(plugin);
+
+ // Check that the plugin is properly formed
+ if (!isFunction(plugin)) {
+ throw new Error('Plugins must be function values that return objects')
+ }
+
+ var pluginProperties = plugin.call(this);
+ if (!isObject(pluginProperties)) {
+ throw new Error('Plugins must return an object of function properties')
+ }
+
+ // Add the plugin function properties to this store instance.
+ each(pluginProperties, function(pluginFnProp, propName) {
+ if (!isFunction(pluginFnProp)) {
+ throw new Error('Bad plugin property: '+propName+' from plugin '+plugin.name+'. Plugins should only return functions.')
+ }
+ self._assignPluginFnProp(pluginFnProp, propName);
+ });
+ },
+
+ // Put deprecated properties in the private API, so as to not expose it to accidential
+ // discovery through inspection of the store object.
+
+ // Deprecated: addStorage
+ addStorage: function(storage) {
+ _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
+ this._addStorage(storage);
+ }
+ };
+
+ var store = create$1(_privateStoreProps, storeAPI, {
+ plugins: []
+ });
+ store.raw = {};
+ each(store, function(prop, propName) {
+ if (isFunction(prop)) {
+ store.raw[propName] = bind(store, prop);
+ }
+ });
+ each(storages, function(storage) {
+ store._addStorage(storage);
+ });
+ each(plugins, function(plugin) {
+ store._addPlugin(plugin);
+ });
+ return store
+}
+
+var Global$1 = util.Global;
+
+var localStorage_1 = {
+ name: 'localStorage',
+ read: read,
+ write: write,
+ each: each$2,
+ remove: remove$2,
+ clearAll: clearAll,
+};
+
+function localStorage$1() {
+ return Global$1.localStorage
+}
+
+function read(key) {
+ return localStorage$1().getItem(key)
+}
+
+function write(key, data) {
+ return localStorage$1().setItem(key, data)
+}
+
+function each$2(fn) {
+ for (var i = localStorage$1().length - 1; i >= 0; i--) {
+ var key = localStorage$1().key(i);
+ fn(read(key), key);
+ }
+}
+
+function remove$2(key) {
+ return localStorage$1().removeItem(key)
+}
+
+function clearAll() {
+ return localStorage$1().clear()
+}
+
+// oldFF-globalStorage provides storage for Firefox
+// versions 6 and 7, where no localStorage, etc
+// is available.
+
+
+var Global$2 = util.Global;
+
+var oldFFGlobalStorage = {
+ name: 'oldFF-globalStorage',
+ read: read$1,
+ write: write$1,
+ each: each$3,
+ remove: remove$3,
+ clearAll: clearAll$1,
+};
+
+var globalStorage = Global$2.globalStorage;
+
+function read$1(key) {
+ return globalStorage[key]
+}
+
+function write$1(key, data) {
+ globalStorage[key] = data;
+}
+
+function each$3(fn) {
+ for (var i = globalStorage.length - 1; i >= 0; i--) {
+ var key = globalStorage.key(i);
+ fn(globalStorage[key], key);
+ }
+}
+
+function remove$3(key) {
+ return globalStorage.removeItem(key)
+}
+
+function clearAll$1() {
+ each$3(function(key, _) {
+ delete globalStorage[key];
+ });
+}
+
+// oldIE-userDataStorage provides storage for Internet Explorer
+// versions 6 and 7, where no localStorage, sessionStorage, etc
+// is available.
+
+
+var Global$3 = util.Global;
+
+var oldIEUserDataStorage = {
+ name: 'oldIE-userDataStorage',
+ write: write$2,
+ read: read$2,
+ each: each$4,
+ remove: remove$4,
+ clearAll: clearAll$2,
+};
+
+var storageName = 'storejs';
+var doc = Global$3.document;
+var _withStorageEl = _makeIEStorageElFunction();
+var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
+
+function write$2(unfixedKey, data) {
+ if (disable) { return }
+ var fixedKey = fixKey(unfixedKey);
+ _withStorageEl(function(storageEl) {
+ storageEl.setAttribute(fixedKey, data);
+ storageEl.save(storageName);
+ });
+}
+
+function read$2(unfixedKey) {
+ if (disable) { return }
+ var fixedKey = fixKey(unfixedKey);
+ var res = null;
+ _withStorageEl(function(storageEl) {
+ res = storageEl.getAttribute(fixedKey);
+ });
+ return res
+}
+
+function each$4(callback) {
+ _withStorageEl(function(storageEl) {
+ var attributes = storageEl.XMLDocument.documentElement.attributes;
+ for (var i=attributes.length-1; i>=0; i--) {
+ var attr = attributes[i];
+ callback(storageEl.getAttribute(attr.name), attr.name);
+ }
+ });
+}
+
+function remove$4(unfixedKey) {
+ var fixedKey = fixKey(unfixedKey);
+ _withStorageEl(function(storageEl) {
+ storageEl.removeAttribute(fixedKey);
+ storageEl.save(storageName);
+ });
+}
+
+function clearAll$2() {
+ _withStorageEl(function(storageEl) {
+ var attributes = storageEl.XMLDocument.documentElement.attributes;
+ storageEl.load(storageName);
+ for (var i=attributes.length-1; i>=0; i--) {
+ storageEl.removeAttribute(attributes[i].name);
+ }
+ storageEl.save(storageName);
+ });
+}
+
+// Helpers
+//////////
+
+// In IE7, keys cannot start with a digit or contain certain chars.
+// See https://github.com/marcuswestin/store.js/issues/40
+// See https://github.com/marcuswestin/store.js/issues/83
+var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
+function fixKey(key) {
+ return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___')
+}
+
+function _makeIEStorageElFunction() {
+ if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
+ return null
+ }
+ var scriptTag = 'script',
+ storageOwner,
+ storageContainer,
+ storageEl;
+
+ // Since #userData storage applies only to specific paths, we need to
+ // somehow link our data to a specific path. We choose /favicon.ico
+ // as a pretty safe option, since all browsers already make a request to
+ // this URL anyway and being a 404 will not hurt us here. We wrap an
+ // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
+ // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
+ // since the iframe access rules appear to allow direct access and
+ // manipulation of the document element, even for a 404 page. This
+ // document can be used instead of the current document (which would
+ // have been limited to the current path) to perform #userData storage.
+ try {
+ /* global ActiveXObject */
+ storageContainer = new ActiveXObject('htmlfile');
+ storageContainer.open();
+ storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>');
+ storageContainer.close();
+ storageOwner = storageContainer.w.frames[0].document;
+ storageEl = storageOwner.createElement('div');
+ } catch(e) {
+ // somehow ActiveXObject instantiation failed (perhaps some special
+ // security settings or otherwse), fall back to per-path storage
+ storageEl = doc.createElement('div');
+ storageOwner = doc.body;
+ }
+
+ return function(storeFunction) {
+ var args = [].slice.call(arguments, 0);
+ args.unshift(storageEl);
+ // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
+ // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
+ storageOwner.appendChild(storageEl);
+ storageEl.addBehavior('#default#userData');
+ storageEl.load(storageName);
+ storeFunction.apply(this, args);
+ storageOwner.removeChild(storageEl);
+ return
+ }
+}
+
+// cookieStorage is useful Safari private browser mode, where localStorage
+// doesn't work but cookies do. This implementation is adopted from
+// https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
+
+
+var Global$4 = util.Global;
+var trim$1 = util.trim;
+
+var cookieStorage = {
+ name: 'cookieStorage',
+ read: read$3,
+ write: write$3,
+ each: each$5,
+ remove: remove$5,
+ clearAll: clearAll$3,
+};
+
+var doc$1 = Global$4.document;
+
+function read$3(key) {
+ if (!key || !_has(key)) { return null }
+ var regexpStr = "(?:^|.*;\\s*)" +
+ escape(key).replace(/[\-\.\+\*]/g, "\\$&") +
+ "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
+ return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"))
+}
+
+function each$5(callback) {
+ var cookies = doc$1.cookie.split(/; ?/g);
+ for (var i = cookies.length - 1; i >= 0; i--) {
+ if (!trim$1(cookies[i])) {
+ continue
+ }
+ var kvp = cookies[i].split('=');
+ var key = unescape(kvp[0]);
+ var val = unescape(kvp[1]);
+ callback(val, key);
+ }
+}
+
+function write$3(key, data) {
+ if(!key) { return }
+ doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
+}
+
+function remove$5(key) {
+ if (!key || !_has(key)) {
+ return
+ }
+ doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
+}
+
+function clearAll$3() {
+ each$5(function(_, key) {
+ remove$5(key);
+ });
+}
+
+function _has(key) {
+ return (new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(doc$1.cookie)
+}
+
+var Global$5 = util.Global;
+
+var sessionStorage_1 = {
+ name: 'sessionStorage',
+ read: read$4,
+ write: write$4,
+ each: each$6,
+ remove: remove$6,
+ clearAll: clearAll$4
+};
+
+function sessionStorage() {
+ return Global$5.sessionStorage
+}
+
+function read$4(key) {
+ return sessionStorage().getItem(key)
+}
+
+function write$4(key, data) {
+ return sessionStorage().setItem(key, data)
+}
+
+function each$6(fn) {
+ for (var i = sessionStorage().length - 1; i >= 0; i--) {
+ var key = sessionStorage().key(i);
+ fn(read$4(key), key);
+ }
+}
+
+function remove$6(key) {
+ return sessionStorage().removeItem(key)
+}
+
+function clearAll$4() {
+ return sessionStorage().clear()
+}
+
+// memoryStorage is a useful last fallback to ensure that the store
+// is functions (meaning store.get(), store.set(), etc will all function).
+// However, stored values will not persist when the browser navigates to
+// a new page or reloads the current page.
+
+var memoryStorage_1 = {
+ name: 'memoryStorage',
+ read: read$5,
+ write: write$5,
+ each: each$7,
+ remove: remove$7,
+ clearAll: clearAll$5,
+};
+
+var memoryStorage = {};
+
+function read$5(key) {
+ return memoryStorage[key]
+}
+
+function write$5(key, data) {
+ memoryStorage[key] = data;
+}
+
+function each$7(callback) {
+ for (var key in memoryStorage) {
+ if (memoryStorage.hasOwnProperty(key)) {
+ callback(memoryStorage[key], key);
+ }
+ }
+}
+
+function remove$7(key) {
+ delete memoryStorage[key];
+}
+
+function clearAll$5(key) {
+ memoryStorage = {};
+}
+
+var all$3 = [
+ // Listed in order of usage preference
+ localStorage_1,
+ oldFFGlobalStorage,
+ oldIEUserDataStorage,
+ cookieStorage,
+ sessionStorage_1,
+ memoryStorage_1
+];
+
+/* eslint-disable */
+
+// json2.js
+// 2016-10-28
+// Public Domain.
+// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+// See http://www.JSON.org/js.html
+// This code should be minified before deployment.
+// See http://javascript.crockford.com/jsmin.html
+
+// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+// NOT CONTROL.
+
+// This file creates a global JSON object containing two methods: stringify
+// and parse. This file provides the ES5 JSON capability to ES3 systems.
+// If a project might run on IE8 or earlier, then this file should be included.
+// This file does nothing on ES5 systems.
+
+// JSON.stringify(value, replacer, space)
+// value any JavaScript value, usually an object or array.
+// replacer an optional parameter that determines how object
+// values are stringified for objects. It can be a
+// function or an array of strings.
+// space an optional parameter that specifies the indentation
+// of nested structures. If it is omitted, the text will
+// be packed without extra whitespace. If it is a number,
+// it will specify the number of spaces to indent at each
+// level. If it is a string (such as "\t" or " "),
+// it contains the characters used to indent at each level.
+// This method produces a JSON text from a JavaScript value.
+// When an object value is found, if the object contains a toJSON
+// method, its toJSON method will be called and the result will be
+// stringified. A toJSON method does not serialize: it returns the
+// value represented by the name/value pair that should be serialized,
+// or undefined if nothing should be serialized. The toJSON method
+// will be passed the key associated with the value, and this will be
+// bound to the value.
+
+// For example, this would serialize Dates as ISO strings.
+
+// Date.prototype.toJSON = function (key) {
+// function f(n) {
+// // Format integers to have at least two digits.
+// return (n < 10)
+// ? "0" + n
+// : n;
+// }
+// return this.getUTCFullYear() + "-" +
+// f(this.getUTCMonth() + 1) + "-" +
+// f(this.getUTCDate()) + "T" +
+// f(this.getUTCHours()) + ":" +
+// f(this.getUTCMinutes()) + ":" +
+// f(this.getUTCSeconds()) + "Z";
+// };
+
+// You can provide an optional replacer method. It will be passed the
+// key and value of each member, with this bound to the containing
+// object. The value that is returned from your method will be
+// serialized. If your method returns undefined, then the member will
+// be excluded from the serialization.
+
+// If the replacer parameter is an array of strings, then it will be
+// used to select the members to be serialized. It filters the results
+// such that only members with keys listed in the replacer array are
+// stringified.
+
+// Values that do not have JSON representations, such as undefined or
+// functions, will not be serialized. Such values in objects will be
+// dropped; in arrays they will be replaced with null. You can use
+// a replacer function to replace those with JSON values.
+
+// JSON.stringify(undefined) returns undefined.
+
+// The optional space parameter produces a stringification of the
+// value that is filled with line breaks and indentation to make it
+// easier to read.
+
+// If the space parameter is a non-empty string, then that string will
+// be used for indentation. If the space parameter is a number, then
+// the indentation will be that many spaces.
+
+// Example:
+
+// text = JSON.stringify(["e", {pluribus: "unum"}]);
+// // text is '["e",{"pluribus":"unum"}]'
+
+// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
+// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+// text = JSON.stringify([new Date()], function (key, value) {
+// return this[key] instanceof Date
+// ? "Date(" + this[key] + ")"
+// : value;
+// });
+// // text is '["Date(---current time---)"]'
+
+// JSON.parse(text, reviver)
+// This method parses a JSON text to produce an object or array.
+// It can throw a SyntaxError exception.
+
+// The optional reviver parameter is a function that can filter and
+// transform the results. It receives each of the keys and values,
+// and its return value is used instead of the original value.
+// If it returns what it received, then the structure is not modified.
+// If it returns undefined then the member is deleted.
+
+// Example:
+
+// // Parse the text. Values that look like ISO date strings will
+// // be converted to Date objects.
+
+// myData = JSON.parse(text, function (key, value) {
+// var a;
+// if (typeof value === "string") {
+// a =
+// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+// if (a) {
+// return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+// +a[5], +a[6]));
+// }
+// }
+// return value;
+// });
+
+// myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+// var d;
+// if (typeof value === "string" &&
+// value.slice(0, 5) === "Date(" &&
+// value.slice(-1) === ")") {
+// d = new Date(value.slice(5, -1));
+// if (d) {
+// return d;
+// }
+// }
+// return value;
+// });
+
+// This is a reference implementation. You are free to copy, modify, or
+// redistribute.
+
+/*jslint
+ eval, for, this
+*/
+
+/*property
+ JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (typeof JSON !== "object") {
+ JSON = {};
+}
+
+(function () {
+ "use strict";
+
+ var rx_one = /^[\],:{}\s]*$/;
+ var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
+ var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
+ var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
+ var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10
+ ? "0" + n
+ : n;
+ }
+
+ function this_value() {
+ return this.valueOf();
+ }
+
+ if (typeof Date.prototype.toJSON !== "function") {
+
+ Date.prototype.toJSON = function () {
+
+ return isFinite(this.valueOf())
+ ? this.getUTCFullYear() + "-" +
+ f(this.getUTCMonth() + 1) + "-" +
+ f(this.getUTCDate()) + "T" +
+ f(this.getUTCHours()) + ":" +
+ f(this.getUTCMinutes()) + ":" +
+ f(this.getUTCSeconds()) + "Z"
+ : null;
+ };
+
+ Boolean.prototype.toJSON = this_value;
+ Number.prototype.toJSON = this_value;
+ String.prototype.toJSON = this_value;
+ }
+
+ var gap;
+ var indent;
+ var meta;
+ var rep;
+
+
+ function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+ rx_escapable.lastIndex = 0;
+ return rx_escapable.test(string)
+ ? "\"" + string.replace(rx_escapable, function (a) {
+ var c = meta[a];
+ return typeof c === "string"
+ ? c
+ : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + "\""
+ : "\"" + string + "\"";
+ }
+
+
+ function str(key, holder) {
+
+// Produce a string from holder[key].
+
+ var i; // The loop counter.
+ var k; // The member key.
+ var v; // The member value.
+ var length;
+ var mind = gap;
+ var partial;
+ var value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value && typeof value === "object" &&
+ typeof value.toJSON === "function") {
+ value = value.toJSON(key);
+ }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+ if (typeof rep === "function") {
+ value = rep.call(holder, key, value);
+ }
+
+// What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case "string":
+ return quote(value);
+
+ case "number":
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value)
+ ? String(value)
+ : "null";
+
+ case "boolean":
+ case "null":
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce "null". The case is included here in
+// the remote chance that this gets fixed someday.
+
+ return String(value);
+
+// If the type is "object", we might be dealing with an object or an array or
+// null.
+
+ case "object":
+
+// Due to a specification blunder in ECMAScript, typeof null is "object",
+// so watch out for that case.
+
+ if (!value) {
+ return "null";
+ }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+// Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === "[object Array]") {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || "null";
+ }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+ v = partial.length === 0
+ ? "[]"
+ : gap
+ ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
+ : "[" + partial.join(",") + "]";
+ gap = mind;
+ return v;
+ }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === "object") {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === "string") {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (
+ gap
+ ? ": "
+ : ":"
+ ) + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (
+ gap
+ ? ": "
+ : ":"
+ ) + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+ v = partial.length === 0
+ ? "{}"
+ : gap
+ ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
+ : "{" + partial.join(",") + "}";
+ gap = mind;
+ return v;
+ }
+ }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+ if (typeof JSON.stringify !== "function") {
+ meta = { // table of character substitutions
+ "\b": "\\b",
+ "\t": "\\t",
+ "\n": "\\n",
+ "\f": "\\f",
+ "\r": "\\r",
+ "\"": "\\\"",
+ "\\": "\\\\"
+ };
+ JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+ var i;
+ gap = "";
+ indent = "";
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+ if (typeof space === "number") {
+ for (i = 0; i < space; i += 1) {
+ indent += " ";
+ }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === "string") {
+ indent = space;
+ }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== "function" &&
+ (typeof replacer !== "object" ||
+ typeof replacer.length !== "number")) {
+ throw new Error("JSON.stringify");
+ }
+
+// Make a fake root object containing our value under the key of "".
+// Return the result of stringifying the value.
+
+ return str("", {"": value});
+ };
+ }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+ if (typeof JSON.parse !== "function") {
+ JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+ var k;
+ var v;
+ var value = holder[key];
+ if (value && typeof value === "object") {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+ text = String(text);
+ rx_dangerous.lastIndex = 0;
+ if (rx_dangerous.test(text)) {
+ text = text.replace(rx_dangerous, function (a) {
+ return "\\u" +
+ ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with "()" and "new"
+// because they can cause invocation, and "=" because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
+// replace all simple value tokens with "]" characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or "]" or
+// "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
+
+ if (
+ rx_one.test(
+ text
+ .replace(rx_two, "@")
+ .replace(rx_three, "]")
+ .replace(rx_four, "")
+ )
+ ) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The "{" operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+ j = eval("(" + text + ")");
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+ return (typeof reviver === "function")
+ ? walk({"": j}, "")
+ : j;
+ }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError("JSON.parse");
+ };
+ }
+}());
+
+var json2 = json2Plugin;
+
+function json2Plugin() {
+
+ return {}
+}
+
+var plugins = [json2];
+
+var store_legacy = storeEngine.createStore(all$3, plugins);
+
+// # osm-auth
+//
+// This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
+// object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
+// does not support custom headers, which this uses everywhere.
+var index$12 = function(o) {
+
+ var oauth = {};
+
+ // authenticated users will also have a request token secret, but it's
+ // not used in transactions with the server
+ oauth.authenticated = function() {
+ return !!(token('oauth_token') && token('oauth_token_secret'));
+ };
+
+ oauth.logout = function() {
+ token('oauth_token', '');
+ token('oauth_token_secret', '');
+ token('oauth_request_token_secret', '');
+ return oauth;
+ };
+
+ // TODO: detect lack of click event
+ oauth.authenticate = function(callback) {
+ if (oauth.authenticated()) return callback();
+
+ oauth.logout();
+
+ // ## Getting a request token
+ var params = timenonce(getAuth(o)),
+ url = o.url + '/oauth/request_token';
+
+ params.oauth_signature = index$13.signature(
+ o.oauth_secret, '',
+ index$13.baseString('POST', url, params));
+
+ if (!o.singlepage) {
+ // Create a 600x550 popup window in the center of the screen
+ var w = 600, h = 550,
+ settings = [
+ ['width', w], ['height', h],
+ ['left', screen.width / 2 - w / 2],
+ ['top', screen.height / 2 - h / 2]].map(function(x) {
+ return x.join('=');
+ }).join(','),
+ popup = window.open('about:blank', 'oauth_window', settings);
+ }
+
+ // Request a request token. When this is complete, the popup
+ // window is redirected to OSM's authorization page.
+ index$13.xhr('POST', url, params, null, {}, reqTokenDone);
+ o.loading();
+
+ function reqTokenDone(err, xhr) {
+ o.done();
+ if (err) return callback(err);
+ var resp = index$13.stringQs(xhr.response);
+ token('oauth_request_token_secret', resp.oauth_token_secret);
+ var authorize_url = o.url + '/oauth/authorize?' + index$13.qsString({
+ oauth_token: resp.oauth_token,
+ oauth_callback: resolveUrl(o.landing)
+ });
+
+ if (o.singlepage) {
+ location.href = authorize_url;
+ } else {
+ popup.location = authorize_url;
+ }
+ }
+
+ // Called by a function in a landing page, in the popup window. The
+ // window closes itself.
+ window.authComplete = function(token) {
+ var oauth_token = index$13.stringQs(token.split('?')[1]);
+ get_access_token(oauth_token.oauth_token);
+ delete window.authComplete;
+ };
+
+ // ## Getting an request token
+ //
+ // At this point we have an `oauth_token`, brought in from a function
+ // call on a landing page popup.
+ function get_access_token(oauth_token) {
+ var url = o.url + '/oauth/access_token',
+ params = timenonce(getAuth(o)),
+ request_token_secret = token('oauth_request_token_secret');
+ params.oauth_token = oauth_token;
+ params.oauth_signature = index$13.signature(
+ o.oauth_secret,
+ request_token_secret,
+ index$13.baseString('POST', url, params));
+
+ // ## Getting an access token
+ //
+ // The final token required for authentication. At this point
+ // we have a `request token secret`
+ index$13.xhr('POST', url, params, null, {}, accessTokenDone);
+ o.loading();
+ }
+
+ function accessTokenDone(err, xhr) {
+ o.done();
+ if (err) return callback(err);
+ var access_token = index$13.stringQs(xhr.response);
+ token('oauth_token', access_token.oauth_token);
+ token('oauth_token_secret', access_token.oauth_token_secret);
+ callback(null, oauth);
+ }
+ };
+
+ oauth.bootstrapToken = function(oauth_token, callback) {
+ // ## Getting an request token
+ // At this point we have an `oauth_token`, brought in from a function
+ // call on a landing page popup.
+ function get_access_token(oauth_token) {
+ var url = o.url + '/oauth/access_token',
+ params = timenonce(getAuth(o)),
+ request_token_secret = token('oauth_request_token_secret');
+ params.oauth_token = oauth_token;
+ params.oauth_signature = index$13.signature(
+ o.oauth_secret,
+ request_token_secret,
+ index$13.baseString('POST', url, params));
+
+ // ## Getting an access token
+ // The final token required for authentication. At this point
+ // we have a `request token secret`
+ index$13.xhr('POST', url, params, null, {}, accessTokenDone);
+ o.loading();
+ }
+
+ function accessTokenDone(err, xhr) {
+ o.done();
+ if (err) return callback(err);
+ var access_token = index$13.stringQs(xhr.response);
+ token('oauth_token', access_token.oauth_token);
+ token('oauth_token_secret', access_token.oauth_token_secret);
+ callback(null, oauth);
+ }
+
+ get_access_token(oauth_token);
+ };
+
+ // # xhr
+ //
+ // A single XMLHttpRequest wrapper that does authenticated calls if the
+ // user has logged in.
+ oauth.xhr = function(options, callback) {
+ if (!oauth.authenticated()) {
+ if (o.auto) {
+ return oauth.authenticate(run);
+ } else {
+ callback('not authenticated', null);
+ return;
+ }
+ } else {
+ return run();
+ }
+
+ function run() {
+ var params = timenonce(getAuth(o)),
+ oauth_token_secret = token('oauth_token_secret'),
+ url = (options.prefix !== false) ? o.url + options.path : options.path,
+ url_parts = url.replace(/#.*$/, '').split('?', 2),
+ base_url = url_parts[0],
+ query = (url_parts.length === 2) ? url_parts[1] : '';
+
+ // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
+ if ((!options.options || !options.options.header ||
+ options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
+ options.content) {
+ params = immutable(params, index$13.stringQs(options.content));
+ }
+
+ params.oauth_token = token('oauth_token');
+ params.oauth_signature = index$13.signature(
+ o.oauth_secret,
+ oauth_token_secret,
+ index$13.baseString(options.method, base_url, immutable(params, index$13.stringQs(query)))
+ );
+
+ return index$13.xhr(options.method, url, params, options.content, options.options, done);
+ }
+
+ function done(err, xhr) {
+ if (err) return callback(err);
+ else if (xhr.responseXML) return callback(err, xhr.responseXML);
+ else return callback(err, xhr.response);
+ }
+ };
+
+ // pre-authorize this object, if we can just get a token and token_secret
+ // from the start
+ oauth.preauth = function(c) {
+ if (!c) return;
+ if (c.oauth_token) token('oauth_token', c.oauth_token);
+ if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
+ return oauth;
+ };
+
+ oauth.options = function(_) {
+ if (!arguments.length) return o;
+
+ o = _;
+ o.url = o.url || 'https://www.openstreetmap.org';
+ o.landing = o.landing || 'land.html';
+ o.singlepage = o.singlepage || false;
+
+ // Optional loading and loading-done functions for nice UI feedback.
+ // by default, no-ops
+ o.loading = o.loading || function() {};
+ o.done = o.done || function() {};
+
+ return oauth.preauth(o);
+ };
+
+ // 'stamp' an authentication object from `getAuth()`
+ // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
+ // and timestamp
+ function timenonce(o) {
+ o.oauth_timestamp = index$13.timestamp();
+ o.oauth_nonce = index$13.nonce();
+ return o;
+ }
+
+ // get/set tokens. These are prefixed with the base URL so that `osm-auth`
+ // can be used with multiple APIs and the keys in `localStorage`
+ // will not clash
+ var token;
+
+ if (store_legacy.enabled) {
+ token = function (x, y) {
+ if (arguments.length === 1) return store_legacy.get(o.url + x);
+ else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
+ };
+ } else {
+ var storage = {};
+ token = function (x, y) {
+ if (arguments.length === 1) return storage[o.url + x];
+ else if (arguments.length === 2) return storage[o.url + x] = y;
+ };
+ }
+
+ // Get an authentication object. If you just add and remove properties
+ // from a single object, you'll need to use `delete` to make sure that
+ // it doesn't contain undesired properties for authentication
+ function getAuth(o) {
+ return {
+ oauth_consumer_key: o.oauth_consumer_key,
+ oauth_signature_method: 'HMAC-SHA1'
+ };
+ }
+
+ // potentially pre-authorize
+ oauth.options(o);
+
+ return oauth;
+};
+
+var JXON = new (function () {
+ var
+ sValueProp = 'keyValue', sAttributesProp = 'keyAttributes', sAttrPref = '@', /* you can customize these values */
+ aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
+
+ function parseText (sValue) {
+ if (rIsNull.test(sValue)) { return null; }
+ if (rIsBool.test(sValue)) { return sValue.toLowerCase() === 'true'; }
+ if (isFinite(sValue)) { return parseFloat(sValue); }
+ if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
+ return sValue;
+ }
+
+ function EmptyTree () { }
+ EmptyTree.prototype.toString = function () { return 'null'; };
+ EmptyTree.prototype.valueOf = function () { return null; };
+
+ function objectify (vValue) {
+ return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
+ }
+
+ function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
+ var
+ nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
+ bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
+
+ var
+ sProp, vContent, nLength = 0, sCollectedTxt = '',
+ vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
+
+ if (bChildren) {
+ for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
+ oNode = oParentNode.childNodes.item(nItem);
+ if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is 'CDATASection' (4) */
+ else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is 'Text' (3) */
+ else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is 'Element' (1) */
+ }
+ }
+
+ var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
+
+ if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
+
+ for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
+ sProp = aCache[nElId].nodeName.toLowerCase();
+ vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
+ if (vResult.hasOwnProperty(sProp)) {
+ if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
+ vResult[sProp].push(vContent);
+ } else {
+ vResult[sProp] = vContent;
+ nLength++;
+ }
+ }
+
+ if (bAttributes) {
+ var
+ nAttrLen = oParentNode.attributes.length,
+ sAPrefix = bNesteAttr ? '' : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
+
+ for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
+ oAttrib = oParentNode.attributes.item(nAttrib);
+ oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
+ }
+
+ if (bNesteAttr) {
+ if (bFreeze) { Object.freeze(oAttrParent); }
+ vResult[sAttributesProp] = oAttrParent;
+ nLength -= nAttrLen - 1;
+ }
+ }
+
+ if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
+ vResult[sValueProp] = vBuiltVal;
+ } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
+ vResult = vBuiltVal;
+ }
+
+ if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
+
+ aCache.length = nLevelStart;
+
+ return vResult;
+ }
+
+ function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
+ var vValue, oChild;
+
+ if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
+ oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
+ } else if (oParentObj.constructor === Date) {
+ oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
+ }
+
+ for (var sName in oParentObj) {
+ vValue = oParentObj[sName];
+ if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
+ if (sName === sValueProp) {
+ if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
+ } else if (sName === sAttributesProp) { /* verbosity level is 3 */
+ for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
+ } else if (sName.charAt(0) === sAttrPref) {
+ oParentEl.setAttribute(sName.slice(1), vValue);
+ } else if (vValue.constructor === Array) {
+ for (var nItem = 0; nItem < vValue.length; nItem++) {
+ oChild = oXMLDoc.createElement(sName);
+ loadObjTree(oXMLDoc, oChild, vValue[nItem]);
+ oParentEl.appendChild(oChild);
+ }
+ } else {
+ oChild = oXMLDoc.createElement(sName);
+ if (vValue instanceof Object) {
+ loadObjTree(oXMLDoc, oChild, vValue);
+ } else if (vValue !== null && vValue !== true) {
+ oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
+ }
+ oParentEl.appendChild(oChild);
+ }
+ }
+ }
+
+ this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
+ var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
+ return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
+ };
+
+ this.unbuild = function (oObjTree) {
+ var oNewDoc = document.implementation.createDocument('', '', null);
+ loadObjTree(oNewDoc, oNewDoc, oObjTree);
+ return oNewDoc;
+ };
+
+ this.stringify = function (oObjTree) {
+ return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
+ };
+})();
+
+// var myObject = JXON.build(doc);
+// we got our javascript object! try: alert(JSON.stringify(myObject));
+
+// var newDoc = JXON.unbuild(myObject);
+// we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
+
+var dispatch$3 = dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded');
+var urlroot = 'https://www.openstreetmap.org';
+var blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
+var inflight$1 = {};
+var loadedTiles = {};
+var tileZoom$1 = 16;
+var oauth = index$12({
+ url: urlroot,
+ oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
+ oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
+ loading: authLoading,
+ done: authDone
+ });
+var rateLimitError;
+var userChangesets;
+var userDetails;
+var off;
+
+
+function authLoading() {
+ dispatch$3.call('authLoading');
+}
+
+
+function authDone() {
+ dispatch$3.call('authDone');
+}
+
+
+function abortRequest$1(i) {
+ if (i) {
+ i.abort();
+ }
+}
+
+
+function getLoc(attrs) {
+ var lon = attrs.lon && attrs.lon.value,
+ lat = attrs.lat && attrs.lat.value;
+ return [parseFloat(lon), parseFloat(lat)];
+}
+
+
+function getNodes(obj) {
+ var elems = obj.getElementsByTagName('nd'),
+ nodes = new Array(elems.length);
+ for (var i = 0, l = elems.length; i < l; i++) {
+ nodes[i] = 'n' + elems[i].attributes.ref.value;
+ }
+ return nodes;
+}
+
+
+function getTags(obj) {
+ var elems = obj.getElementsByTagName('tag'),
+ tags = {};
+ for (var i = 0, l = elems.length; i < l; i++) {
+ var attrs = elems[i].attributes;
+ tags[attrs.k.value] = attrs.v.value;
+ }
+
+ return tags;
+}
+
+
+function getMembers(obj) {
+ var elems = obj.getElementsByTagName('member'),
+ members = new Array(elems.length);
+ for (var i = 0, l = elems.length; i < l; i++) {
+ var attrs = elems[i].attributes;
+ members[i] = {
+ id: attrs.type.value[0] + attrs.ref.value,
+ type: attrs.type.value,
+ role: attrs.role.value
+ };
+ }
+ return members;
+}
+
+
+function getVisible(attrs) {
+ return (!attrs.visible || attrs.visible.value !== 'false');
+}
+
+
+var parsers = {
+ node: function nodeData(obj) {
+ var attrs = obj.attributes;
+ return new osmNode({
+ id: osmEntity$$1.id.fromOSM('node', attrs.id.value),
+ visible: getVisible(attrs),
+ version: attrs.version.value,
+ changeset: attrs.changeset && attrs.changeset.value,
+ timestamp: attrs.timestamp && attrs.timestamp.value,
+ user: attrs.user && attrs.user.value,
+ uid: attrs.uid && attrs.uid.value,
+ loc: getLoc(attrs),
+ tags: getTags(obj)
+ });
+ },
+
+ way: function wayData(obj) {
+ var attrs = obj.attributes;
+ return new osmWay({
+ id: osmEntity$$1.id.fromOSM('way', attrs.id.value),
+ visible: getVisible(attrs),
+ version: attrs.version.value,
+ changeset: attrs.changeset && attrs.changeset.value,
+ timestamp: attrs.timestamp && attrs.timestamp.value,
+ user: attrs.user && attrs.user.value,
+ uid: attrs.uid && attrs.uid.value,
+ tags: getTags(obj),
+ nodes: getNodes(obj),
+ });
+ },
+
+ relation: function relationData(obj) {
+ var attrs = obj.attributes;
+ return new osmRelation({
+ id: osmEntity$$1.id.fromOSM('relation', attrs.id.value),
+ visible: getVisible(attrs),
+ version: attrs.version.value,
+ changeset: attrs.changeset && attrs.changeset.value,
+ timestamp: attrs.timestamp && attrs.timestamp.value,
+ user: attrs.user && attrs.user.value,
+ uid: attrs.uid && attrs.uid.value,
+ tags: getTags(obj),
+ members: getMembers(obj)
+ });
+ }
+};
+
+
+function parse$1(xml$$1) {
+ if (!xml$$1 || !xml$$1.childNodes) return;
+
+ var root = xml$$1.childNodes[0],
+ children = root.childNodes,
+ entities = [];
+
+ for (var i = 0, l = children.length; i < l; i++) {
+ var child = children[i],
+ parser = parsers[child.nodeName];
+ if (parser) {
+ entities.push(parser(child));
+ }
+ }
+
+ return entities;
+}
+
+
+var serviceOsm = {
+
+ init: function() {
+ utilRebind(this, dispatch$3, 'on');
+ },
+
+
+ reset: function() {
+ userChangesets = undefined;
+ userDetails = undefined;
+ rateLimitError = undefined;
+ lodash.forEach(inflight$1, abortRequest$1);
+ loadedTiles = {};
+ inflight$1 = {};
+ return this;
+ },
+
+
+ changesetURL: function(changesetId) {
+ return urlroot + '/changeset/' + changesetId;
+ },
+
+
+ changesetsURL: function(center, zoom$$1) {
+ var precision = Math.max(0, Math.ceil(Math.log(zoom$$1) / Math.LN2));
+ return urlroot + '/history#map=' +
+ Math.floor(zoom$$1) + '/' +
+ center[1].toFixed(precision) + '/' +
+ center[0].toFixed(precision);
+ },
+
+
+ entityURL: function(entity) {
+ return urlroot + '/' + entity.type + '/' + entity.osmId();
+ },
+
+
+ historyURL: function(entity) {
+ return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
+ },
+
+
+ userURL: function(username) {
+ return urlroot + '/user/' + username;
+ },
+
+
+ loadFromAPI: function(path$$1, callback) {
+ var that = this;
+
+ function done(err, xml$$1) {
+ var isAuthenticated = that.authenticated();
+
+ // 400 Bad Request, 401 Unauthorized, 403 Forbidden
+ // Logout and retry the request..
+ if (isAuthenticated && err &&
+ (err.status === 400 || err.status === 401 || err.status === 403)) {
+ that.logout();
+ that.loadFromAPI(path$$1, callback);
+
+ // else, no retry..
+ } else {
+ // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
+ // Set the rateLimitError flag and trigger a warning..
+ if (!isAuthenticated && !rateLimitError && err &&
+ (err.status === 509 || err.status === 429)) {
+ rateLimitError = err;
+ dispatch$3.call('change');
+ }
+
+ if (callback) {
+ callback(err, parse$1(xml$$1));
+ }
+ }
+ }
+
+ if (this.authenticated()) {
+ return oauth.xhr({ method: 'GET', path: path$$1 }, done);
+ } else {
+ var url = urlroot + path$$1;
+ return xml(url).get(done);
+ }
+ },
+
+
+ loadEntity: function(id, callback) {
+ var type = osmEntity$$1.id.type(id),
+ osmID = osmEntity$$1.id.toOSM(id);
+
+ this.loadFromAPI(
+ '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
+ function(err, entities) {
+ if (callback) callback(err, { data: entities });
+ }
+ );
+ },
+
+
+ loadEntityVersion: function(id, version$$1, callback) {
+ var type = osmEntity$$1.id.type(id),
+ osmID = osmEntity$$1.id.toOSM(id);
+
+ this.loadFromAPI(
+ '/api/0.6/' + type + '/' + osmID + '/' + version$$1,
+ function(err, entities) {
+ if (callback) callback(err, { data: entities });
+ }
+ );
+ },
+
+
+ loadMultiple: function(ids, callback) {
+ var that = this;
+ lodash.each(lodash.groupBy(lodash.uniq(ids), osmEntity$$1.id.type), function(v, k) {
+ var type = k + 's',
+ osmIDs = lodash.map(v, osmEntity$$1.id.toOSM);
+
+ lodash.each(lodash.chunk(osmIDs, 150), function(arr) {
+ that.loadFromAPI(
+ '/api/0.6/' + type + '?' + type + '=' + arr.join(),
+ function(err, entities) {
+ if (callback) callback(err, { data: entities });
+ }
+ );
+ });
+ });
+ },
+
+
+ authenticated: function() {
+ return oauth.authenticated();
+ },
+
+
+ putChangeset: function(changeset, changes, callback) {
+
+ // Create the changeset..
+ oauth.xhr({
+ method: 'PUT',
+ path: '/api/0.6/changeset/create',
+ options: { header: { 'Content-Type': 'text/xml' } },
+ content: JXON.stringify(changeset.asJXON())
+ }, createdChangeset);
+
+
+ function createdChangeset(err, changeset_id) {
+ if (err) return callback(err);
+ changeset = changeset.update({ id: changeset_id });
+
+ // Upload the changeset..
+ oauth.xhr({
+ method: 'POST',
+ path: '/api/0.6/changeset/' + changeset_id + '/upload',
+ options: { header: { 'Content-Type': 'text/xml' } },
+ content: JXON.stringify(changeset.osmChangeJXON(changes))
+ }, uploadedChangeset);
+ }
+
+
+ function uploadedChangeset(err) {
+ if (err) return callback(err);
+
+ // Upload was successful, safe to call the callback.
+ // Add delay to allow for postgres replication #1646 #2678
+ window.setTimeout(function() {
+ callback(null, changeset);
+ }, 2500);
+
+ // Still attempt to close changeset, but ignore response because #2667
+ oauth.xhr({
+ method: 'PUT',
+ path: '/api/0.6/changeset/' + changeset.id + '/close',
+ options: { header: { 'Content-Type': 'text/xml' } }
+ }, function() { return true; });
+ }
+ },
+
+
+ userDetails: function(callback) {
+ if (userDetails) {
+ callback(undefined, userDetails);
+ return;
+ }
+
+ function done(err, user_details) {
+ if (err) return callback(err);
+
+ var u = user_details.getElementsByTagName('user')[0],
+ img = u.getElementsByTagName('img'),
+ image_url = '';
+
+ if (img && img[0] && img[0].getAttribute('href')) {
+ image_url = img[0].getAttribute('href');
+ }
+
+ var changesets = u.getElementsByTagName('changesets'),
+ changesets_count = 0;
+
+ if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
+ changesets_count = changesets[0].getAttribute('count');
+ }
+
+ userDetails = {
+ id: u.attributes.id.value,
+ display_name: u.attributes.display_name.value,
+ image_url: image_url,
+ changesets_count: changesets_count
+ };
+
+ callback(undefined, userDetails);
+ }
+
+ oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
+ },
+
+
+ userChangesets: function(callback) {
+ if (userChangesets) {
+ callback(undefined, userChangesets);
+ return;
+ }
+
+ this.userDetails(function(err, user) {
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ function done(err, changesets) {
+ if (err) {
+ callback(err);
+ } else {
+ userChangesets = Array.prototype.map.call(
+ changesets.getElementsByTagName('changeset'),
+ function (changeset) {
+ return { tags: getTags(changeset) };
+ }
+ ).filter(function (changeset) {
+ var comment = changeset.tags.comment;
+ return comment && comment !== '';
+ });
+ callback(undefined, userChangesets);
+ }
+ }
+
+ oauth.xhr({ method: 'GET', path: '/api/0.6/changesets?user=' + user.id }, done);
+ });
+ },
+
+
+ status: function(callback) {
+ function done(xml$$1) {
+ // update blacklists
+ var elements = xml$$1.getElementsByTagName('blacklist'),
+ regexes = [];
+ for (var i = 0; i < elements.length; i++) {
+ var regex = elements[i].getAttribute('regex'); // needs unencode?
+ if (regex) {
+ regexes.push(regex);
+ }
+ }
+ if (regexes.length) {
+ blacklists = regexes;
+ }
+
+
+ if (rateLimitError) {
+ callback(rateLimitError, 'rateLimited');
+ } else {
+ var apiStatus = xml$$1.getElementsByTagName('status'),
+ val = apiStatus[0].getAttribute('api');
+
+ callback(undefined, val);
+ }
+ }
+
+ xml(urlroot + '/api/capabilities').get()
+ .on('load', done)
+ .on('error', callback);
+ },
+
+
+ imageryBlacklists: function() {
+ return blacklists;
+ },
+
+
+ tileZoom: function(_) {
+ if (!arguments.length) return tileZoom$1;
+ tileZoom$1 = _;
+ return this;
+ },
+
+
+ loadTiles: function(projection$$1, dimensions, callback) {
+ if (off) return;
+
+ var that = this,
+ s = projection$$1.scale() * 2 * Math.PI,
+ z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
+ ts = 256 * Math.pow(2, z - tileZoom$1),
+ origin = [
+ s / 2 - projection$$1.translate()[0],
+ s / 2 - projection$$1.translate()[1]
+ ];
+
+ var tiles = d3geoTile()
+ .scaleExtent([tileZoom$1, tileZoom$1])
+ .scale(s)
+ .size(dimensions)
+ .translate(projection$$1.translate())()
+ .map(function(tile) {
+ var x = tile[0] * ts - origin[0],
+ y = tile[1] * ts - origin[1];
+
+ return {
+ id: tile.toString(),
+ extent: geoExtent$$1(
+ projection$$1.invert([x, y + ts]),
+ projection$$1.invert([x + ts, y]))
+ };
+ });
+
+ lodash.filter(inflight$1, function(v, i) {
+ var wanted = lodash.find(tiles, function(tile) {
+ return i === tile.id;
+ });
+ if (!wanted) delete inflight$1[i];
+ return !wanted;
+ }).map(abortRequest$1);
+
+ tiles.forEach(function(tile) {
+ var id = tile.id;
+
+ if (loadedTiles[id] || inflight$1[id]) return;
+
+ if (lodash.isEmpty(inflight$1)) {
+ dispatch$3.call('loading');
+ }
+
+ inflight$1[id] = that.loadFromAPI(
+ '/api/0.6/map?bbox=' + tile.extent.toParam(),
+ function(err, parsed) {
+ delete inflight$1[id];
+ if (!err) {
+ loadedTiles[id] = true;
+ }
+
+ if (callback) {
+ callback(err, lodash.extend({ data: parsed }, tile));
+ }
+
+ if (lodash.isEmpty(inflight$1)) {
+ dispatch$3.call('loaded');
+ }
+ }
+ );
+ });
+ },
+
+
+ switch: function(options) {
+ urlroot = options.urlroot;
+
+ oauth.options(lodash.extend({
+ url: urlroot,
+ loading: authLoading,
+ done: authDone
+ }, options));
+
+ dispatch$3.call('change');
+ this.reset();
+ this.userChangesets(function() {}); // eagerly load user details/changesets
+ return this;
+ },
+
+
+ toggle: function(_) {
+ off = !_;
+ return this;
+ },
+
+
+ loadedTiles: function(_) {
+ if (!arguments.length) return loadedTiles;
+ loadedTiles = _;
+ return this;
+ },
+
+
+ logout: function() {
+ userChangesets = undefined;
+ userDetails = undefined;
+ oauth.logout();
+ dispatch$3.call('change');
+ return this;
+ },
+
+
+ authenticate: function(callback) {
+ var that = this;
+ userChangesets = undefined;
+ userDetails = undefined;
+
+ function done(err, res) {
+ rateLimitError = undefined;
+ dispatch$3.call('change');
+ if (callback) callback(err, res);
+ that.userChangesets(function() {}); // eagerly load user details/changesets
+ }
+
+ return oauth.authenticate(done);
+ }
+};
+
+var apibase$2 = 'https://taginfo.openstreetmap.org/api/4/';
+var inflight$2 = {};
+var popularKeys = {};
+var taginfoCache = {};
+var tag_sorts = {
+ point: 'count_nodes',
+ vertex: 'count_nodes',
+ area: 'count_ways',
+ line: 'count_ways'
+ };
+var tag_sort_members = {
+ point: 'count_node_members',
+ vertex: 'count_node_members',
+ area: 'count_way_members',
+ line: 'count_way_members',
+ relation: 'count_relation_members'
+ };
+var tag_filters = {
+ point: 'nodes',
+ vertex: 'nodes',
+ area: 'ways',
+ line: 'ways'
+ };
+var tag_members_fractions = {
+ point: 'count_node_members_fraction',
+ vertex: 'count_node_members_fraction',
+ area: 'count_way_members_fraction',
+ line: 'count_way_members_fraction',
+ relation: 'count_relation_members_fraction'
+ };
+
+
+function sets(params, n, o) {
+ if (params.geometry && o[params.geometry]) {
+ params[n] = o[params.geometry];
+ }
+ return params;
+}
+
+
+function setFilter(params) {
+ return sets(params, 'filter', tag_filters);
+}
+
+
+function setSort(params) {
+ return sets(params, 'sortname', tag_sorts);
+}
+
+
+function setSortMembers(params) {
+ return sets(params, 'sortname', tag_sort_members);
+}
+
+
+function clean(params) {
+ return lodash.omit(params, ['geometry', 'debounce']);
+}
+
+
+function filterKeys(type) {
+ var count_type = type ? 'count_' + type : 'count_all';
+ return function(d) {
+ return parseFloat(d[count_type]) > 2500 || d.in_wiki;
+ };
+}
+
+
+function filterMultikeys(prefix) {
+ return function(d) {
+ // d.key begins with prefix, and d.key contains no additional ':'s
+ var re = new RegExp('^' + prefix + '(.*)$');
+ var matches = d.key.match(re) || [];
+ return (matches.length === 2 && matches[1].indexOf(':') === -1);
+ };
+}
+
+
+function filterValues(allowUpperCase) {
+ return function(d) {
+ if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
+ if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
+ return parseFloat(d.fraction) > 0.0 || d.in_wiki;
+ };
+}
+
+
+function filterRoles(geometry) {
+ return function(d) {
+ if (d.role === '') return false; // exclude empty role
+ if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
+ return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
+ };
+}
+
+
+function valKey(d) {
+ return {
+ value: d.key,
+ title: d.key
+ };
+}
+
+
+function valKeyDescription(d) {
+ return {
+ value: d.value,
+ title: d.description || d.value
+ };
+}
+
+
+function roleKey(d) {
+ return {
+ value: d.role,
+ title: d.role
+ };
+}
+
+
+// sort keys with ':' lower than keys without ':'
+function sortKeys(a, b) {
+ return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
+ : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
+ : 0;
+}
+
+
+var debouncedRequest = lodash.debounce(request$1, 500, { leading: false });
+
+function request$1(url, params, exactMatch, callback, loaded) {
+ if (inflight$2[url]) return;
+
+ if (checkCache(url, params, exactMatch, callback)) return;
+
+ inflight$2[url] = json(url, function (err, data) {
+ delete inflight$2[url];
+ loaded(err, data);
+ });
+}
+
+
+function checkCache(url, params, exactMatch, callback) {
+ var rp = params.rp || 25,
+ testQuery = params.query || '',
+ testUrl = url;
+
+ do {
+ var hit = taginfoCache[testUrl];
+
+ // exact match, or shorter match yielding fewer than max results (rp)
+ if (hit && (url === testUrl || hit.length < rp)) {
+ callback(null, hit);
+ return true;
+ }
+
+ // don't try to shorten the query
+ if (exactMatch || !testQuery.length) return false;
+
+ // do shorten the query to see if we already have a cached result
+ // that has returned fewer than max results (rp)
+ testQuery = testQuery.slice(0, -1);
+ testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
+ } while (testQuery.length >= 0);
+
+ return false;
+}
+
+
+var serviceTaginfo = {
+
+ init: function() {
+ inflight$2 = {};
+ taginfoCache = {};
+ popularKeys = {};
+
+ // Fetch popular keys. We'll exclude these from `values`
+ // lookups because they stress taginfo, and they aren't likely
+ // to yield meaningful autocomplete results.. see #3955
+ var params = { rp: 100, sortname: 'values_all', sortorder: 'desc', page: 1, debounce: false };
+ this.keys(params, function(err, data) {
+ if (err) return;
+ data.forEach(function(d) {
+ if (d.value === 'opening_hours') return; // exception
+ popularKeys[d.value] = true;
+ });
+ });
+ },
+
+
+ reset: function() {
+ lodash.forEach(inflight$2, function(req) { req.abort(); });
+ inflight$2 = {};
+ },
+
+
+ keys: function(params, callback) {
+ var doRequest = params.debounce ? debouncedRequest : request$1;
+ params = clean(setSort(params));
+ params = lodash.extend({ rp: 10, sortname: 'count_all', sortorder: 'desc', page: 1 }, params);
+
+ var url = apibase$2 + 'keys/all?' + utilQsString(params);
+ doRequest(url, params, false, callback, function(err, d) {
+ if (err) {
+ callback(err);
+ } else {
+ var f = filterKeys(params.filter);
+ var result = d.data.filter(f).sort(sortKeys).map(valKey);
+ taginfoCache[url] = result;
+ callback(null, result);
+ }
+ });
+ },
+
+
+ multikeys: function(params, callback) {
+ var doRequest = params.debounce ? debouncedRequest : request$1;
+ params = clean(setSort(params));
+ params = lodash.extend({ rp: 25, sortname: 'count_all', sortorder: 'desc', page: 1 }, params);
+ var prefix = params.query;
+
+ var url = apibase$2 + 'keys/all?' + utilQsString(params);
+ doRequest(url, params, true, callback, function(err, d) {
+ if (err) {
+ callback(err);
+ } else {
+ var f = filterMultikeys(prefix);
+ var result = d.data.filter(f).map(valKey);
+ taginfoCache[url] = result;
+ callback(null, result);
+ }
+ });
+ },
+
+
+ values: function(params, callback) {
+ // Exclude popular keys from values lookups.. see #3955
+ var key = params.key;
+ if (key && popularKeys[key]) {
+ callback(null, []);
+ return;
+ }
+
+ var doRequest = params.debounce ? debouncedRequest : request$1;
+ params = clean(setSort(setFilter(params)));
+ params = lodash.extend({ rp: 25, sortname: 'count_all', sortorder: 'desc', page: 1 }, params);
+
+ var url = apibase$2 + 'key/values?' + utilQsString(params);
+ doRequest(url, params, false, callback, function(err, d) {
+ if (err) {
+ callback(err);
+ } else {
+ // In most cases we prefer taginfo value results with lowercase letters.
+ // A few OSM keys expect values to contain uppercase values (see #3377).
+ // This is not an exhaustive list (e.g. `name` also has uppercase values)
+ // but these are the fields where taginfo value lookup is most useful.
+ var re = /network|taxon|genus|species|brand|grape_variety|_hours|_times/;
+ var allowUpperCase = (params.key.match(re) !== null);
+ var f = filterValues(allowUpperCase);
+
+ var result = d.data.filter(f).map(valKeyDescription);
+ taginfoCache[url] = result;
+ callback(null, result);
+ }
+ });
+ },
+
+
+ roles: function(params, callback) {
+ var doRequest = params.debounce ? debouncedRequest : request$1;
+ var geometry = params.geometry;
+ params = clean(setSortMembers(params));
+ params = lodash.extend({ rp: 25, sortname: 'count_all_members', sortorder: 'desc', page: 1 }, params);
+
+ var url = apibase$2 + 'relation/roles?' + utilQsString(params);
+ doRequest(url, params, true, callback, function(err, d) {
+ if (err) {
+ callback(err);
+ } else {
+ var f = filterRoles(geometry);
+ var result = d.data.filter(f).map(roleKey);
+ taginfoCache[url] = result;
+ callback(null, result);
+ }
+ });
+ },
+
+
+ docs: function(params, callback) {
+ var doRequest = params.debounce ? debouncedRequest : request$1;
+ params = clean(setSort(params));
+
+ var path$$1 = 'key/wiki_pages?';
+ if (params.value) path$$1 = 'tag/wiki_pages?';
+ else if (params.rtype) path$$1 = 'relation/wiki_pages?';
+
+ var url = apibase$2 + path$$1 + utilQsString(params);
+ doRequest(url, params, true, callback, function(err, d) {
+ if (err) {
+ callback(err);
+ } else {
+ taginfoCache[url] = d.data;
+ callback(null, d.data);
+ }
+ });
+ },
+
+
+ apibase: function(_) {
+ if (!arguments.length) return apibase$2;
+ apibase$2 = _;
+ return this;
+ }
+
+};
+
+var jsonpCache = {};
+window.jsonpCache = jsonpCache;
+
+function jsonpRequest(url, callback) {
+
+ if (window.JSONP_FIX) {
+ if (window.JSONP_DELAY === 0) {
+ callback(window.JSONP_FIX);
+ } else {
+ setTimeout(function() {
+ callback(window.JSONP_FIX);
+ }, window.JSONP_DELAY || 0);
+ }
+ return;
+ }
+
+ function rand() {
+ var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
+ c = '', i = -1;
+ while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52));
+ return c;
+ }
+
+ function create(url) {
+ var e = url.match(/callback=(\w+)/),
+ c = e ? e[1] : rand();
+ jsonpCache[c] = function(data) {
+ callback(data);
+ delete jsonpCache[c];
+ script.remove();
+ };
+ return 'jsonpCache.' + c;
+ }
+
+ var cb = create(url),
+ script = select('head')
+ .append('script')
+ .attr('type', 'text/javascript')
+ .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
+}
+
+var endpoint = 'https://www.wikidata.org/w/api.php?';
+
+var serviceWikidata = {
+
+ init: function() {},
+ reset: function() {},
+
+
+ // Given a Wikipedia language and article title, return an array of
+ // corresponding Wikidata entities.
+ itemsByTitle: function(lang, title, callback) {
+ if (!title) {
+ callback('', {});
+ return;
+ }
+
+ lang = lang || 'en';
+ jsonpRequest(endpoint + utilQsString({
+ action: 'wbgetentities',
+ format: 'json',
+ sites: lang.replace(/-/g, '_') + 'wiki',
+ titles: title,
+ languages: 'en', // shrink response by filtering to one language
+ callback: '{callback}'
+ }), function(data) {
+ if (!data || data.error) {
+ callback('', {});
+ } else {
+ callback(title, data.entities || {});
+ }
+ });
+ }
+
+};
+
+var endpoint$1 = 'https://en.wikipedia.org/w/api.php?';
+
+var serviceWikipedia = {
+
+ init: function() {},
+ reset: function() {},
+
+
+ search: function(lang, query, callback) {
+ if (!query) {
+ callback('', []);
+ return;
+ }
+
+ lang = lang || 'en';
+ jsonpRequest(endpoint$1.replace('en', lang) +
+ utilQsString({
+ action: 'query',
+ list: 'search',
+ srlimit: '10',
+ srinfo: 'suggestion',
+ format: 'json',
+ callback: '{callback}',
+ srsearch: query
+ }), function(data) {
+ if (!data || !data.query || !data.query.search || data.error) {
+ callback('', []);
+ } else {
+ var results = data.query.search.map(function(d) { return d.title; });
+ callback(query, results);
+ }
+ }
+ );
+ },
+
+
+ suggestions: function(lang, query, callback) {
+ if (!query) {
+ callback('', []);
+ return;
+ }
+
+ lang = lang || 'en';
+ jsonpRequest(endpoint$1.replace('en', lang) +
+ utilQsString({
+ action: 'opensearch',
+ namespace: 0,
+ suggest: '',
+ format: 'json',
+ callback: '{callback}',
+ search: query
+ }), function(data) {
+ if (!data || data.error) {
+ callback('', []);
+ } else {
+ callback(data[0], data[1] || []);
+ }
+ }
+ );
+ },
+
+
+ translations: function(lang, title, callback) {
+ if (!title) {
+ callback({});
+ return;
+ }
+
+ jsonpRequest(endpoint$1.replace('en', lang) +
+ utilQsString({
+ action: 'query',
+ prop: 'langlinks',
+ format: 'json',
+ callback: '{callback}',
+ lllimit: 500,
+ titles: title
+ }), function(data) {
+ if (!data || !data.query || !data.query.pages || data.error) {
+ callback({});
+ } else {
+ var list = data.query.pages[Object.keys(data.query.pages)[0]],
+ translations = {};
+ if (list && list.langlinks) {
+ list.langlinks.forEach(function(d) {
+ translations[d.lang] = d['*'];
+ });
+ }
+ callback(translations);
+ }
+ }
+ );
+ }
+
+};
+
+var services = {
+ mapillary: serviceMapillary,
+ geocoder: serviceNominatim,
+ osm: serviceOsm,
+ taginfo: serviceTaginfo,
+ wikidata: serviceWikidata,
+ wikipedia: serviceWikipedia
+};
+
+function svgMapillaryImages(projection$$1, context, dispatch$$1) {
+ var throttledRedraw = lodash.throttle(function () { dispatch$$1.call('change'); }, 1000),
+ minZoom = 12,
+ minViewfieldZoom = 17,
+ layer = select(null),
+ _mapillary;
+
+
+ function init() {
+ if (svgMapillaryImages.initialized) return; // run once
+ svgMapillaryImages.enabled = false;
+ svgMapillaryImages.initialized = true;
+ }
+
+
+ function getMapillary() {
+ if (services.mapillary && !_mapillary) {
+ _mapillary = services.mapillary;
+ _mapillary.event.on('loadedImages', throttledRedraw);
+ } else if (!services.mapillary && _mapillary) {
+ _mapillary = null;
+ }
+
+ return _mapillary;
+ }
+
+
+ function showLayer() {
+ var mapillary = getMapillary();
+ if (!mapillary) return;
+
+ mapillary.loadViewer(context);
+ editOn();
+
+ layer
+ .style('opacity', 0)
+ .transition()
+ .duration(500)
+ .style('opacity', 1)
+ .on('end', function () { dispatch$$1.call('change'); });
+ }
+
+
+ function hideLayer() {
+ var mapillary = getMapillary();
+ if (mapillary) {
+ mapillary.hideViewer();
+ }
+
+ throttledRedraw.cancel();
+
+ layer
+ .transition()
+ .duration(500)
+ .style('opacity', 0)
+ .on('end', editOff);
+ }
+
+
+ function editOn() {
+ layer.style('display', 'block');
+ }
+
+
+ function editOff() {
+ layer.selectAll('.viewfield-group').remove();
+ layer.style('display', 'none');
+ }
+
+
+ function click(d) {
+ var mapillary = getMapillary();
+ if (!mapillary) return;
+
+ context.map().centerEase(d.loc);
+
+ mapillary
+ .selectedImage(d.key, true)
+ .updateViewer(d.key, context)
+ .showViewer();
+ }
+
+
+ function transform$$1(d) {
+ var t = svgPointTransform(projection$$1)(d);
+ if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
+ return t;
+ }
+
+
+ function update() {
+ var mapillary = getMapillary(),
+ data = (mapillary ? mapillary.images(projection$$1) : []),
+ imageKey = mapillary ? mapillary.selectedImage() : null;
+
+ var markers = layer.selectAll('.viewfield-group')
+ .data(data, function(d) { return d.key; });
+
+ markers.exit()
+ .remove();
+
+ var enter = markers.enter()
+ .append('g')
+ .attr('class', 'viewfield-group')
+ .classed('selected', function(d) { return d.key === imageKey; })
+ .on('click', click);
+
+ markers = markers
+ .merge(enter)
+ .attr('transform', transform$$1);
+
+
+ var viewfields = markers.selectAll('.viewfield')
+ .data(~~context.map().zoom() >= minViewfieldZoom ? [0] : []);
+
+ viewfields.exit()
+ .remove();
+
+ viewfields.enter()
+ .append('path')
+ .attr('class', 'viewfield')
+ .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
+ .attr('d', 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z');
+
+ markers.selectAll('circle')
+ .data([0])
+ .enter()
+ .append('circle')
+ .attr('dx', '0')
+ .attr('dy', '0')
+ .attr('r', '6');
+ }
+
+
+ function drawImages(selection$$1) {
+ var enabled = svgMapillaryImages.enabled,
+ mapillary = getMapillary();
+
+ layer = selection$$1.selectAll('.layer-mapillary-images')
+ .data(mapillary ? [0] : []);
+
+ layer.exit()
+ .remove();
+
+ layer = layer.enter()
+ .append('g')
+ .attr('class', 'layer-mapillary-images')
+ .style('display', enabled ? 'block' : 'none')
+ .merge(layer);
+
+ if (enabled) {
+ if (mapillary && ~~context.map().zoom() >= minZoom) {
+ editOn();
+ update();
+ mapillary.loadImages(projection$$1);
+ } else {
+ editOff();
+ }
+ }
+ }
+
+
+ drawImages.enabled = function(_) {
+ if (!arguments.length) return svgMapillaryImages.enabled;
+ svgMapillaryImages.enabled = _;
+ if (svgMapillaryImages.enabled) {
+ showLayer();
+ } else {
+ hideLayer();
+ }
+ dispatch$$1.call('change');
+ return this;
+ };
+
+
+ drawImages.supported = function() {
+ return !!getMapillary();
+ };
+
+
+ init();
+ return drawImages;
+}
+
+function svgMapillarySigns(projection$$1, context, dispatch$$1) {
+ var throttledRedraw = lodash.throttle(function () { dispatch$$1.call('change'); }, 1000),
+ minZoom = 12,
+ layer = select(null),
+ _mapillary;
+
+
+ function init() {
+ if (svgMapillarySigns.initialized) return; // run once
+ svgMapillarySigns.enabled = false;
+ svgMapillarySigns.initialized = true;
+ }
+
+
+ function getMapillary() {
+ if (services.mapillary && !_mapillary) {
+ _mapillary = services.mapillary;
+ _mapillary.event.on('loadedSigns', throttledRedraw);
+ } else if (!services.mapillary && _mapillary) {
+ _mapillary = null;
+ }
+ return _mapillary;
+ }
+
+
+ function showLayer() {
+ editOn();
+ }
+
+
+ function hideLayer() {
+ throttledRedraw.cancel();
+ editOff();
+ }
+
+
+ function editOn() {
+ layer.style('display', 'block');
+ }
+
+
+ function editOff() {
+ layer.selectAll('.icon-sign').remove();
+ layer.style('display', 'none');
+ }
+
+
+ function click(d) {
+ var mapillary = getMapillary();
+ if (!mapillary) return;
+
+ context.map().centerEase(d.loc);
+
+ mapillary
+ .selectedImage(d.key, true)
+ .updateViewer(d.key, context)
+ .showViewer();
+ }
+
+
+ function update() {
+ var mapillary = getMapillary(),
+ data = (mapillary ? mapillary.signs(projection$$1) : []),
+ imageKey = mapillary ? mapillary.selectedImage() : null;
+
+ var signs = layer.selectAll('.icon-sign')
+ .data(data, function(d) { return d.key; });
+
+ signs.exit()
+ .remove();
+
+ var enter = signs.enter()
+ .append('foreignObject')
+ .attr('class', 'icon-sign')
+ .attr('width', '32px') // for Firefox
+ .attr('height', '32px') // for Firefox
+ .classed('selected', function(d) { return d.key === imageKey; })
+ .on('click', click);
+
+ enter
+ .append('xhtml:body')
+ .attr('class', 'icon-sign-body')
+ .html(mapillary.signHTML);
+
+ signs
+ .merge(enter)
+ .attr('x', function(d) { return projection$$1(d.loc)[0] - 16; }) // offset by -16px to
+ .attr('y', function(d) { return projection$$1(d.loc)[1] - 16; }); // center signs on loc
+ }
+
+
+ function drawSigns(selection$$1) {
+ var enabled = svgMapillarySigns.enabled,
+ mapillary = getMapillary();
+
+ layer = selection$$1.selectAll('.layer-mapillary-signs')
+ .data(mapillary ? [0] : []);
+
+ layer.exit()
+ .remove();
+
+ layer = layer.enter()
+ .append('g')
+ .attr('class', 'layer-mapillary-signs')
+ .style('display', enabled ? 'block' : 'none')
+ .merge(layer);
+
+ if (enabled) {
+ if (mapillary && ~~context.map().zoom() >= minZoom) {
+ editOn();
+ update();
+ mapillary.loadSigns(context, projection$$1);
+ } else {
+ editOff();
+ }
+ }
+ }
+
+
+ drawSigns.enabled = function(_) {
+ if (!arguments.length) return svgMapillarySigns.enabled;
+ svgMapillarySigns.enabled = _;
+ if (svgMapillarySigns.enabled) {
+ showLayer();
+ } else {
+ hideLayer();
+ }
+ dispatch$$1.call('change');
+ return this;
+ };
+
+
+ drawSigns.supported = function() {
+ var mapillary = getMapillary();
+ return (mapillary && mapillary.signsSupported());
+ };
+
+
+ init();
+ return drawSigns;
+}
+
+function svgOsm() {
+ return function drawOsm(selection) {
+ var layers = selection.selectAll('.layer-osm')
+ .data(['areas', 'lines', 'hit', 'halo', 'label']);
+
+ layers.enter()
+ .append('g')
+ .attr('class', function(d) { return 'layer-osm layer-' + d; });
+ };
+}
+
+function svgLayers(projection$$1, context) {
+ var dispatch$$1 = dispatch('change'),
+ svg = select(null),
+ layers = [
+ { id: 'osm', layer: svgOsm(projection$$1, context, dispatch$$1) },
+ { id: 'gpx', layer: svgGpx(projection$$1, context, dispatch$$1) },
+ { id: 'mapillary-images', layer: svgMapillaryImages(projection$$1, context, dispatch$$1) },
+ { id: 'mapillary-signs', layer: svgMapillarySigns(projection$$1, context, dispatch$$1) },
+ { id: 'debug', layer: svgDebug(projection$$1, context, dispatch$$1) }
+ ];
+
+
+ function drawLayers(selection$$1) {
+ svg = selection$$1.selectAll('.surface')
+ .data([0]);
+
+ svg = svg.enter()
+ .append('svg')
+ .attr('class', 'surface')
+ .merge(svg);
+
+ var defs = svg.selectAll('.surface-defs')
+ .data([0]);
+
+ defs.enter()
+ .append('defs')
+ .attr('class', 'surface-defs');
+
+ var groups = svg.selectAll('.data-layer')
+ .data(layers);
+
+ groups.exit()
+ .remove();
+
+ groups.enter()
+ .append('g')
+ .attr('class', function(d) { return 'data-layer data-layer-' + d.id; })
+ .merge(groups)
+ .each(function(d) { select(this).call(d.layer); });
+ }
+
+
+ drawLayers.all = function() {
+ return layers;
+ };
+
+
+ drawLayers.layer = function(id) {
+ var obj = lodash.find(layers, function(o) {return o.id === id;});
+ return obj && obj.layer;
+ };
+
+
+ drawLayers.only = function(what) {
+ var arr = [].concat(what);
+ drawLayers.remove(lodash.difference(lodash.map(layers, 'id'), arr));
+ return this;
+ };
+
+
+ drawLayers.remove = function(what) {
+ var arr = [].concat(what);
+ arr.forEach(function(id) {
+ layers = lodash.reject(layers, function(o) {return o.id === id;});
+ });
+ dispatch$$1.call('change');
+ return this;
+ };
+
+
+ drawLayers.add = function(what) {
+ var arr = [].concat(what);
+ arr.forEach(function(obj) {
+ if ('id' in obj && 'layer' in obj) {
+ layers.push(obj);
+ }
+ });
+ dispatch$$1.call('change');
+ return this;
+ };
+
+
+ drawLayers.dimensions = function(_) {
+ if (!arguments.length) return utilGetDimensions(svg);
+ utilSetDimensions(svg, _);
+ return this;
+ };
+
+
+ return utilRebind(drawLayers, dispatch$$1, 'on');
+}
+
+function svgLines$$1(projection$$1, context) {
+ var detected = utilDetect();
+
+ var highway_stack = {
+ motorway: 0,
+ motorway_link: 1,
+ trunk: 2,
+ trunk_link: 3,
+ primary: 4,
+ primary_link: 5,
+ secondary: 6,
+ tertiary: 7,
+ unclassified: 8,
+ residential: 9,
+ service: 10,
+ footway: 11
+ };
+
+
+ function drawLines(selection$$1, graph, entities, filter) {
+
+
+ function waystack(a, b) {
+ var selected = context.selectedIDs(),
+ scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0,
+ scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
+
+ if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
+ if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
+ return scoreA - scoreB;
+ }
+
+
+ function drawLineGroup(selection$$1, klass, isSelected) {
+ var lines = selection$$1
+ .selectAll('path')
+ .filter(filter)
+ .data(getPathData(isSelected), osmEntity$$1.key);
+
+ lines.exit()
+ .remove();
+
+ // Optimization: call simple TagClasses only on enter selection. This
+ // works because osmEntity.key is defined to include the entity v attribute.
+ lines.enter()
+ .append('path')
+ .attr('class', function(d) {
+ return 'way line ' + klass + ' ' + d.id + (isSelected ? ' selected' : '') +
+ (oldMultiPolygonOuters[d.id] ? ' old-multipolygon' : '');
+ })
+ .call(svgTagClasses())
+ .merge(lines)
+ .sort(waystack)
+ .attr('d', getPath)
+ .call(svgTagClasses().tags(svgRelationMemberTags(graph)));
+
+ return selection$$1;
+ }
+
+
+ function getPathData(isSelected) {
+ return function() {
+ var layer = this.parentNode.__data__;
+ var data = pathdata[layer] || [];
+ return data.filter(function(d) {
+ if (isSelected)
+ return context.selectedIDs().indexOf(d.id) !== -1;
+ else
+ return context.selectedIDs().indexOf(d.id) === -1;
+ });
+ };
+ }
+
+
+ var getPath = svgPath(projection$$1, graph),
+ ways = [],
+ pathdata = {},
+ onewaydata = {},
+ oldMultiPolygonOuters = {};
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i],
+ outer = osmSimpleMultipolygonOuterMember(entity, graph);
+ if (outer) {
+ ways.push(entity.mergeTags(outer.tags));
+ oldMultiPolygonOuters[outer.id] = true;
+ } else if (entity.geometry(graph) === 'line') {
+ ways.push(entity);
+ }
+ }
+
+ ways = ways.filter(getPath);
+ pathdata = lodash.groupBy(ways, function(way) { return way.layer(); });
+
+ lodash.forOwn(pathdata, function(v, k) {
+ onewaydata[k] = lodash(v)
+ .filter(function(d) { return d.isOneWay(); })
+ .map(svgOneWaySegments(projection$$1, graph, 35))
+ .flatten()
+ .valueOf();
+ });
+
+
+ var layer = selection$$1.selectAll('.layer-lines');
+
+ var layergroup = layer
+ .selectAll('g.layergroup')
+ .data(sequence(-10, 11));
+
+ layergroup = layergroup.enter()
+ .append('g')
+ .attr('class', function(d) { return 'layergroup layer' + String(d); })
+ .merge(layergroup);
+
+ layergroup
+ .selectAll('g.linegroup')
+ .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
+ .enter()
+ .append('g')
+ .attr('class', function(d) { return 'linegroup line-' + d; });
+
+
+ layergroup.selectAll('g.line-shadow')
+ .call(drawLineGroup, 'shadow', false);
+ layergroup.selectAll('g.line-casing')
+ .call(drawLineGroup, 'casing', false);
+ layergroup.selectAll('g.line-stroke')
+ .call(drawLineGroup, 'stroke', false);
+
+ layergroup.selectAll('g.line-shadow-highlighted')
+ .call(drawLineGroup, 'shadow', true);
+ layergroup.selectAll('g.line-casing-highlighted')
+ .call(drawLineGroup, 'casing', true);
+ layergroup.selectAll('g.line-stroke-highlighted')
+ .call(drawLineGroup, 'stroke', true);
+
+
+ var onewaygroup = layergroup
+ .selectAll('g.onewaygroup')
+ .data(['oneway']);
+
+ onewaygroup = onewaygroup.enter()
+ .append('g')
+ .attr('class', 'onewaygroup')
+ .merge(onewaygroup);
+
+ var oneways = onewaygroup
+ .selectAll('path')
+ .filter(filter)
+ .data(
+ function() { return onewaydata[this.parentNode.__data__] || []; },
+ function(d) { return [d.id, d.index]; }
+ );
+
+ oneways.exit()
+ .remove();
+
+ oneways = oneways.enter()
+ .append('path')
+ .attr('class', 'oneway')
+ .attr('marker-mid', 'url(#oneway-marker)')
+ .merge(oneways)
+ .attr('d', function(d) { return d.d; });
+
+ if (detected.ie) {
+ oneways.each(function() { this.parentNode.insertBefore(this, this); });
+ }
+ }
+
+
+ return drawLines;
+}
+
+function svgMidpoints$$1(projection, context) {
+
+ return function drawMidpoints(selection, graph, entities, filter, extent) {
+ var layer = selection.selectAll('.layer-hit');
+
+ var mode = context.mode();
+ if (mode && mode.id !== 'select') {
+ layer.selectAll('g.midpoint').remove();
+ return;
+ }
+
+ var poly = extent.polygon(),
+ midpoints = {};
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+
+ if (entity.type !== 'way')
+ continue;
+ if (!filter(entity))
+ continue;
+ if (context.selectedIDs().indexOf(entity.id) < 0)
+ continue;
+
+ var nodes = graph.childNodes(entity);
+ for (var j = 0; j < nodes.length - 1; j++) {
+
+ var a = nodes[j],
+ b = nodes[j + 1],
+ id = [a.id, b.id].sort().join('-');
+
+ if (midpoints[id]) {
+ midpoints[id].parents.push(entity);
+ } else {
+ if (geoEuclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
+ var point = geoInterp(a.loc, b.loc, 0.5),
+ loc = null;
+
+ if (extent.intersects(point)) {
+ loc = point;
+ } else {
+ for (var k = 0; k < 4; k++) {
+ point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
+ if (point &&
+ geoEuclideanDistance(projection(a.loc), projection(point)) > 20 &&
+ geoEuclideanDistance(projection(b.loc), projection(point)) > 20)
+ {
+ loc = point;
+ break;
+ }
+ }
+ }
+
+ if (loc) {
+ midpoints[id] = {
+ type: 'midpoint',
+ id: id,
+ loc: loc,
+ edge: [a.id, b.id],
+ parents: [entity]
+ };
+ }
+ }
+ }
+ }
+ }
+
+
+ function midpointFilter(d) {
+ if (midpoints[d.id])
+ return true;
+
+ for (var i = 0; i < d.parents.length; i++) {
+ if (filter(d.parents[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ var groups = layer
+ .selectAll('g.midpoint')
+ .filter(midpointFilter)
+ .data(lodash.values(midpoints), function(d) { return d.id; });
+
+ groups.exit()
+ .remove();
+
+ var enter = groups.enter()
+ .insert('g', ':first-child')
+ .attr('class', 'midpoint');
+
+ enter.append('polygon')
+ .attr('points', '-6,8 10,0 -6,-8')
+ .attr('class', 'shadow');
+
+ enter.append('polygon')
+ .attr('points', '-3,4 5,0 -3,-4')
+ .attr('class', 'fill');
+
+ groups = groups
+ .merge(enter)
+ .attr('transform', function(d) {
+ var translate = svgPointTransform(projection),
+ a = graph.entity(d.edge[0]),
+ b = graph.entity(d.edge[1]),
+ angleVal = Math.round(geoAngle(a, b, projection) * (180 / Math.PI));
+ return translate(d) + ' rotate(' + angleVal + ')';
+ })
+ .call(svgTagClasses().tags(
+ function(d) { return d.parents[0].tags; }
+ ));
+
+ // Propagate data bindings.
+ groups.select('polygon.shadow');
+ groups.select('polygon.fill');
+
+ };
+}
+
+function svgOneWaySegments(projection$$1, graph, dt) {
+ return function(entity) {
+ var a,
+ b,
+ i = 0,
+ offset = dt,
+ segments = [],
+ clip = identity$5().clipExtent(projection$$1.clipExtent()).stream,
+ coordinates = graph.childNodes(entity).map(function(n) {
+ return n.loc;
+ });
+
+ if (entity.tags.oneway === '-1') coordinates.reverse();
+
+ geoStream({
+ type: 'LineString',
+ coordinates: coordinates
+ }, projection$$1.stream(clip({
+ lineStart: function() {},
+ lineEnd: function() {
+ a = null;
+ },
+ point: function(x, y) {
+ b = [x, y];
+
+ if (a) {
+ var span = geoEuclideanDistance(a, b) - offset;
+
+ if (span >= 0) {
+ var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
+ dx = dt * Math.cos(angle),
+ dy = dt * Math.sin(angle),
+ p = [a[0] + offset * Math.cos(angle),
+ a[1] + offset * Math.sin(angle)];
+
+ var segment = 'M' + a[0] + ',' + a[1] +
+ 'L' + p[0] + ',' + p[1];
+
+ for (span -= dt; span >= 0; span -= dt) {
+ p[0] += dx;
+ p[1] += dy;
+ segment += 'L' + p[0] + ',' + p[1];
+ }
+
+ segment += 'L' + b[0] + ',' + b[1];
+ segments.push({id: entity.id, index: i, d: segment});
+ }
+
+ offset = -span;
+ i++;
+ }
+
+ a = b;
+ }
+ })));
+
+ return segments;
+ };
+}
+
+function svgPath(projection$$1, graph, isArea) {
+
+ // Explanation of magic numbers:
+ // "padding" here allows space for strokes to extend beyond the viewport,
+ // so that the stroke isn't drawn along the edge of the viewport when
+ // the shape is clipped.
+ //
+ // When drawing lines, pad viewport by 5px.
+ // When drawing areas, pad viewport by 65px in each direction to allow
+ // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
+
+ var cache = {},
+ padding = isArea ? 65 : 5,
+ viewport = projection$$1.clipExtent(),
+ paddedExtent = [
+ [viewport[0][0] - padding, viewport[0][1] - padding],
+ [viewport[1][0] + padding, viewport[1][1] + padding]
+ ],
+ clip = identity$5().clipExtent(paddedExtent).stream,
+ project = projection$$1.stream,
+ path$$1 = index$4()
+ .projection({stream: function(output) { return project(clip(output)); }});
+
+ return function(entity) {
+ if (entity.id in cache) {
+ return cache[entity.id];
+ } else {
+ return cache[entity.id] = path$$1(entity.asGeoJSON(graph));
+ }
+ };
+}
+
+function svgPoints$$1(projection, context) {
+
+ function markerPath(selection, klass) {
+ selection
+ .attr('class', klass)
+ .attr('transform', 'translate(-8, -23)')
+ .attr('d', 'M 17,8 C 17,13 11,21 8.5,23.5 C 6,21 0,13 0,8 C 0,4 4,-0.5 8.5,-0.5 C 13,-0.5 17,4 17,8 z');
+ }
+
+ function sortY(a, b) {
+ return b.loc[1] - a.loc[1];
+ }
+
+
+ return function drawPoints(selection, graph, entities, filter) {
+ var wireframe = context.surface().classed('fill-wireframe'),
+ points = wireframe ? [] : lodash.filter(entities, function(e) {
+ return e.geometry(graph) === 'point';
+ });
+
+ points.sort(sortY);
+
+ var layer = selection.selectAll('.layer-hit');
+
+ var groups = layer.selectAll('g.point')
+ .filter(filter)
+ .data(points, osmEntity$$1.key);
+
+ groups.exit()
+ .remove();
+
+ var enter = groups.enter()
+ .append('g')
+ .attr('class', function(d) { return 'node point ' + d.id; })
+ .order();
+
+ enter.append('path')
+ .call(markerPath, 'shadow');
+
+ enter.append('ellipse')
+ .attr('cx', 0.5)
+ .attr('cy', 1)
+ .attr('rx', 6.5)
+ .attr('ry', 3)
+ .attr('class', 'stroke');
+
+ enter.append('path')
+ .call(markerPath, 'stroke');
+
+ enter.append('use')
+ .attr('transform', 'translate(-5, -19)')
+ .attr('class', 'icon')
+ .attr('width', '11px')
+ .attr('height', '11px');
+
+ groups = groups
+ .merge(enter)
+ .attr('transform', svgPointTransform(projection))
+ .call(svgTagClasses());
+
+ // Selecting the following implicitly
+ // sets the data (point entity) on the element
+ groups.select('.shadow');
+ groups.select('.stroke');
+ groups.select('.icon')
+ .attr('xlink:href', function(entity) {
+ var preset = context.presets().match(entity, graph),
+ picon = preset && preset.icon;
+
+ if (!picon)
+ return '';
+ else {
+ var isMaki = dataFeatureIcons.indexOf(picon) !== -1;
+ return '#' + picon + (isMaki ? '-11' : '');
+ }
+ });
+ };
+}
+
+function svgRelationMemberTags(graph) {
+ return function(entity) {
+ var tags = entity.tags;
+ graph.parentRelations(entity).forEach(function(relation) {
+ var type = relation.tags.type;
+ if (type === 'multipolygon' || type === 'boundary') {
+ tags = lodash.extend({}, relation.tags, tags);
+ }
+ });
+ return tags;
+ };
+}
+
+function svgTagClasses() {
+ var primaries = [
+ 'building', 'highway', 'railway', 'waterway', 'aeroway',
+ 'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse',
+ 'leisure', 'military', 'place'
+ ],
+ statuses = [
+ 'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
+ 'razed', 'demolished', 'obliterated'
+ ],
+ secondaries = [
+ 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
+ 'surface', 'tracktype', 'crossing'
+ ],
+ tagClassRe = /^tag-/,
+ tags = function(entity) { return entity.tags; };
+
+
+ var tagClasses = function(selection$$1) {
+ selection$$1.each(function tagClassesEach(entity) {
+ var value = this.className,
+ classes, primary, status;
+
+ if (value.baseVal !== undefined) value = value.baseVal;
+
+ classes = value.trim().split(/\s+/).filter(function(name) {
+ return name.length && !tagClassRe.test(name);
+ }).join(' ');
+
+ var t = tags(entity), i, k, v;
+
+ // pick at most one primary classification tag..
+ for (i = 0; i < primaries.length; i++) {
+ k = primaries[i];
+ v = t[k];
+ if (!v || v === 'no') continue;
+
+ primary = k;
+ if (statuses.indexOf(v) !== -1) { // e.g. `railway=abandoned`
+ status = v;
+ classes += ' tag-' + k;
+ } else {
+ classes += ' tag-' + k + ' tag-' + k + '-' + v;
+ }
+
+ break;
+ }
+
+ // add at most one status tag, only if relates to primary tag..
+ if (!status) {
+ for (i = 0; i < statuses.length; i++) {
+ k = statuses[i];
+ v = t[k];
+ if (!v || v === 'no') continue;
+
+ if (v === 'yes') { // e.g. `railway=rail + abandoned=yes`
+ status = k;
+ }
+ else if (primary && primary === v) { // e.g. `railway=rail + abandoned=railway`
+ status = k;
+ } else if (!primary && primaries.indexOf(v) !== -1) { // e.g. `abandoned=railway`
+ status = k;
+ primary = v;
+ classes += ' tag-' + v;
+ } // else ignore e.g. `highway=path + abandoned=railway`
+
+ if (status) break;
+ }
+ }
+
+ if (status) {
+ classes += ' tag-status tag-status-' + status;
+ }
+
+ // add any secondary (structure) tags
+ for (i = 0; i < secondaries.length; i++) {
+ k = secondaries[i];
+ v = t[k];
+ if (!v || v === 'no') continue;
+ classes += ' tag-' + k + ' tag-' + k + '-' + v;
+ }
+
+ // For highways, look for surface tagging..
+ if (primary === 'highway') {
+ var paved = (t.highway !== 'track');
+ for (k in t) {
+ v = t[k];
+ if (k in osmPavedTags) {
+ paved = !!osmPavedTags[k][v];
+ break;
+ }
+ }
+ if (!paved) {
+ classes += ' tag-unpaved';
+ }
+ }
+
+ classes = classes.trim();
+
+ if (classes !== value) {
+ select(this).attr('class', classes);
+ }
+ });
+ };
+
+
+ tagClasses.tags = function(_) {
+ if (!arguments.length) return tags;
+ tags = _;
+ return tagClasses;
+ };
+
+ return tagClasses;
+}
+
+function svgTurns(projection) {
+
+ return function drawTurns(selection, graph, turns) {
+
+ function key(turn) {
+ return [turn.from.node + turn.via.node + turn.to.node].join('-');
+ }
+
+ function icon(turn) {
+ var u = turn.u ? '-u' : '';
+ if (!turn.restriction)
+ return '#turn-yes' + u;
+ var restriction = graph.entity(turn.restriction).tags.restriction;
+ return '#turn-' +
+ (!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') + u;
+ }
+
+ var groups = selection.selectAll('.layer-hit').selectAll('g.turn')
+ .data(turns, key);
+
+ groups.exit()
+ .remove();
+
+
+ var enter = groups.enter()
+ .append('g')
+ .attr('class', 'turn');
+
+ var nEnter = enter
+ .filter(function (turn) { return !turn.u; });
+
+ nEnter.append('rect')
+ .attr('transform', 'translate(-22, -12)')
+ .attr('width', '44')
+ .attr('height', '24');
+
+ nEnter.append('use')
+ .attr('transform', 'translate(-22, -12)')
+ .attr('width', '44')
+ .attr('height', '24');
+
+
+ var uEnter = enter
+ .filter(function (turn) { return turn.u; });
+
+ uEnter.append('circle')
+ .attr('r', '16');
+
+ uEnter.append('use')
+ .attr('transform', 'translate(-16, -16)')
+ .attr('width', '32')
+ .attr('height', '32');
+
+
+ groups = groups
+ .merge(enter);
+
+ groups
+ .attr('transform', function (turn) {
+ var v = graph.entity(turn.via.node),
+ t = graph.entity(turn.to.node),
+ a = geoAngle(v, t, projection),
+ p = projection(v.loc),
+ r = turn.u ? 0 : 60;
+
+ return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ') ' +
+ 'rotate(' + a * 180 / Math.PI + ')';
+ });
+
+ groups.select('use')
+ .attr('xlink:href', icon);
+
+ groups.select('rect');
+ groups.select('circle');
+
+ return this;
+ };
+}
+
+function svgVertices$$1(projection$$1, context) {
+ var radiuses = {
+ // z16-, z17, z18+, tagged
+ shadow: [6, 7.5, 7.5, 11.5],
+ stroke: [2.5, 3.5, 3.5, 7],
+ fill: [1, 1.5, 1.5, 1.5]
+ };
+
+ var hover;
+
+
+ function siblingAndChildVertices(ids, graph, extent$$1) {
+ var vertices = {};
+
+ function addChildVertices(entity) {
+ if (!context.features().isHiddenFeature(entity, graph, entity.geometry(graph))) {
+ var i;
+ if (entity.type === 'way') {
+ for (i = 0; i < entity.nodes.length; i++) {
+ addChildVertices(graph.entity(entity.nodes[i]));
+ }
+ } else if (entity.type === 'relation') {
+ for (i = 0; i < entity.members.length; i++) {
+ var member = context.hasEntity(entity.members[i].id);
+ if (member) {
+ addChildVertices(member);
+ }
+ }
+ } else if (entity.intersects(extent$$1, graph)) {
+ vertices[entity.id] = entity;
+ }
+ }
+ }
+
+ ids.forEach(function(id) {
+ var entity = context.hasEntity(id);
+ if (entity && entity.type === 'node') {
+ vertices[entity.id] = entity;
+ context.graph().parentWays(entity).forEach(function(entity) {
+ addChildVertices(entity);
+ });
+ } else if (entity) {
+ addChildVertices(entity);
+ }
+ });
+
+ return vertices;
+ }
+
+
+ function draw(selection$$1, vertices, klass, graph, zoom$$1, siblings$$1) {
+
+ function icon(entity) {
+ if (entity.id in icons) return icons[entity.id];
+ icons[entity.id] =
+ entity.hasInterestingTags() &&
+ context.presets().match(entity, graph).icon;
+ return icons[entity.id];
+ }
+
+ function setClass(klass) {
+ return function(entity) {
+ this.setAttribute('class', 'node vertex ' + klass + ' ' + entity.id);
+ };
+ }
+
+ function setAttributes(selection$$1) {
+ ['shadow','stroke','fill'].forEach(function(klass) {
+ var rads = radiuses[klass];
+ selection$$1.selectAll('.' + klass)
+ .each(function(entity) {
+ var i = z && icon(entity),
+ c = i ? 0.5 : 0,
+ r = rads[i ? 3 : z];
+
+ // slightly increase the size of unconnected endpoints #3775
+ if (entity.isEndpoint(graph) && !entity.isConnected(graph)) {
+ r += 1.5;
+ }
+
+ this.setAttribute('cx', c);
+ this.setAttribute('cy', -c);
+ this.setAttribute('r', r);
+ if (i && klass === 'fill') {
+ this.setAttribute('visibility', 'hidden');
+ } else {
+ this.removeAttribute('visibility');
+ }
+ });
+ });
+
+ selection$$1.selectAll('use')
+ .each(function() {
+ if (z) {
+ this.removeAttribute('visibility');
+ } else {
+ this.setAttribute('visibility', 'hidden');
+ }
+ });
+ }
+
+
+ siblings$$1 = siblings$$1 || {};
+
+ var icons = {},
+ z = (zoom$$1 < 17 ? 0 : zoom$$1 < 18 ? 1 : 2);
+
+ var groups = selection$$1
+ .data(vertices, osmEntity$$1.key);
+
+ groups.exit()
+ .remove();
+
+ var enter = groups.enter()
+ .append('g')
+ .attr('class', function(d) { return 'node vertex ' + klass + ' ' + d.id; });
+
+ enter.append('circle')
+ .each(setClass('shadow'));
+
+ enter.append('circle')
+ .each(setClass('stroke'));
+
+ // Vertices with icons get a `use`.
+ enter.filter(function(d) { return icon(d); })
+ .append('use')
+ .attr('transform', 'translate(-5, -6)')
+ .attr('xlink:href', function(d) {
+ var picon = icon(d),
+ isMaki = dataFeatureIcons.indexOf(picon) !== -1;
+ return '#' + picon + (isMaki ? '-11' : '');
+ })
+ .attr('width', '11px')
+ .attr('height', '11px')
+ .each(setClass('icon'));
+
+ // Vertices with tags get a fill.
+ enter.filter(function(d) { return d.hasInterestingTags(); })
+ .append('circle')
+ .each(setClass('fill'));
+
+ groups
+ .merge(enter)
+ .attr('transform', svgPointTransform(projection$$1))
+ .classed('sibling', function(entity) { return entity.id in siblings$$1; })
+ .classed('shared', function(entity) { return graph.isShared(entity); })
+ .classed('endpoint', function(entity) { return entity.isEndpoint(graph); })
+ .call(setAttributes);
+ }
+
+
+ function drawVertices(selection$$1, graph, entities, filter, extent$$1, zoom$$1) {
+ var siblings$$1 = siblingAndChildVertices(context.selectedIDs(), graph, extent$$1),
+ wireframe = context.surface().classed('fill-wireframe'),
+ vertices = [];
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i],
+ geometry = entity.geometry(graph);
+
+ if (wireframe && geometry === 'point') {
+ vertices.push(entity);
+ continue;
+ }
+
+ if (geometry !== 'vertex')
+ continue;
+
+ if (entity.id in siblings$$1 ||
+ entity.hasInterestingTags() ||
+ entity.isEndpoint(graph) ||
+ entity.isConnected(graph)) {
+ vertices.push(entity);
+ }
+ }
+
+ var layer = selection$$1.selectAll('.layer-hit');
+ layer.selectAll('g.vertex.vertex-persistent')
+ .filter(filter)
+ .call(draw, vertices, 'vertex-persistent', graph, zoom$$1, siblings$$1);
+
+ drawHover(selection$$1, graph, extent$$1, zoom$$1);
+ }
+
+
+ function drawHover(selection$$1, graph, extent$$1, zoom$$1) {
+ var hovered = hover ? siblingAndChildVertices([hover.id], graph, extent$$1) : {};
+ var layer = selection$$1.selectAll('.layer-hit');
+
+ layer.selectAll('g.vertex.vertex-hover')
+ .call(draw, values(hovered), 'vertex-hover', graph, zoom$$1);
+ }
+
+
+ drawVertices.drawHover = function(selection$$1, graph, target, extent$$1, zoom$$1) {
+ if (target === hover) return;
+ hover = target;
+ drawHover(selection$$1, graph, extent$$1, zoom$$1);
+ };
+
+ return drawVertices;
+}
+
+function behaviorEdit(context) {
+
+ function edit() {
+ context.map()
+ .minzoom(context.minEditableZoom());
+ }
+
+
+ edit.off = function() {
+ context.map()
+ .minzoom(0);
+ };
+
+
+ return edit;
+}
+
+/*
+ The hover behavior adds the `.hover` class on mouseover to all elements to which
+ the identical datum is bound, and removes it on mouseout.
+
+ The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
+ representation may consist of several elements scattered throughout the DOM hierarchy.
+ Only one of these elements can have the :hover pseudo-class, but all of them will
+ have the .hover class.
+ */
+function behaviorHover(context) {
+ var dispatch$$1 = dispatch('hover'),
+ _selection = select(null),
+ newId = null,
+ buttonDown,
+ altDisables,
+ target;
+
+
+ function keydown() {
+ if (altDisables && event.keyCode === d3keybinding.modifierCodes.alt) {
+ _selection.selectAll('.hover')
+ .classed('hover-suppressed', true)
+ .classed('hover', false);
+
+ _selection
+ .classed('hover-disabled', true);
+
+ dispatch$$1.call('hover', this, null);
+ }
+ }
+
+
+ function keyup() {
+ if (altDisables && event.keyCode === d3keybinding.modifierCodes.alt) {
+ _selection.selectAll('.hover-suppressed')
+ .classed('hover-suppressed', false)
+ .classed('hover', true);
+
+ _selection
+ .classed('hover-disabled', false);
+
+ dispatch$$1.call('hover', this, target ? target.id : null);
+ }
+ }
+
+
+ var hover = function(selection$$1) {
+ _selection = selection$$1;
+ newId = null;
+
+ _selection
+ .on('mouseover.hover', mouseover)
+ .on('mouseout.hover', mouseout)
+ .on('mousedown.hover', mousedown);
+
+ select(window)
+ .on('keydown.hover', keydown)
+ .on('keyup.hover', keyup);
+
+
+ function mouseover() {
+ if (buttonDown) return;
+ var target = event.target;
+ enter(target ? target.__data__ : null);
+ }
+
+
+ function mouseout() {
+ if (buttonDown) return;
+ var target = event.relatedTarget;
+ enter(target ? target.__data__ : null);
+ }
+
+
+ function mousedown() {
+ buttonDown = true;
+ select(window)
+ .on('mouseup.hover', mouseup, true);
+ }
+
+
+ function mouseup() {
+ buttonDown = false;
+ select(window)
+ .on('mouseup.hover', null, true);
+ }
+
+
+ function enter(d) {
+ if (d === target) return;
+ target = d;
+
+ _selection.selectAll('.hover')
+ .classed('hover', false);
+ _selection.selectAll('.hover-suppressed')
+ .classed('hover-suppressed', false);
+
+ if (target instanceof osmEntity$$1 && target.id !== newId) {
+
+ // If drawing a way, don't hover on a node that was just placed. #3974
+ var mode = context.mode() && context.mode().id;
+ if ((mode === 'draw-line' || mode === 'draw-area') && !newId && target.type === 'node') {
+ newId = target.id;
+ return;
+ }
+
+ var selector$$1 = '.' + target.id;
+
+ if (target.type === 'relation') {
+ target.members.forEach(function(member) {
+ selector$$1 += ', .' + member.id;
+ });
+ }
+
+ var suppressed = altDisables && event && event.altKey;
+
+ _selection.selectAll(selector$$1)
+ .classed(suppressed ? 'hover-suppressed' : 'hover', true);
+
+ dispatch$$1.call('hover', this, !suppressed && target.id);
+
+ } else {
+ dispatch$$1.call('hover', this, null);
+ }
+ }
+
+ };
+
+
+ hover.off = function(selection$$1) {
+ selection$$1.selectAll('.hover')
+ .classed('hover', false);
+ selection$$1.selectAll('.hover-suppressed')
+ .classed('hover-suppressed', false);
+ selection$$1
+ .classed('hover-disabled', false);
+
+
+ selection$$1
+ .on('mouseover.hover', null)
+ .on('mouseout.hover', null)
+ .on('mousedown.hover', null);
+
+ select(window)
+ .on('keydown.hover', null)
+ .on('keyup.hover', null);
+ };
+
+
+ hover.altDisables = function(_) {
+ if (!arguments.length) return altDisables;
+ altDisables = _;
+ return hover;
+ };
+
+
+ return utilRebind(hover, dispatch$$1, 'on');
+}
+
+function behaviorTail() {
+ var text$$1,
+ container,
+ xmargin = 25,
+ tooltipSize = [0, 0],
+ selectionSize = [0, 0];
+
+
+ function tail(selection$$1) {
+ if (!text$$1) return;
+
+ select(window)
+ .on('resize.tail', function() { selectionSize = utilGetDimensions(selection$$1); });
+
+ container = select(document.body)
+ .append('div')
+ .style('display', 'none')
+ .attr('class', 'tail tooltip-inner');
+
+ container.append('div')
+ .text(text$$1);
+
+ selection$$1
+ .on('mousemove.tail', mousemove)
+ .on('mouseenter.tail', mouseenter)
+ .on('mouseleave.tail', mouseleave);
+
+ container
+ .on('mousemove.tail', mousemove);
+
+ tooltipSize = utilGetDimensions(container);
+ selectionSize = utilGetDimensions(selection$$1);
+
+
+ function show() {
+ container.style('display', 'block');
+ tooltipSize = utilGetDimensions(container);
+ }
+
+
+ function mousemove() {
+ if (container.style('display') === 'none') show();
+ var xoffset = ((event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
+ -tooltipSize[0] - xmargin : xmargin;
+ container.classed('left', xoffset > 0);
+ utilSetTransform(container, event.clientX + xoffset, event.clientY);
+ }
+
+
+ function mouseleave() {
+ if (event.relatedTarget !== container.node()) {
+ container.style('display', 'none');
+ }
+ }
+
+
+ function mouseenter() {
+ if (event.relatedTarget !== container.node()) {
+ show();
+ }
+ }
+ }
+
+
+ tail.off = function(selection$$1) {
+ if (!text$$1) return;
+
+ container
+ .on('mousemove.tail', null)
+ .remove();
+
+ selection$$1
+ .on('mousemove.tail', null)
+ .on('mouseenter.tail', null)
+ .on('mouseleave.tail', null);
+
+ select(window)
+ .on('resize.tail', null);
+ };
+
+
+ tail.text = function(_) {
+ if (!arguments.length) return text$$1;
+ text$$1 = _;
+ return tail;
+ };
+
+
+ return tail;
+}
+
+var usedTails = {};
+var disableSpace = false;
+var lastSpace = null;
+
+
+function behaviorDraw(context) {
+ var dispatch$$1 = dispatch('move', 'click', 'clickWay',
+ 'clickNode', 'undo', 'cancel', 'finish'),
+ keybinding = d3keybinding('draw'),
+ hover = behaviorHover(context)
+ .altDisables(true)
+ .on('hover', context.ui().sidebar.hover),
+ tail = behaviorTail(),
+ edit = behaviorEdit(context),
+ closeTolerance = 4,
+ tolerance = 12,
+ mouseLeave = false,
+ lastMouse = null;
+
+
+ function datum() {
+ if (event.altKey) return {};
+
+ if (event.type === 'keydown') {
+ return (lastMouse && lastMouse.target.__data__) || {};
+ } else {
+ return event.target.__data__ || {};
+ }
+ }
+
+
+ function mousedown() {
+
+ function point() {
+ var p = context.container().node();
+ return touchId !== null ? touches(p).filter(function(p) {
+ return p.identifier === touchId;
+ })[0] : mouse(p);
+ }
+
+ var element = select(this),
+ touchId = event.touches ? event.changedTouches[0].identifier : null,
+ t1 = +new Date(),
+ p1 = point();
+
+ element.on('mousemove.draw', null);
+
+ select(window).on('mouseup.draw', function() {
+ var t2 = +new Date(),
+ p2 = point(),
+ dist = geoEuclideanDistance(p1, p2);
+
+ element.on('mousemove.draw', mousemove);
+ select(window).on('mouseup.draw', null);
+
+ if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
+ // Prevent a quick second click
+ select(window).on('click.draw-block', function() {
+ event.stopPropagation();
+ }, true);
+
+ context.map().dblclickEnable(false);
+
+ window.setTimeout(function() {
+ context.map().dblclickEnable(true);
+ select(window).on('click.draw-block', null);
+ }, 500);
+
+ click();
+ }
+ }, true);
+ }
+
+
+ function mousemove() {
+ lastMouse = event;
+ dispatch$$1.call('move', this, datum());
+ }
+
+
+ function mouseenter() {
+ mouseLeave = false;
+ }
+
+
+ function mouseleave() {
+ mouseLeave = true;
+ }
+
+
+ function click() {
+ var d = datum();
+ if (d.type === 'way') {
+ var dims = context.map().dimensions(),
+ mouse$$1 = context.mouse(),
+ pad = 5,
+ trySnap = mouse$$1[0] > pad && mouse$$1[0] < dims[0] - pad &&
+ mouse$$1[1] > pad && mouse$$1[1] < dims[1] - pad;
+
+ if (trySnap) {
+ var choice = geoChooseEdge(context.childNodes(d), context.mouse(), context.projection),
+ edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
+ dispatch$$1.call('clickWay', this, choice.loc, edge);
+ } else {
+ dispatch$$1.call('click', this, context.map().mouseCoordinates());
+ }
+
+ } else if (d.type === 'node') {
+ dispatch$$1.call('clickNode', this, d);
+
+ } else {
+ dispatch$$1.call('click', this, context.map().mouseCoordinates());
+ }
+ }
+
+
+ function space() {
+ event.preventDefault();
+ event.stopPropagation();
+
+ var currSpace = context.mouse();
+ if (disableSpace && lastSpace) {
+ var dist = geoEuclideanDistance(lastSpace, currSpace);
+ if (dist > tolerance) {
+ disableSpace = false;
+ }
+ }
+
+ if (disableSpace || mouseLeave || !lastMouse) return;
+
+ // user must move mouse or release space bar to allow another click
+ lastSpace = currSpace;
+ disableSpace = true;
+
+ select(window).on('keyup.space-block', function() {
+ event.preventDefault();
+ event.stopPropagation();
+ disableSpace = false;
+ select(window).on('keyup.space-block', null);
+ });
+
+ click();
+ }
+
+
+ function backspace() {
+ event.preventDefault();
+ dispatch$$1.call('undo');
+ }
+
+
+ function del() {
+ event.preventDefault();
+ dispatch$$1.call('cancel');
+ }
+
+
+ function ret() {
+ event.preventDefault();
+ dispatch$$1.call('finish');
+ }
+
+
+ function draw(selection$$1) {
+ context.install(hover);
+ context.install(edit);
+
+ if (!context.inIntro() && !usedTails[tail.text()]) {
+ context.install(tail);
+ }
+
+ keybinding
+ .on('⌫', backspace)
+ .on('⌦', del)
+ .on('⎋', ret)
+ .on('↩', ret)
+ .on('space', space)
+ .on('⌥space', space);
+
+ selection$$1
+ .on('mouseenter.draw', mouseenter)
+ .on('mouseleave.draw', mouseleave)
+ .on('mousedown.draw', mousedown)
+ .on('mousemove.draw', mousemove);
+
+ select(document)
+ .call(keybinding);
+
+ return draw;
+ }
+
+
+ draw.off = function(selection$$1) {
+ context.ui().sidebar.hover.cancel();
+ context.uninstall(hover);
+ context.uninstall(edit);
+
+ if (!context.inIntro() && !usedTails[tail.text()]) {
+ context.uninstall(tail);
+ usedTails[tail.text()] = true;
+ }
+
+ selection$$1
+ .on('mouseenter.draw', null)
+ .on('mouseleave.draw', null)
+ .on('mousedown.draw', null)
+ .on('mousemove.draw', null);
+
+ select(window)
+ .on('mouseup.draw', null);
+ // note: keyup.space-block, click.draw-block should remain
+
+ select(document)
+ .call(keybinding.off);
+ };
+
+
+ draw.tail = function(_) {
+ tail.text(_);
+ return draw;
+ };
+
+
+ return utilRebind(draw, dispatch$$1, 'on');
+}
+
+function behaviorAddWay(context) {
+ var dispatch$$1 = dispatch('start', 'startFromWay', 'startFromNode'),
+ draw = behaviorDraw(context);
+
+ var addWay = function(surface) {
+ draw.on('click', function() { dispatch$$1.apply('start', this, arguments); })
+ .on('clickWay', function() { dispatch$$1.apply('startFromWay', this, arguments); })
+ .on('clickNode', function() { dispatch$$1.apply('startFromNode', this, arguments); })
+ .on('cancel', addWay.cancel)
+ .on('finish', addWay.cancel);
+
+ context.map()
+ .dblclickEnable(false);
+
+ surface.call(draw);
+ };
+
+
+ addWay.off = function(surface) {
+ surface.call(draw.off);
+ };
+
+
+ addWay.cancel = function() {
+ window.setTimeout(function() {
+ context.map().dblclickEnable(true);
+ }, 1000);
+
+ context.enter(modeBrowse(context));
+ };
+
+
+ addWay.tail = function(text$$1) {
+ draw.tail(text$$1);
+ return addWay;
+ };
+
+
+ return utilRebind(addWay, dispatch$$1, 'on');
+}
+
+function behaviorBreathe() {
+ var duration = 800,
+ steps = 4,
+ selector$$1 = '.selected.shadow, .selected .shadow',
+ selected = select(null),
+ classed = '',
+ params = {},
+ done = false,
+ timer$$1;
+
+
+ function ratchetyInterpolator(a, b, steps, units) {
+ a = parseFloat(a);
+ b = parseFloat(b);
+ var sample = quantize$1()
+ .domain([0, 1])
+ .range(quantize(reinterpolate(a, b), steps));
+
+ return function(t) {
+ return String(sample(t)) + (units || '');
+ };
+ }
+
+
+ function reset(selection$$1) {
+ selection$$1
+ .style('stroke-opacity', null)
+ .style('stroke-width', null)
+ .style('fill-opacity', null)
+ .style('r', null);
+ }
+
+
+ function setAnimationParams(transition$$1, fromTo) {
+ var toFrom = (fromTo === 'from' ? 'to' : 'from');
+
+ transition$$1
+ .styleTween('stroke-opacity', function(d) {
+ return ratchetyInterpolator(
+ params[d.id][toFrom].opacity,
+ params[d.id][fromTo].opacity,
+ steps
+ );
+ })
+ .styleTween('stroke-width', function(d) {
+ return ratchetyInterpolator(
+ params[d.id][toFrom].width,
+ params[d.id][fromTo].width,
+ steps,
+ 'px'
+ );
+ })
+ .styleTween('fill-opacity', function(d) {
+ return ratchetyInterpolator(
+ params[d.id][toFrom].opacity,
+ params[d.id][fromTo].opacity,
+ steps
+ );
+ })
+ .styleTween('r', function(d) {
+ return ratchetyInterpolator(
+ params[d.id][toFrom].width,
+ params[d.id][fromTo].width,
+ steps,
+ 'px'
+ );
+ });
+ }
+
+
+ function calcAnimationParams(selection$$1) {
+ selection$$1
+ .call(reset)
+ .each(function(d) {
+ var s = select(this),
+ tag = s.node().tagName,
+ p = {'from': {}, 'to': {}},
+ opacity, width;
+
+ // determine base opacity and width
+ if (tag === 'circle') {
+ opacity = parseFloat(s.style('fill-opacity') || 0.5);
+ width = parseFloat(s.style('r') || 15.5);
+ } else {
+ opacity = parseFloat(s.style('stroke-opacity') || 0.7);
+ width = parseFloat(s.style('stroke-width') || 10);
+ }
+
+ // calculate from/to interpolation params..
+ p.tag = tag;
+ p.from.opacity = opacity * 0.6;
+ p.to.opacity = opacity * 1.25;
+ p.from.width = width * 0.7;
+ p.to.width = width * (tag === 'circle' ? 1.5 : 1);
+ params[d.id] = p;
+ });
+ }
+
+
+ function run(surface, fromTo) {
+ var toFrom = (fromTo === 'from' ? 'to' : 'from'),
+ currSelected = surface.selectAll(selector$$1),
+ currClassed = surface.attr('class');
+
+ if (done || currSelected.empty()) {
+ selected.call(reset);
+ return;
+ }
+
+ if (!lodash.isEqual(currSelected.data(), selected.data()) || currClassed !== classed) {
+ selected.call(reset);
+ classed = currClassed;
+ selected = currSelected.call(calcAnimationParams);
+ }
+
+ selected
+ .transition()
+ .duration(duration)
+ .call(setAnimationParams, fromTo)
+ .on('end', function() {
+ surface.call(run, toFrom);
+ });
+ }
+
+
+ var breathe = function(surface) {
+ done = false;
+ timer$$1 = timer(function() {
+ // wait for elements to actually become selected
+ if (surface.selectAll(selector$$1).empty()) {
+ return false;
+ }
+
+ surface.call(run, 'from');
+ timer$$1.stop();
+ return true;
+ }, 20);
+ };
+
+
+ breathe.off = function() {
+ done = true;
+ if (timer$$1) {
+ timer$$1.stop();
+ }
+ selected
+ .interrupt()
+ .call(reset);
+ };
+
+
+ return breathe;
+}
+
+function behaviorCopy(context) {
+ var keybinding = d3keybinding('copy');
+
+
+ function groupEntities(ids, graph) {
+ var entities = ids.map(function (id) { return graph.entity(id); });
+ return lodash.extend({relation: [], way: [], node: []},
+ lodash.groupBy(entities, function(entity) { return entity.type; }));
+ }
+
+
+ function getDescendants(id, graph, descendants) {
+ var entity = graph.entity(id),
+ i, children;
+
+ descendants = descendants || {};
+
+ if (entity.type === 'relation') {
+ children = lodash.map(entity.members, 'id');
+ } else if (entity.type === 'way') {
+ children = entity.nodes;
+ } else {
+ children = [];
+ }
+
+ for (i = 0; i < children.length; i++) {
+ if (!descendants[children[i]]) {
+ descendants[children[i]] = true;
+ descendants = getDescendants(children[i], graph, descendants);
+ }
+ }
+
+ return descendants;
+ }
+
+
+ function doCopy() {
+ event.preventDefault();
+
+ var graph = context.graph(),
+ selected = groupEntities(context.selectedIDs(), graph),
+ canCopy = [],
+ skip = {},
+ i, entity;
+
+ for (i = 0; i < selected.relation.length; i++) {
+ entity = selected.relation[i];
+ if (!skip[entity.id] && entity.isComplete(graph)) {
+ canCopy.push(entity.id);
+ skip = getDescendants(entity.id, graph, skip);
+ }
+ }
+ for (i = 0; i < selected.way.length; i++) {
+ entity = selected.way[i];
+ if (!skip[entity.id]) {
+ canCopy.push(entity.id);
+ skip = getDescendants(entity.id, graph, skip);
+ }
+ }
+ for (i = 0; i < selected.node.length; i++) {
+ entity = selected.node[i];
+ if (!skip[entity.id]) {
+ canCopy.push(entity.id);
+ }
+ }
+
+ context.copyIDs(canCopy);
+ }
+
+
+ function copy() {
+ keybinding.on(uiCmd('⌘C'), doCopy);
+ select(document).call(keybinding);
+ return copy;
+ }
+
+
+ copy.off = function() {
+ select(document).call(keybinding.off);
+ };
+
+
+ return copy;
+}
+
+/*
+ `behaviorDrag` is like `d3.behavior.drag`, with the following differences:
+
+ * The `origin` function is expected to return an [x, y] tuple rather than an
+ {x, y} object.
+ * The events are `start`, `move`, and `end`.
+ (https://github.com/mbostock/d3/issues/563)
+ * The `start` event is not dispatched until the first cursor movement occurs.
+ (https://github.com/mbostock/d3/pull/368)
+ * The `move` event has a `point` and `delta` [x, y] tuple properties rather
+ than `x`, `y`, `dx`, and `dy` properties.
+ * The `end` event is not dispatched if no movement occurs.
+ * An `off` function is available that unbinds the drag's internal event handlers.
+ */
+
+function behaviorDrag() {
+ var event$$1 = dispatch('start', 'move', 'end'),
+ origin = null,
+ selector$$1 = '',
+ filter = null,
+ event_, target, surface;
+
+
+ var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect'),
+ d3_event_userSelectSuppress = function() {
+ var selection$$1 = selection(),
+ select$$1 = selection$$1.style(d3_event_userSelectProperty);
+ selection$$1.style(d3_event_userSelectProperty, 'none');
+ return function() {
+ selection$$1.style(d3_event_userSelectProperty, select$$1);
+ };
+ };
+
+
+ function d3_eventCancel() {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+
+
+ function eventOf(thiz, argumentz) {
+ return function(e1) {
+ e1.target = drag$$1;
+ customEvent(e1, event$$1.apply, event$$1, [e1.type, thiz, argumentz]);
+ };
+ }
+
+
+ function dragstart() {
+ target = this;
+ event_ = eventOf(target, arguments);
+
+ var eventTarget = event.target,
+ touchId = event.touches ? event.changedTouches[0].identifier : null,
+ offset,
+ origin_ = point(),
+ started = false,
+ selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
+
+ select(window)
+ .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove)
+ .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true);
+
+ if (origin) {
+ offset = origin.apply(target, arguments);
+ offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
+ } else {
+ offset = [0, 0];
+ }
+
+ if (touchId === null) {
+ event.stopPropagation();
+ }
+
+
+ function point() {
+ var p = surface || target.parentNode;
+ return touchId !== null ? touches(p).filter(function(p) {
+ return p.identifier === touchId;
+ })[0] : mouse(p);
+ }
+
+
+ function dragmove() {
+ var p = point(),
+ dx = p[0] - origin_[0],
+ dy = p[1] - origin_[1];
+
+ if (dx === 0 && dy === 0)
+ return;
+
+ if (!started) {
+ started = true;
+ event_({ type: 'start' });
+ }
+
+ origin_ = p;
+ d3_eventCancel();
+
+ event_({
+ type: 'move',
+ point: [p[0] + offset[0], p[1] + offset[1]],
+ delta: [dx, dy]
+ });
+ }
+
+
+ function dragend() {
+ if (started) {
+ event_({ type: 'end' });
+
+ d3_eventCancel();
+ if (event.target === eventTarget) {
+ select(window)
+ .on('click.drag', click, true);
+ }
+ }
+
+ select(window)
+ .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
+ .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
+
+ selectEnable();
+ }
+
+
+ function click() {
+ d3_eventCancel();
+ select(window)
+ .on('click.drag', null);
+ }
+ }
+
+
+ function drag$$1(selection$$1) {
+ var matchesSelector = utilPrefixDOMProperty('matchesSelector'),
+ delegate = dragstart;
+
+ if (selector$$1) {
+ delegate = function() {
+ var root = this,
+ target = event.target;
+ for (; target && target !== root; target = target.parentNode) {
+ if (target[matchesSelector](selector$$1) &&
+ (!filter || filter(target.__data__))) {
+ return dragstart.call(target, target.__data__);
+ }
+ }
+ };
+ }
+
+ selection$$1
+ .on('mousedown.drag' + selector$$1, delegate)
+ .on('touchstart.drag' + selector$$1, delegate);
+ }
+
+
+ drag$$1.off = function(selection$$1) {
+ selection$$1
+ .on('mousedown.drag' + selector$$1, null)
+ .on('touchstart.drag' + selector$$1, null);
+ };
+
+
+ drag$$1.selector = function(_) {
+ if (!arguments.length) return selector$$1;
+ selector$$1 = _;
+ return drag$$1;
+ };
+
+
+ drag$$1.filter = function(_) {
+ if (!arguments.length) return origin;
+ filter = _;
+ return drag$$1;
+ };
+
+
+ drag$$1.origin = function (_) {
+ if (!arguments.length) return origin;
+ origin = _;
+ return drag$$1;
+ };
+
+
+ drag$$1.cancel = function() {
+ select(window)
+ .on('mousemove.drag', null)
+ .on('mouseup.drag', null);
+ return drag$$1;
+ };
+
+
+ drag$$1.target = function() {
+ if (!arguments.length) return target;
+ target = arguments[0];
+ event_ = eventOf(target, Array.prototype.slice.call(arguments, 1));
+ return drag$$1;
+ };
+
+
+ drag$$1.surface = function() {
+ if (!arguments.length) return surface;
+ surface = arguments[0];
+ return drag$$1;
+ };
+
+
+ return utilRebind(drag$$1, event$$1, 'on');
+}
+
+function behaviorDrawWay(context, wayId, index, mode, startGraph) {
+
+ var origWay = context.entity(wayId),
+ isArea = context.geometry(wayId) === 'area',
+ tempEdits = 0,
+ annotation = t((origWay.isDegenerate() ?
+ 'operations.start.annotation.' :
+ 'operations.continue.annotation.') + context.geometry(wayId)),
+ draw = behaviorDraw(context),
+ startIndex,
+ start,
+ end,
+ segment;
+
+
+ // initialize the temporary drawing entities
+ if (!isArea) {
+ startIndex = typeof index === 'undefined' ? origWay.nodes.length - 1 : 0;
+ start = osmNode({ id: 'nStart', loc: context.entity(origWay.nodes[startIndex]).loc });
+ end = osmNode({ id: 'nEnd', loc: context.map().mouseCoordinates() });
+ segment = osmWay({ id: 'wTemp',
+ nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id],
+ tags: lodash.clone(origWay.tags)
+ });
+ } else {
+ end = osmNode({ loc: context.map().mouseCoordinates() });
+ }
+
+ // Push an annotated state for undo to return back to.
+ // We must make sure to remove this edit later.
+ context.perform(actionNoop(), annotation);
+ tempEdits++;
+
+ // Add the temporary drawing entities to the graph.
+ // We must make sure to remove this edit later.
+ context.perform(AddDrawEntities());
+ tempEdits++;
+
+
+ function move(datum) {
+ var loc;
+
+ if (datum.type === 'node' && datum.id !== end.id) {
+ loc = datum.loc;
+
+ } else if (datum.type === 'way') {
+ var dims = context.map().dimensions(),
+ mouse = context.mouse(),
+ pad = 5,
+ trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
+ mouse[1] > pad && mouse[1] < dims[1] - pad;
+
+ if (trySnap) {
+ loc = geoChooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc;
+ }
+ }
+
+ if (!loc) {
+ loc = context.map().mouseCoordinates();
+ }
+
+ context.replace(actionMoveNode(end.id, loc));
+ end = context.entity(end.id);
+ }
+
+
+ function undone() {
+ // Undo popped the history back to the initial annotated no-op edit.
+ // Remove initial no-op edit and whatever edit happened immediately before it.
+ context.pop(2);
+ tempEdits = 0;
+
+ if (context.hasEntity(wayId)) {
+ context.enter(mode);
+ } else {
+ context.enter(modeBrowse(context));
+ }
+ }
+
+
+ function setActiveElements() {
+ var active = isArea ? [wayId, end.id] : [segment.id, start.id, end.id];
+ context.surface().selectAll(utilEntitySelector(active))
+ .classed('active', true);
+ }
+
+
+ var drawWay = function(surface) {
+ draw.on('move', move)
+ .on('click', drawWay.add)
+ .on('clickWay', drawWay.addWay)
+ .on('clickNode', drawWay.addNode)
+ .on('undo', context.undo)
+ .on('cancel', drawWay.cancel)
+ .on('finish', drawWay.finish);
+
+ context.map()
+ .dblclickEnable(false)
+ .on('drawn.draw', setActiveElements);
+
+ setActiveElements();
+
+ surface.call(draw);
+
+ context.history()
+ .on('undone.draw', undone);
+ };
+
+
+ drawWay.off = function(surface) {
+ // Drawing was interrupted unexpectedly.
+ // This can happen if the user changes modes,
+ // clicks geolocate button, a hashchange event occurs, etc.
+ if (tempEdits) {
+ context.pop(tempEdits);
+ while (context.graph() !== startGraph) {
+ context.pop();
+ }
+ }
+
+ context.map()
+ .on('drawn.draw', null);
+
+ surface.call(draw.off)
+ .selectAll('.active')
+ .classed('active', false);
+
+ context.history()
+ .on('undone.draw', null);
+ };
+
+
+ function AddDrawEntities() {
+ return function(graph) {
+ if (isArea) {
+ // For area drawing, there is no need for a temporary node.
+ // `end` gets inserted into the way as the penultimate node.
+ return graph
+ .replace(end)
+ .replace(origWay.addNode(end.id));
+ } else {
+ // For line drawing, add a temporary start, end, and segment to the graph.
+ // This allows us to class the new segment as `active`, but still
+ // connect it back to parts of the way that have already been drawn.
+ return graph
+ .replace(start)
+ .replace(end)
+ .replace(segment);
+ }
+ };
+ }
+
+
+ function ReplaceDrawEntities(newNode) {
+ return function(graph) {
+ if (isArea) {
+ // For area drawing, we didn't create a temporary node.
+ // `newNode` gets inserted into the _original_ way as the penultimate node.
+ return graph
+ .replace(origWay.addNode(newNode.id))
+ .remove(end);
+ } else {
+ // For line drawing, add the `newNode` to the way at specified index,
+ // and remove the temporary start, end, and segment.
+ return graph
+ .replace(origWay.addNode(newNode.id, index))
+ .remove(end)
+ .remove(segment)
+ .remove(start);
+ }
+ };
+ }
+
+
+ // Accept the current position of the temporary node and continue drawing.
+ drawWay.add = function(loc) {
+ // prevent duplicate nodes
+ var last = context.hasEntity(origWay.nodes[origWay.nodes.length - (isArea ? 2 : 1)]);
+ if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return;
+
+ context.pop(tempEdits);
+
+ if (isArea) {
+ context.perform(
+ AddDrawEntities(),
+ annotation
+ );
+ } else {
+ var newNode = osmNode({loc: loc});
+ context.perform(
+ actionAddEntity(newNode),
+ ReplaceDrawEntities(newNode),
+ annotation
+ );
+ }
+
+ tempEdits = 0;
+ context.enter(mode);
+ };
+
+
+ // Connect the way to an existing way.
+ drawWay.addWay = function(loc, edge) {
+ if (isArea) {
+ context.pop(tempEdits);
+
+ context.perform(
+ AddDrawEntities(),
+ actionAddMidpoint({ loc: loc, edge: edge}, end),
+ annotation
+ );
+ } else {
+ var previousEdge = startIndex ?
+ [origWay.nodes[startIndex], origWay.nodes[startIndex - 1]] :
+ [origWay.nodes[0], origWay.nodes[1]];
+
+ // Avoid creating duplicate segments
+ if (geoEdgeEqual(edge, previousEdge))
+ return;
+
+ context.pop(tempEdits);
+
+ var newNode = osmNode({ loc: loc });
+ context.perform(
+ actionAddMidpoint({ loc: loc, edge: edge}, newNode),
+ ReplaceDrawEntities(newNode),
+ annotation
+ );
+ }
+
+ tempEdits = 0;
+ context.enter(mode);
+ };
+
+
+ // Connect the way to an existing node and continue drawing.
+ drawWay.addNode = function(node) {
+ // Avoid creating duplicate segments
+ if (origWay.areAdjacent(node.id, origWay.nodes[origWay.nodes.length - 1])) return;
+
+ // Clicks should not occur on the drawing node, however a space keypress can
+ // sometimes grab that node's datum (before it gets classed as `active`?) #4016
+ if (node.id === end.id) {
+ drawWay.add(node.loc);
+ return;
+ }
+
+ context.pop(tempEdits);
+
+ context.perform(
+ ReplaceDrawEntities(node),
+ annotation
+ );
+
+ tempEdits = 0;
+ context.enter(mode);
+ };
+
+
+ // Finish the draw operation, removing the temporary edits.
+ // If the way has enough nodes to be valid, it's selected.
+ // Otherwise, delete everything and return to browse mode.
+ drawWay.finish = function() {
+ context.pop(tempEdits);
+ tempEdits = 0;
+
+ var way = context.hasEntity(wayId);
+ if (!way || way.isDegenerate()) {
+ drawWay.cancel();
+ return;
+ }
+
+ window.setTimeout(function() {
+ context.map().dblclickEnable(true);
+ }, 1000);
+
+ context.enter(modeSelect(context, [wayId]).newFeature(true));
+ };
+
+
+ // Cancel the draw operation, delete everything, and return to browse mode.
+ drawWay.cancel = function() {
+ context.pop(tempEdits);
+ tempEdits = 0;
+
+ while (context.graph() !== startGraph) {
+ context.pop();
+ }
+
+ window.setTimeout(function() {
+ context.map().dblclickEnable(true);
+ }, 1000);
+
+ context.enter(modeBrowse(context));
+ };
+
+
+ drawWay.tail = function(text) {
+ draw.tail(text);
+ return drawWay;
+ };
+
+
+ return drawWay;
+}
+
+function behaviorHash(context) {
+ var s0 = null, // cached window.location.hash
+ lat = 90 - 1e-8; // allowable latitude range
+
+
+ var parser = function(map, s) {
+ var q = utilStringQs(s);
+ var args = (q.map || '').split('/').map(Number);
+
+ if (args.length < 3 || args.some(isNaN)) {
+ return true; // replace bogus hash
+
+ } else if (s !== formatter(map).slice(1)) { // hash has changed
+ var mode = context.mode(),
+ dist = geoSphericalDistance(map.center(), [args[2], args[1]]),
+ maxdist = 500;
+
+ // Don't allow the hash location to change too much while drawing
+ // This can happen if the user accidently hit the back button. #3996
+ if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
+ context.enter(modeBrowse(context));
+ }
+
+ map.centerZoom([args[2], Math.min(lat, Math.max(-lat, args[1]))], args[0]);
+ }
+ };
+
+
+ var formatter = function(map) {
+ var center = map.center(),
+ zoom$$1 = map.zoom(),
+ precision = Math.max(0, Math.ceil(Math.log(zoom$$1) / Math.LN2)),
+ q = lodash.omit(utilStringQs(window.location.hash.substring(1)), ['comment', 'walkthrough']),
+ newParams = {};
+
+ delete q.id;
+ var selected = context.selectedIDs().filter(function(id) {
+ return !context.entity(id).isNew();
+ });
+ if (selected.length) {
+ newParams.id = selected.join(',');
+ }
+
+ newParams.map = zoom$$1.toFixed(2) +
+ '/' + center[1].toFixed(precision) +
+ '/' + center[0].toFixed(precision);
+
+ return '#' + utilQsString(lodash.assign(q, newParams), true);
+ };
+
+
+ function update() {
+ if (context.inIntro()) return;
+ var s1 = formatter(context.map());
+ if (s0 !== s1) {
+ window.location.replace(s0 = s1); // don't recenter the map!
+ }
+ }
+
+
+ var throttledUpdate = lodash.throttle(update, 500);
+
+
+ function hashchange() {
+ if (window.location.hash === s0) return; // ignore spurious hashchange events
+ if (parser(context.map(), (s0 = window.location.hash).substring(1))) {
+ update(); // replace bogus hash
+ }
+ }
+
+
+ function hash() {
+ context.map()
+ .on('move.hash', throttledUpdate);
+
+ context
+ .on('enter.hash', throttledUpdate);
+
+ select(window)
+ .on('hashchange.hash', hashchange);
+
+ if (window.location.hash) {
+
+ var q = utilStringQs(window.location.hash.substring(1));
+
+ if (q.id) {
+ context.zoomToEntity(q.id.split(',')[0], !q.map);
+ }
+
+ if (q.comment) {
+ context.storage('comment', q.comment);
+ context.storage('commentDate', Date.now());
+ }
+
+ if (q.walkthrough) {
+ hash.startWalkthrough = true;
+ }
+
+ hashchange();
+
+ if (q.map) {
+ hash.hadHash = true;
+ }
+ }
+ }
+
+
+ hash.off = function() {
+ throttledUpdate.cancel();
+
+ context.map()
+ .on('move.hash', null);
+
+ context
+ .on('enter.hash', null);
+
+ select(window)
+ .on('hashchange.hash', null);
+
+ window.location.hash = '';
+ };
+
+
+ return hash;
+}
+
+function behaviorLasso(context) {
+
+ var behavior = function(selection$$1) {
+ var lasso;
+
+
+ function mousedown() {
+ var button = 0; // left
+ if (event.button === button && event.shiftKey === true) {
+ lasso = null;
+
+ select(window)
+ .on('mousemove.lasso', mousemove)
+ .on('mouseup.lasso', mouseup);
+
+ event.stopPropagation();
+ }
+ }
+
+
+ function mousemove() {
+ if (!lasso) {
+ lasso = uiLasso(context);
+ context.surface().call(lasso);
+ }
+
+ lasso.p(context.mouse());
+ }
+
+
+ function normalize(a, b) {
+ return [
+ [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
+ [Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
+ }
+
+
+ function lassoed() {
+ if (!lasso) return [];
+
+ var graph = context.graph(),
+ bounds$$1 = lasso.extent().map(context.projection.invert),
+ extent$$1 = geoExtent$$1(normalize(bounds$$1[0], bounds$$1[1]));
+
+ return lodash.map(context.intersects(extent$$1).filter(function(entity) {
+ return entity.type === 'node' &&
+ geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
+ !context.features().isHidden(entity, graph, entity.geometry(graph));
+ }), 'id');
+ }
+
+
+ function mouseup() {
+ select(window)
+ .on('mousemove.lasso', null)
+ .on('mouseup.lasso', null);
+
+ if (!lasso) return;
+
+ var ids = lassoed();
+ lasso.close();
+
+ if (ids.length) {
+ context.enter(modeSelect(context, ids));
+ }
+ }
+
+ selection$$1
+ .on('mousedown.lasso', mousedown);
+ };
+
+
+ behavior.off = function(selection$$1) {
+ selection$$1.on('mousedown.lasso', null);
+ };
+
+
+ return behavior;
+}
+
+/* Creates a keybinding behavior for an operation */
+function behaviorOperation() {
+ var which, keybinding;
+
+
+ function drawIcon(selection$$1) {
+ var button = selection$$1
+ .append('svg')
+ .attr('class', 'operation-icon')
+ .append('g')
+ .attr('class', 'radial-menu-item radial-menu-item-' + which.id)
+ .attr('transform', 'translate(10,10)')
+ .classed('disabled', which.disabled());
+
+ button
+ .append('circle')
+ .attr('r', 9);
+
+ button
+ .append('use')
+ .attr('transform', 'translate(-7,-7)')
+ .attr('width', '14')
+ .attr('height', '14')
+ .attr('xlink:href', '#operation-' + which.id);
+
+ return selection$$1;
+ }
+
+
+ var behavior = function () {
+ if (which && which.available()) {
+ keybinding = d3keybinding('behavior.key.' + which.id);
+ keybinding.on(which.keys, function() {
+ event.preventDefault();
+ var disabled = which.disabled();
+
+ if (disabled) {
+ uiFlash(3000)
+ .html('')
+ .call(drawIcon)
+ .append('div')
+ .attr('class', 'operation-tip')
+ .text(which.tooltip);
+
+ } else {
+ uiFlash(1500)
+ .html('')
+ .call(drawIcon)
+ .append('div')
+ .attr('class', 'operation-tip')
+ .text(which.annotation() || which.title);
+
+ which();
+ }
+ });
+ select(document).call(keybinding);
+ }
+ return behavior;
+ };
+
+
+ behavior.off = function() {
+ if (keybinding) {
+ select(document).call(keybinding.off);
+ }
+ };
+
+
+ behavior.which = function (_) {
+ if (!arguments.length) return which;
+ which = _;
+ return behavior;
+ };
+
+
+ return behavior;
+}
+
+function behaviorPaste(context) {
+ var keybinding = d3keybinding('paste');
+
+
+ function doPaste() {
+ event.preventDefault();
+
+ var baseGraph = context.graph(),
+ mouse$$1 = context.mouse(),
+ projection$$1 = context.projection,
+ viewport = geoExtent$$1(projection$$1.clipExtent()).polygon();
+
+ if (!geoPointInPolygon(mouse$$1, viewport)) return;
+
+ var extent$$1 = geoExtent$$1(),
+ oldIDs = context.copyIDs(),
+ oldGraph = context.copyGraph(),
+ newIDs = [];
+
+ if (!oldIDs.length) return;
+
+ var action = actionCopyEntities(oldIDs, oldGraph);
+ context.perform(action);
+
+ var copies = action.copies();
+ var originals = lodash.invert(lodash.mapValues(copies, 'id'));
+ for (var id in copies) {
+ var oldEntity = oldGraph.entity(id),
+ newEntity = copies[id];
+
+ extent$$1._extend(oldEntity.extent(oldGraph));
+
+ // Exclude child nodes from newIDs if their parent way was also copied.
+ var parents = context.graph().parentWays(newEntity),
+ parentCopied = false;
+ for (var i = 0; i < parents.length; i++) {
+ if (originals[parents[i].id]) {
+ parentCopied = true;
+ break;
+ }
+ }
+
+ if (!parentCopied) {
+ newIDs.push(newEntity.id);
+ }
+ }
+
+ // Put pasted objects where mouse pointer is..
+ var center = projection$$1(extent$$1.center()),
+ delta = [ mouse$$1[0] - center[0], mouse$$1[1] - center[1] ];
+
+ context.perform(actionMove(newIDs, delta, projection$$1));
+ context.enter(modeMove$$1(context, newIDs, baseGraph));
+ }
+
+
+ function paste() {
+ keybinding.on(uiCmd('⌘V'), doPaste);
+ select(document).call(keybinding);
+ return paste;
+ }
+
+
+ paste.off = function() {
+ select(document).call(keybinding.off);
+ };
+
+
+ return paste;
+}
+
+function behaviorSelect(context) {
+ var lastMouse = null,
+ suppressMenu = true,
+ tolerance = 4,
+ p1 = null;
+
+
+ function point() {
+ return mouse(context.container().node());
+ }
+
+
+ function keydown() {
+ var e = event;
+ if (e && e.shiftKey) {
+ context.surface()
+ .classed('behavior-multiselect', true);
+ }
+
+ if (e && e.keyCode === 93) { // context menu
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+
+
+ function keyup() {
+ var e = event;
+ if (!e || !e.shiftKey) {
+ context.surface()
+ .classed('behavior-multiselect', false);
+ }
+
+
+ if (e && e.keyCode === 93) { // context menu
+ e.preventDefault();
+ e.stopPropagation();
+ contextmenu();
+ }
+ }
+
+
+ function mousedown() {
+ if (!p1) p1 = point();
+ select(window)
+ .on('mouseup.select', mouseup, true);
+
+ var isShowAlways = +context.storage('edit-menu-show-always') === 1;
+ suppressMenu = !isShowAlways;
+ }
+
+
+ function mousemove() {
+ if (event) lastMouse = event;
+ }
+
+
+ function mouseup() {
+ click();
+ }
+
+
+ function contextmenu() {
+ var e = event;
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (!+e.clientX && !+e.clientY) {
+ if (lastMouse) {
+ e.sourceEvent = lastMouse;
+ } else {
+ return;
+ }
+ }
+
+ if (!p1) p1 = point();
+ suppressMenu = false;
+ click();
+ }
+
+
+ function click() {
+ select(window)
+ .on('mouseup.select', null, true);
+
+ if (!p1) return;
+ var p2 = point(),
+ dist = geoEuclideanDistance(p1, p2);
+
+ p1 = null;
+ if (dist > tolerance) {
+ return;
+ }
+
+ var isMultiselect = event.shiftKey || select('#surface .lasso').node(),
+ isShowAlways = +context.storage('edit-menu-show-always') === 1,
+ datum = event.target.__data__ || (lastMouse && lastMouse.target.__data__),
+ mode = context.mode();
+
+
+ if (datum && datum.type === 'midpoint') {
+ datum = datum.parents[0];
+ }
+
+ if (!(datum instanceof osmEntity$$1)) {
+ // clicked nothing..
+ if (!isMultiselect && mode.id !== 'browse') {
+ context.enter(modeBrowse(context));
+ }
+
+ } else {
+ // clicked an entity..
+ var selectedIDs = context.selectedIDs();
+
+ if (!isMultiselect) {
+ if (selectedIDs.length > 1 && (!suppressMenu && !isShowAlways)) {
+ // multiple things already selected, just show the menu...
+ mode.suppressMenu(false).reselect();
+ } else {
+ // select a single thing..
+ context.enter(modeSelect(context, [datum.id]).suppressMenu(suppressMenu));
+ }
+
+ } else {
+ if (selectedIDs.indexOf(datum.id) !== -1) {
+ // clicked entity is already in the selectedIDs list..
+ if (!suppressMenu && !isShowAlways) {
+ // don't deselect clicked entity, just show the menu.
+ mode.suppressMenu(false).reselect();
+ } else {
+ // deselect clicked entity, then reenter select mode or return to browse mode..
+ selectedIDs = lodash.without(selectedIDs, datum.id);
+ context.enter(selectedIDs.length ? modeSelect(context, selectedIDs) : modeBrowse(context));
+ }
+ } else {
+ // clicked entity is not in the selected list, add it..
+ selectedIDs = selectedIDs.concat([datum.id]);
+ context.enter(modeSelect(context, selectedIDs).suppressMenu(suppressMenu));
+ }
+ }
+ }
+
+ // reset for next time..
+ suppressMenu = true;
+ }
+
+
+ var behavior = function(selection$$1) {
+ lastMouse = null;
+ suppressMenu = true;
+ p1 = null;
+
+ select(window)
+ .on('keydown.select', keydown)
+ .on('keyup.select', keyup)
+ .on('contextmenu.select-window', function() {
+ // Edge and IE really like to show the contextmenu on the
+ // menubar when user presses a keyboard menu button
+ // even after we've already preventdefaulted the key event.
+ var e = event;
+ if (+e.clientX === 0 && +e.clientY === 0) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ });
+
+ selection$$1
+ .on('mousedown.select', mousedown)
+ .on('mousemove.select', mousemove)
+ .on('contextmenu.select', contextmenu);
+
+ if (event && event.shiftKey) {
+ context.surface()
+ .classed('behavior-multiselect', true);
+ }
+ };
+
+
+ behavior.off = function(selection$$1) {
+ select(window)
+ .on('keydown.select', null)
+ .on('keyup.select', null)
+ .on('contextmenu.select-window', null)
+ .on('mouseup.select', null, true);
+
+ selection$$1
+ .on('mousedown.select', null)
+ .on('mousemove.select', null)
+ .on('contextmenu.select', null);
+
+ context.surface()
+ .classed('behavior-multiselect', false);
+ };
+
+
+ return behavior;
+}
+
+function modeAddArea$$1(context) {
+ var mode = {
+ id: 'add-area',
+ button: 'area',
+ title: t('modes.add_area.title'),
+ description: t('modes.add_area.description'),
+ key: '3'
+ };
+
+ var behavior = behaviorAddWay(context)
+ .tail(t('modes.add_area.tail'))
+ .on('start', start)
+ .on('startFromWay', startFromWay)
+ .on('startFromNode', startFromNode),
+ defaultTags = { area: 'yes' };
+
+
+ function actionClose(wayId) {
+ return function (graph) {
+ return graph.replace(graph.entity(wayId).close());
+ };
+ }
+
+
+ function start(loc) {
+ var startGraph = context.graph(),
+ node = osmNode({ loc: loc }),
+ way = osmWay({ tags: defaultTags });
+
+ context.perform(
+ actionAddEntity(node),
+ actionAddEntity(way),
+ actionAddVertex(way.id, node.id),
+ actionClose(way.id)
+ );
+
+ context.enter(modeDrawArea(context, way.id, startGraph));
+ }
+
+
+ function startFromWay(loc, edge) {
+ var startGraph = context.graph(),
+ node = osmNode({ loc: loc }),
+ way = osmWay({ tags: defaultTags });
+
+ context.perform(
+ actionAddEntity(node),
+ actionAddEntity(way),
+ actionAddVertex(way.id, node.id),
+ actionClose(way.id),
+ actionAddMidpoint({ loc: loc, edge: edge }, node)
+ );
+
+ context.enter(modeDrawArea(context, way.id, startGraph));
+ }
+
+
+ function startFromNode(node) {
+ var startGraph = context.graph(),
+ way = osmWay({ tags: defaultTags });
+
+ context.perform(
+ actionAddEntity(way),
+ actionAddVertex(way.id, node.id),
+ actionClose(way.id)
+ );
+
+ context.enter(modeDrawArea(context, way.id, startGraph));
+ }
+
+
+ mode.enter = function() {
+ context.install(behavior);
+ };
+
+
+ mode.exit = function() {
+ context.uninstall(behavior);
+ };
+
+
+ return mode;
+}
+
+function modeAddLine$$1(context) {
+ var mode = {
+ id: 'add-line',
+ button: 'line',
+ title: t('modes.add_line.title'),
+ description: t('modes.add_line.description'),
+ key: '2'
+ };
+
+ var behavior = behaviorAddWay(context)
+ .tail(t('modes.add_line.tail'))
+ .on('start', start)
+ .on('startFromWay', startFromWay)
+ .on('startFromNode', startFromNode);
+
+
+ function start(loc) {
+ var startGraph = context.graph(),
+ node = osmNode({ loc: loc }),
+ way = osmWay();
+
+ context.perform(
+ actionAddEntity(node),
+ actionAddEntity(way),
+ actionAddVertex(way.id, node.id)
+ );
+
+ context.enter(modeDrawLine(context, way.id, startGraph));
+ }
+
+
+ function startFromWay(loc, edge) {
+ var startGraph = context.graph(),
+ node = osmNode({ loc: loc }),
+ way = osmWay();
+
+ context.perform(
+ actionAddEntity(node),
+ actionAddEntity(way),
+ actionAddVertex(way.id, node.id),
+ actionAddMidpoint({ loc: loc, edge: edge }, node)
+ );
+
+ context.enter(modeDrawLine(context, way.id, startGraph));
+ }
+
+
+ function startFromNode(node) {
+ var startGraph = context.graph(),
+ way = osmWay();
+
+ context.perform(
+ actionAddEntity(way),
+ actionAddVertex(way.id, node.id)
+ );
+
+ context.enter(modeDrawLine(context, way.id, startGraph));
+ }
+
+
+ mode.enter = function() {
+ context.install(behavior);
+ };
+
+
+ mode.exit = function() {
+ context.uninstall(behavior);
+ };
+
+ return mode;
+}
+
+function modeAddPoint$$1(context) {
+ var mode = {
+ id: 'add-point',
+ button: 'point',
+ title: t('modes.add_point.title'),
+ description: t('modes.add_point.description'),
+ key: '1'
+ };
+
+ var behavior = behaviorDraw(context)
+ .tail(t('modes.add_point.tail'))
+ .on('click', add)
+ .on('clickWay', addWay)
+ .on('clickNode', addNode)
+ .on('cancel', cancel)
+ .on('finish', cancel);
+
+
+ function add(loc) {
+ var node = osmNode({ loc: loc });
+
+ context.perform(
+ actionAddEntity(node),
+ t('operations.add.annotation.point')
+ );
+
+ context.enter(
+ modeSelect(context, [node.id]).newFeature(true)
+ );
+ }
+
+
+ function addWay(loc) {
+ add(loc);
+ }
+
+
+ function addNode(node) {
+ add(node.loc);
+ }
+
+
+ function cancel() {
+ context.enter(modeBrowse(context));
+ }
+
+
+ mode.enter = function() {
+ context.install(behavior);
+ };
+
+
+ mode.exit = function() {
+ context.uninstall(behavior);
+ };
+
+
+ return mode;
+}
+
+function modeDragNode$$1(context) {
+ var mode = {
+ id: 'drag-node',
+ button: 'browse'
+ };
+
+ var nudgeInterval,
+ activeIDs,
+ wasMidpoint,
+ isCancelled,
+ lastLoc,
+ selectedIDs = [],
+ hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover),
+ edit = behaviorEdit(context);
+
+
+ function vecSub(a, b) {
+ return [a[0] - b[0], a[1] - b[1]];
+ }
+
+ function edge(point, size) {
+ var pad = [80, 20, 50, 20], // top, right, bottom, left
+ x = 0,
+ y = 0;
+
+ if (point[0] > size[0] - pad[1])
+ x = -10;
+ if (point[0] < pad[3])
+ x = 10;
+ if (point[1] > size[1] - pad[2])
+ y = -10;
+ if (point[1] < pad[0])
+ y = 10;
+
+ if (x || y) {
+ return [x, y];
+ } else {
+ return null;
+ }
+ }
+
+
+ function startNudge(entity, nudge) {
+ if (nudgeInterval) window.clearInterval(nudgeInterval);
+ nudgeInterval = window.setInterval(function() {
+ context.pan(nudge);
+ doMove(entity, nudge);
+ }, 50);
+ }
+
+
+ function stopNudge() {
+ if (nudgeInterval) {
+ window.clearInterval(nudgeInterval);
+ nudgeInterval = null;
+ }
+ }
+
+
+ function moveAnnotation(entity) {
+ return t('operations.move.annotation.' + entity.geometry(context.graph()));
+ }
+
+
+ function connectAnnotation(entity) {
+ return t('operations.connect.annotation.' + entity.geometry(context.graph()));
+ }
+
+
+ function origin(entity) {
+ return context.projection(entity.loc);
+ }
+
+
+ function start(entity) {
+ wasMidpoint = entity.type === 'midpoint';
+
+ isCancelled = event.sourceEvent.shiftKey ||
+ context.features().hasHiddenConnections(entity, context.graph());
+
+ if (isCancelled) {
+ return behavior.cancel();
+ }
+
+ if (wasMidpoint) {
+ var midpoint = entity;
+ entity = osmNode();
+ context.perform(actionAddMidpoint(midpoint, entity));
+
+ var vertex = context.surface().selectAll('.' + entity.id);
+ behavior.target(vertex.node(), entity);
+
+ } else {
+ context.perform(actionNoop());
+ }
+
+ // activeIDs generate no pointer events. This prevents the node or vertex
+ // being dragged from trying to connect to itself or its parent element.
+ activeIDs = lodash.map(context.graph().parentWays(entity), 'id');
+ activeIDs.push(entity.id);
+ setActiveElements();
+
+ context.enter(mode);
+ }
+
+
+ function datum() {
+ var event$$1 = event && event.sourceEvent;
+ if (!event$$1 || event$$1.altKey) {
+ return {};
+ } else {
+ return event$$1.target.__data__ || {};
+ }
+ }
+
+
+ function doMove(entity, nudge) {
+ nudge = nudge || [0, 0];
+
+ var currPoint = (event && event.point) || context.projection(lastLoc),
+ currMouse = vecSub(currPoint, nudge),
+ loc = context.projection.invert(currMouse),
+ d = datum();
+
+ if (!nudgeInterval) {
+ if (d.type === 'node' && d.id !== entity.id) {
+ loc = d.loc;
+ } else if (d.type === 'way' && !select(event.sourceEvent.target).classed('fill')) {
+ loc = geoChooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
+ }
+ }
+
+ context.replace(
+ actionMoveNode(entity.id, loc),
+ moveAnnotation(entity)
+ );
+
+ lastLoc = loc;
+ }
+
+
+ function move(entity) {
+ if (isCancelled) return;
+ event.sourceEvent.stopPropagation();
+ lastLoc = context.projection.invert(event.point);
+
+ doMove(entity);
+ var nudge = edge(event.point, context.map().dimensions());
+ if (nudge) {
+ startNudge(entity, nudge);
+ } else {
+ stopNudge();
+ }
+ }
+
+
+ function end(entity) {
+ if (isCancelled) return;
+
+ var d = datum();
+
+ if (d.type === 'way') {
+ var choice = geoChooseEdge(context.childNodes(d), context.mouse(), context.projection);
+ context.replace(
+ actionAddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
+ connectAnnotation(d)
+ );
+
+ } else if (d.type === 'node' && d.id !== entity.id) {
+ context.replace(
+ actionConnect([d.id, entity.id]),
+ connectAnnotation(d)
+ );
+
+ } else if (wasMidpoint) {
+ context.replace(
+ actionNoop(),
+ t('operations.add.annotation.vertex')
+ );
+
+ } else {
+ context.replace(
+ actionNoop(),
+ moveAnnotation(entity)
+ );
+ }
+
+ var reselection = selectedIDs.filter(function(id) {
+ return context.graph().hasEntity(id);
+ });
+
+ if (reselection.length) {
+ context.enter(modeSelect(context, reselection));
+ } else {
+ context.enter(modeBrowse(context));
+ }
+ }
+
+
+ function cancel() {
+ behavior.cancel();
+ context.enter(modeBrowse(context));
+ }
+
+
+ function setActiveElements() {
+ context.surface().selectAll(utilEntitySelector(activeIDs))
+ .classed('active', true);
+ }
+
+
+ var behavior = behaviorDrag()
+ .selector('g.node, g.point, g.midpoint')
+ .surface(select('#map').node())
+ .origin(origin)
+ .on('start', start)
+ .on('move', move)
+ .on('end', end);
+
+
+ mode.enter = function() {
+ context.install(hover);
+ context.install(edit);
+
+ context.history()
+ .on('undone.drag-node', cancel);
+
+ context.map()
+ .on('drawn.drag-node', setActiveElements);
+
+ setActiveElements();
+ };
+
+
+ mode.exit = function() {
+ context.ui().sidebar.hover.cancel();
+ context.uninstall(hover);
+ context.uninstall(edit);
+
+ context.history()
+ .on('undone.drag-node', null);
+
+ context.map()
+ .on('drawn.drag-node', null);
+
+ context.surface()
+ .selectAll('.active')
+ .classed('active', false);
+
+ stopNudge();
+ };
+
+
+ mode.selectedIDs = function(_) {
+ if (!arguments.length) return selectedIDs;
+ selectedIDs = _;
+ return mode;
+ };
+
+
+ mode.behavior = behavior;
+
+
+ return mode;
+}
+
+function modeBrowse(context) {
+ var mode = {
+ button: 'browse',
+ id: 'browse',
+ title: t('modes.browse.title'),
+ description: t('modes.browse.description')
+ }, sidebar;
+
+ var behaviors = [
+ behaviorPaste(context),
+ behaviorHover(context).on('hover', context.ui().sidebar.hover),
+ behaviorSelect(context),
+ behaviorLasso(context),
+ modeDragNode$$1(context).behavior
+ ];
+
+
+ mode.enter = function() {
+ behaviors.forEach(function(behavior) {
+ context.install(behavior);
+ });
+
+ // Get focus on the body.
+ if (document.activeElement && document.activeElement.blur) {
+ document.activeElement.blur();
+ }
+
+ if (sidebar) {
+ context.ui().sidebar.show(sidebar);
+ } else {
+ context.ui().sidebar.select(null);
+ }
+ };
+
+
+ mode.exit = function() {
+ context.ui().sidebar.hover.cancel();
+ behaviors.forEach(function(behavior) {
+ context.uninstall(behavior);
+ });
+
+ if (sidebar) {
+ context.ui().sidebar.hide();
+ }
+ };
+
+
+ mode.sidebar = function(_) {
+ if (!arguments.length) return sidebar;
+ sidebar = _;
+ return mode;
+ };
+
+
+ return mode;
+}
+
+function modeDrawArea(context, wayId, startGraph) {
+ var mode = {
+ button: 'area',
+ id: 'draw-area'
+ };
+
+ var behavior;
+
+
+ mode.enter = function() {
+ var way = context.entity(wayId);
+
+ behavior = behaviorDrawWay(context, wayId, undefined, mode, startGraph)
+ .tail(t('modes.draw_area.tail'));
+
+ var addNode = behavior.addNode;
+
+ behavior.addNode = function(node) {
+ var length = way.nodes.length,
+ penultimate = length > 2 ? way.nodes[length - 2] : null;
+
+ if (node.id === way.first() || node.id === penultimate) {
+ behavior.finish();
+ } else {
+ addNode(node);
+ }
+ };
+
+ context.install(behavior);
+ };
+
+
+ mode.exit = function() {
+ context.uninstall(behavior);
+ };
+
+
+ mode.selectedIDs = function() {
+ return [wayId];
+ };
+
+
+ return mode;
+}
+
+function modeDrawLine(context, wayId, startGraph, affix) {
+ var mode = {
+ button: 'line',
+ id: 'draw-line'
+ };
+
+ var behavior;
+
+
+ mode.enter = function() {
+ var way = context.entity(wayId),
+ index = (affix === 'prefix') ? 0 : undefined,
+ headId = (affix === 'prefix') ? way.first() : way.last();
+
+ behavior = behaviorDrawWay(context, wayId, index, mode, startGraph)
+ .tail(t('modes.draw_line.tail'));
+
+ var addNode = behavior.addNode;
+
+ behavior.addNode = function(node) {
+ if (node.id === headId) {
+ behavior.finish();
+ } else {
+ addNode(node);
+ }
+ };
+
+ context.install(behavior);
+ };
+
+
+ mode.exit = function() {
+ context.uninstall(behavior);
+ };
+
+
+ mode.selectedIDs = function() {
+ return [wayId];
+ };
+
+
+ return mode;
+}
+
+function operationCircularize(selectedIDs, context) {
+ var entityId = selectedIDs[0],
+ entity = context.entity(entityId),
+ extent = entity.extent(context.graph()),
+ geometry = context.geometry(entityId),
+ action = actionCircularize(entityId, context.projection);
+
+
+ var operation = function() {
+ context.perform(action, operation.annotation());
+ };
+
+
+ operation.available = function() {
+ return selectedIDs.length === 1 &&
+ entity.type === 'way' &&
+ lodash.uniq(entity.nodes).length > 1;
+ };
+
+
+ operation.disabled = function() {
+ var reason;
+ if (extent.percentContainedIn(context.extent()) < 0.8) {
+ reason = 'too_large';
+ } else if (context.hasHiddenConnections(entityId)) {
+ reason = 'connected_to_hidden';
+ }
+ return action.disabled(context.graph()) || reason;
+ };
+
+
+ operation.tooltip = function() {
+ var disable = operation.disabled();
+ return disable ?
+ t('operations.circularize.' + disable) :
+ t('operations.circularize.description.' + geometry);
+ };
+
+
+ operation.annotation = function() {
+ return t('operations.circularize.annotation.' + geometry);
+ };
+
+
+ operation.id = 'circularize';
+ operation.keys = [t('operations.circularize.key')];
+ operation.title = t('operations.circularize.title');
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}
+
+function operationContinue(selectedIDs, context) {
+ var graph = context.graph(),
+ entities = selectedIDs.map(function(id) { return graph.entity(id); }),
+ geometries = lodash.extend({ line: [], vertex: [] },
+ lodash.groupBy(entities, function(entity) { return entity.geometry(graph); })),
+ vertex = geometries.vertex[0];
+
+
+ function candidateWays() {
+ return graph.parentWays(vertex).filter(function(parent) {
+ return parent.geometry(graph) === 'line' &&
+ !parent.isClosed() &&
+ parent.affix(vertex.id) &&
+ (geometries.line.length === 0 || geometries.line[0] === parent);
+ });
+ }
+
+
+ var operation = function() {
+ var candidate = candidateWays()[0];
+ context.enter(
+ modeDrawLine(context, candidate.id, context.graph(), candidate.affix(vertex.id))
+ );
+ };
+
+
+ operation.available = function() {
+ return geometries.vertex.length === 1 && geometries.line.length <= 1 &&
+ !context.features().hasHiddenConnections(vertex, context.graph());
+ };
+
+
+ operation.disabled = function() {
+ var candidates = candidateWays();
+ if (candidates.length === 0)
+ return 'not_eligible';
+ if (candidates.length > 1)
+ return 'multiple';
+ };
+
+
+ operation.tooltip = function() {
+ var disable = operation.disabled();
+ return disable ?
+ t('operations.continue.' + disable) :
+ t('operations.continue.description');
+ };
+
+
+ operation.annotation = function() {
+ return t('operations.continue.annotation.line');
+ };
+
+
+ operation.id = 'continue';
+ operation.keys = [t('operations.continue.key')];
+ operation.title = t('operations.continue.title');
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}
+
+function operationDelete(selectedIDs, context) {
+ var multi = (selectedIDs.length === 1 ? 'single' : 'multiple'),
+ action = actionDeleteMultiple(selectedIDs),
+ extent = selectedIDs.reduce(function(extent, id) {
+ return extent.extend(context.entity(id).extent(context.graph()));
+ }, geoExtent$$1());
+
+
+ var operation = function() {
+ var nextSelectedID;
+
+ if (selectedIDs.length === 1) {
+ var id = selectedIDs[0],
+ entity = context.entity(id),
+ geometry = context.geometry(id),
+ parents = context.graph().parentWays(entity),
+ parent = parents[0];
+
+ // Select the next closest node in the way.
+ if (geometry === 'vertex' && parent.nodes.length > 2) {
+ var nodes = parent.nodes,
+ i = nodes.indexOf(id);
+
+ if (i === 0) {
+ i++;
+ } else if (i === nodes.length - 1) {
+ i--;
+ } else {
+ var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc),
+ b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
+ i = a < b ? i - 1 : i + 1;
+ }
+
+ nextSelectedID = nodes[i];
+ }
+ }
+
+ context.perform(action, operation.annotation());
+
+ if (nextSelectedID && context.hasEntity(nextSelectedID)) {
+ context.enter(modeSelect(context, [nextSelectedID]).follow(true));
+ } else {
+ context.enter(modeBrowse(context));
+ }
+
+ };
+
+
+ operation.available = function() {
+ return true;
+ };
+
+
+ operation.disabled = function() {
+ var reason;
+ if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
+ reason = 'too_large';
+ } else if (lodash.some(selectedIDs, context.hasHiddenConnections)) {
+ reason = 'connected_to_hidden';
+ } else if (lodash.some(selectedIDs, protectedMember)) {
+ reason = 'part_of_relation';
+ } else if (lodash.some(selectedIDs, incompleteRelation)) {
+ reason = 'incomplete_relation';
+ }
+ return reason;
+
+ function incompleteRelation(id) {
+ var entity = context.entity(id);
+ return entity.type === 'relation' && !entity.isComplete(context.graph());
+ }
+
+ function protectedMember(id) {
+ var entity = context.entity(id);
+ if (entity.type !== 'way') return false;
+
+ var parents = context.graph().parentRelations(entity);
+ for (var i = 0; i < parents.length; i++) {
+ var parent = parents[i],
+ type = parent.tags.type,
+ role = parent.memberById(id).role || 'outer';
+ if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ };
+
+
+ operation.tooltip = function() {
+ var disable = operation.disabled();
+ return disable ?
+ t('operations.delete.' + disable + '.' + multi) :
+ t('operations.delete.description' + '.' + multi);
+ };
+
+
+ operation.annotation = function() {
+ return selectedIDs.length === 1 ?
+ t('operations.delete.annotation.' + context.geometry(selectedIDs[0])) :
+ t('operations.delete.annotation.multiple', { n: selectedIDs.length });
+ };
+
+
+ operation.id = 'delete';
+ operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
+ operation.title = t('operations.delete.title');
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}
+
+function operationDisconnect(selectedIDs, context) {
+ var vertices = lodash.filter(selectedIDs, function(entityId) {
+ return context.geometry(entityId) === 'vertex';
+ });
+
+ var entityId = vertices[0],
+ action = actionDisconnect(entityId);
+
+ if (selectedIDs.length > 1) {
+ action.limitWays(lodash.without(selectedIDs, entityId));
+ }
+
+
+ var operation = function() {
+ context.perform(action, operation.annotation());
+ };
+
+
+ operation.available = function() {
+ return vertices.length === 1;
+ };
+
+
+ operation.disabled = function() {
+ var reason;
+ if (lodash.some(selectedIDs, context.hasHiddenConnections)) {
+ reason = 'connected_to_hidden';
+ }
+ return action.disabled(context.graph()) || reason;
+ };
+
+
+ operation.tooltip = function() {
+ var disable = operation.disabled();
+ return disable ?
+ t('operations.disconnect.' + disable) :
+ t('operations.disconnect.description');
+ };
+
+
+ operation.annotation = function() {
+ return t('operations.disconnect.annotation');
+ };
+
+
+ operation.id = 'disconnect';
+ operation.keys = [t('operations.disconnect.key')];
+ operation.title = t('operations.disconnect.title');
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}
+
+function operationMerge(selectedIDs, context) {
+
+ function updatePresetTags(newGraph, ids) {
+ var id = ids[0],
+ newEntity = newGraph.hasEntity(id);
+
+ if (!newEntity) return;
+
+ var newPreset = context.presets().match(newEntity, newGraph);
+
+ context.replace(actionChangePreset(id, null, newPreset), operation.annotation());
+ }
+
+
+ var join = actionJoin(selectedIDs),
+ merge = actionMerge(selectedIDs),
+ mergePolygon = actionMergePolygon(selectedIDs);
+
+
+ var operation = function() {
+ var origGraph = context.graph(),
+ action;
+
+ if (!join.disabled(origGraph)) {
+ action = join;
+ } else if (!merge.disabled(origGraph)) {
+ action = merge;
+ } else {
+ action = mergePolygon;
+ }
+
+ context.perform(action, operation.annotation());
+
+ var ids = selectedIDs.filter(function(id) {
+ var entity = context.hasEntity(id);
+ return entity && entity.type !== 'node';
+ });
+
+ // if we merged tags, rematch preset to update tags if necessary (#3851)
+ if (action === merge) {
+ updatePresetTags(context.graph(), ids);
+ }
+
+ context.enter(modeSelect(context, ids));
+ };
+
+
+ operation.available = function() {
+ return selectedIDs.length >= 2;
+ };
+
+
+ operation.disabled = function() {
+ return join.disabled(context.graph()) &&
+ merge.disabled(context.graph()) &&
+ mergePolygon.disabled(context.graph());
+ };
+
+
+ operation.tooltip = function() {
+ var j = join.disabled(context.graph()),
+ m = merge.disabled(context.graph()),
+ p = mergePolygon.disabled(context.graph());
+
+ if (j === 'restriction' && m && p) {
+ return t('operations.merge.restriction',
+ { relation: context.presets().item('type/restriction').name() });
+ }
+
+ if (p === 'incomplete_relation' && j && m) {
+ return t('operations.merge.incomplete_relation');
+ }
+
+ if (j && m && p) {
+ return t('operations.merge.' + j);
+ }
+
+ return t('operations.merge.description');
+ };
+
+
+ operation.annotation = function() {
+ return t('operations.merge.annotation', { n: selectedIDs.length });
+ };
+
+
+ operation.id = 'merge';
+ operation.keys = [t('operations.merge.key')];
+ operation.title = t('operations.merge.title');
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}
+
+function operationMove(selectedIDs, context) {
+ var multi = (selectedIDs.length === 1 ? 'single' : 'multiple'),
+ extent = selectedIDs.reduce(function(extent, id) {
+ return extent.extend(context.entity(id).extent(context.graph()));
+ }, geoExtent$$1());
+
+
+ var operation = function() {
+ context.enter(modeMove$$1(context, selectedIDs));
+ };
+
+
+ operation.available = function() {
+ return selectedIDs.length > 1 ||
+ context.entity(selectedIDs[0]).type !== 'node';
+ };
+
+
+ operation.disabled = function() {
+ var reason;
+ if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
+ reason = 'too_large';
+ } else if (lodash.some(selectedIDs, context.hasHiddenConnections)) {
+ reason = 'connected_to_hidden';
+ } else if (lodash.some(selectedIDs, incompleteRelation)) {
+ reason = 'incomplete_relation';
+ }
+ return reason;
+
+ function incompleteRelation(id) {
+ var entity = context.entity(id);
+ return entity.type === 'relation' && !entity.isComplete(context.graph());
+ }
+ };
+
+
+ operation.tooltip = function() {
+ var disable = operation.disabled();
+ return disable ?
+ t('operations.move.' + disable + '.' + multi) :
+ t('operations.move.description.' + multi);
+ };
+
+
+ operation.annotation = function() {
+ return selectedIDs.length === 1 ?
+ t('operations.move.annotation.' + context.geometry(selectedIDs[0])) :
+ t('operations.move.annotation.multiple');
+ };
+
+
+ operation.id = 'move';
+ operation.keys = [t('operations.move.key')];
+ operation.title = t('operations.move.title');
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}
+
+function operationOrthogonalize(selectedIDs, context) {
+ var entityId = selectedIDs[0],
+ entity = context.entity(entityId),
+ extent = entity.extent(context.graph()),
+ geometry = context.geometry(entityId),
+ action = actionOrthogonalize(entityId, context.projection);
+
+
+ var operation = function() {
+ context.perform(action, operation.annotation());
+ };
+
+
+ operation.available = function() {
+ return selectedIDs.length === 1 &&
+ entity.type === 'way' &&
+ entity.isClosed() &&
+ lodash.uniq(entity.nodes).length > 2;
+ };
+
+
+ operation.disabled = function() {
+ var reason;
+ if (extent.percentContainedIn(context.extent()) < 0.8) {
+ reason = 'too_large';
+ } else if (context.hasHiddenConnections(entityId)) {
+ reason = 'connected_to_hidden';
+ }
+ return action.disabled(context.graph()) || reason;
+ };
+
+
+ operation.tooltip = function() {
+ var disable = operation.disabled();
+ return disable ?
+ t('operations.orthogonalize.' + disable) :
+ t('operations.orthogonalize.description.' + geometry);
+ };
+
+
+ operation.annotation = function() {
+ return t('operations.orthogonalize.annotation.' + geometry);
+ };
+
+
+ operation.id = 'orthogonalize';
+ operation.keys = [t('operations.orthogonalize.key')];
+ operation.title = t('operations.orthogonalize.title');
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}
+
+function operationReflectShort(selectedIDs, context) {
+ return operationReflect(selectedIDs, context, 'short');
+}
+
+
+function operationReflectLong(selectedIDs, context) {
+ return operationReflect(selectedIDs, context, 'long');
+}
+
+
+function operationReflect(selectedIDs, context, axis) {
+ axis = axis || 'long';
+ var multi = (selectedIDs.length === 1 ? 'single' : 'multiple'),
+ extent = selectedIDs.reduce(function(extent, id) {
+ return extent.extend(context.entity(id).extent(context.graph()));
+ }, geoExtent$$1());
+
+
+ var operation = function() {
+ var action = actionReflect(selectedIDs, context.projection)
+ .useLongAxis(Boolean(axis === 'long'));
+ context.perform(action, operation.annotation());
+ };
+
+
+ operation.available = function() {
+ return lodash.some(selectedIDs, hasArea);
+
+ function hasArea(id) {
+ var entity = context.entity(id);
+ return (entity.type === 'way' && entity.isClosed()) ||
+ (entity.type ==='relation' && entity.isMultipolygon());
+ }
+ };
+
+
+ operation.disabled = function() {
+ var reason;
+ if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
+ reason = 'too_large';
+ } else if (lodash.some(selectedIDs, context.hasHiddenConnections)) {
+ reason = 'connected_to_hidden';
+ } else if (lodash.some(selectedIDs, incompleteRelation)) {
+ reason = 'incomplete_relation';
+ }
+ return reason;
+
+ function incompleteRelation(id) {
+ var entity = context.entity(id);
+ return entity.type === 'relation' && !entity.isComplete(context.graph());
+ }
+ };
+
+
+ operation.tooltip = function() {
+ var disable = operation.disabled();
+ return disable ?
+ t('operations.reflect.' + disable + '.' + multi) :
+ t('operations.reflect.description.' + axis + '.' + multi);
+ };
+
+
+ operation.annotation = function() {
+ return t('operations.reflect.annotation.' + axis + '.' + multi);
+ };
+
+
+ operation.id = 'reflect-' + axis;
+ operation.keys = [t('operations.reflect.key.' + axis)];
+ operation.title = t('operations.reflect.title.' + axis);
+ operation.behavior = behaviorOperation(context).which(operation);
+
+ return operation;
+}