X-Git-Url: https://git.openstreetmap.org./rails.git/blobdiff_plain/1104165e7ba4640e0c5e0bb4361e76f6911407bd..ad368d189f4842f18e81fd272c677e5d820a2da4:/vendor/assets/iD/iD.js?ds=sidebyside
diff --git a/vendor/assets/iD/iD.js b/vendor/assets/iD/iD.js
index 6c0845b7c..a19dc713e 100644
--- a/vendor/assets/iD/iD.js
+++ b/vendor/assets/iD/iD.js
@@ -175,7 +175,7 @@
})(this);
d3 = (function(){
- var d3 = {version: "3.2.7"}; // semver
+ var d3 = {version: "3.3.8"}; // semver
d3.ascending = function(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
};
@@ -313,12 +313,15 @@ d3.shuffle = function(array) {
return array;
};
d3.permute = function(array, indexes) {
- var permutes = [],
- i = -1,
- n = indexes.length;
- while (++i < n) permutes[i] = array[indexes[i]];
+ var i = indexes.length, permutes = new Array(i);
+ while (i--) permutes[i] = array[indexes[i]];
return permutes;
};
+d3.pairs = function(array) {
+ var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
+ while (i < n) pairs[i] = [p0 = p1, p1 = array[++i]];
+ return pairs;
+};
d3.zip = function() {
if (!(n = arguments.length)) return [];
@@ -353,8 +356,28 @@ d3.entries = function(map) {
return entries;
};
d3.merge = function(arrays) {
- return Array.prototype.concat.apply([], arrays);
+ var n = arrays.length,
+ m,
+ i = -1,
+ j = 0,
+ merged,
+ array;
+
+ while (++i < n) j += arrays[i].length;
+ merged = new Array(j);
+
+ while (--n >= 0) {
+ array = arrays[n];
+ m = array.length;
+ while (--m >= 0) {
+ merged[--j] = array[m];
+ }
+ }
+
+ return merged;
};
+var abs = Math.abs;
+
d3.range = function(start, stop, step) {
if (arguments.length < 3) {
step = 1;
@@ -365,7 +388,7 @@ d3.range = function(start, stop, step) {
}
if ((stop - start) / step === Infinity) throw new Error("infinite range");
var range = [],
- k = d3_range_integerScale(Math.abs(step)),
+ k = d3_range_integerScale(abs(step)),
i = -1,
j;
start *= k, stop *= k, step *= k;
@@ -394,7 +417,8 @@ function d3_class(ctor, properties) {
d3.map = function(object) {
var map = new d3_Map;
- for (var key in object) map.set(key, object[key]);
+ if (object instanceof d3_Map) object.forEach(function(key, value) { map.set(key, value); });
+ else for (var key in object) map.set(key, object[key]);
return map;
};
@@ -538,8 +562,8 @@ d3.nest = function() {
};
d3.set = function(array) {
- var set = new d3_Set();
- if (array) for (var i = 0; i < array.length; i++) set.add(array[i]);
+ var set = new d3_Set;
+ if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
return set;
};
@@ -573,9 +597,23 @@ d3_class(d3_Set, {
}
});
d3.behavior = {};
+var d3_arraySlice = [].slice,
+ d3_array = function(list) { return d3_arraySlice.call(list); }; // conversion for NodeLists
+
var d3_document = document,
d3_documentElement = d3_document.documentElement,
d3_window = window;
+
+// Redefine d3_array if the browser doesnât support slice-based conversion.
+try {
+ d3_array(d3_documentElement.childNodes)[0].nodeType;
+} catch(e) {
+ d3_array = function(list) {
+ var i = list.length, array = new Array(i);
+ while (i--) array[i] = list[i];
+ return array;
+ };
+}
// Copies a variable number of methods from source to target.
d3.rebind = function(target, source) {
var i = 1, n = arguments.length, method;
@@ -603,24 +641,6 @@ function d3_vendorSymbol(object, name) {
}
var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"];
-
-var d3_array = d3_arraySlice; // conversion for NodeLists
-
-function d3_arrayCopy(pseudoarray) {
- var i = -1, n = pseudoarray.length, array = [];
- while (++i < n) array.push(pseudoarray[i]);
- return array;
-}
-
-function d3_arraySlice(pseudoarray) {
- return Array.prototype.slice.call(pseudoarray);
-}
-
-try {
- d3_array(d3_documentElement.childNodes)[0].nodeType;
-} catch(e) {
- d3_array = d3_arrayCopy;
-}
function d3_noop() {}
d3.dispatch = function() {
@@ -1118,15 +1138,15 @@ d3_selectionPrototype.append = function(name) {
function d3_selection_creator(name) {
return typeof name === "function" ? name
- : (name = d3.ns.qualify(name)).local ? function() { return d3_document.createElementNS(name.space, name.local); }
- : function() { return d3_document.createElementNS(this.namespaceURI, name); };
+ : (name = d3.ns.qualify(name)).local ? function() { return this.ownerDocument.createElementNS(name.space, name.local); }
+ : function() { return this.ownerDocument.createElementNS(this.namespaceURI, name); };
}
d3_selectionPrototype.insert = function(name, before) {
name = d3_selection_creator(name);
before = d3_selection_selector(before);
return this.select(function() {
- return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments));
+ return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
});
};
@@ -1310,7 +1330,7 @@ d3_selectionPrototype.sort = function(comparator) {
function d3_selection_sortComparator(comparator) {
if (!arguments.length) comparator = d3.ascending;
return function(a, b) {
- return (!a - !b) || comparator(a.__data__, b.__data__);
+ return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
};
}
@@ -1415,6 +1435,8 @@ function d3_selection_enterInsertBefore(enter) {
};
}
+// import "../transition/transition";
+
d3_selectionPrototype.transition = function() {
var id = d3_transitionInheritId || ++d3_transitionId,
subgroups = [],
@@ -1432,6 +1454,16 @@ d3_selectionPrototype.transition = function() {
return d3_transition(subgroups, id);
};
+// import "../transition/transition";
+
+d3_selectionPrototype.interrupt = function() {
+ return this.each(d3_selection_interrupt);
+};
+
+function d3_selection_interrupt() {
+ var lock = this.__transition__;
+ if (lock) ++lock.active;
+}
// TODO fast singleton implementation?
d3.select = function(node) {
@@ -1578,6 +1610,7 @@ d3.mouse = function(container) {
var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0;
function d3_mousePoint(container, e) {
+ if (e.changedTouches) e = e.changedTouches[0];
var svg = container.ownerSVGElement || container;
if (svg.createSVGPoint) {
var point = svg.createSVGPoint();
@@ -1594,13 +1627,8 @@ function d3_mousePoint(container, e) {
d3_mouse_bug44083 = !(ctm.f || ctm.e);
svg.remove();
}
- if (d3_mouse_bug44083) {
- point.x = e.pageX;
- point.y = e.pageY;
- } else {
- point.x = e.clientX;
- point.y = e.clientY;
- }
+ if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY;
+ else point.x = e.clientX, point.y = e.clientY;
point = point.matrixTransform(container.getScreenCTM().inverse());
return [point.x, point.y];
}
@@ -1616,47 +1644,180 @@ d3.touches = function(container, touches) {
return point;
}) : [];
};
+var Ï = Math.PI,
+ Ï = 2 * Ï,
+ halfÏ = Ï / 2,
+ ε = 1e-6,
+ ε2 = ε * ε,
+ d3_radians = Ï / 180,
+ d3_degrees = 180 / Ï;
+
+function d3_sgn(x) {
+ return x > 0 ? 1 : x < 0 ? -1 : 0;
+}
+
+function d3_acos(x) {
+ return x > 1 ? 0 : x < -1 ? Ï : Math.acos(x);
+}
+
+function d3_asin(x) {
+ return x > 1 ? halfÏ : x < -1 ? -halfÏ : Math.asin(x);
+}
+
+function d3_sinh(x) {
+ return ((x = Math.exp(x)) - 1 / x) / 2;
+}
+
+function d3_cosh(x) {
+ return ((x = Math.exp(x)) + 1 / x) / 2;
+}
+
+function d3_tanh(x) {
+ return ((x = Math.exp(2 * x)) - 1) / (x + 1);
+}
+
+function d3_haversin(x) {
+ return (x = Math.sin(x / 2)) * x;
+}
+
+var Ï = Math.SQRT2,
+ Ï2 = 2,
+ Ï4 = 4;
+
+// p0 = [ux0, uy0, w0]
+// p1 = [ux1, uy1, w1]
+d3.interpolateZoom = function(p0, p1) {
+ var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
+ ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
+
+ var dx = ux1 - ux0,
+ dy = uy1 - uy0,
+ d2 = dx * dx + dy * dy,
+ d1 = Math.sqrt(d2),
+ b0 = (w1 * w1 - w0 * w0 + Ï4 * d2) / (2 * w0 * Ï2 * d1),
+ b1 = (w1 * w1 - w0 * w0 - Ï4 * d2) / (2 * w1 * Ï2 * d1),
+ r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
+ r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1),
+ dr = r1 - r0,
+ S = (dr || Math.log(w1 / w0)) / Ï;
+
+ function interpolate(t) {
+ var s = t * S;
+ if (dr) {
+ // General case.
+ var coshr0 = d3_cosh(r0),
+ u = w0 / (Ï2 * d1) * (coshr0 * d3_tanh(Ï * s + r0) - d3_sinh(r0));
+ return [
+ ux0 + u * dx,
+ uy0 + u * dy,
+ w0 * coshr0 / d3_cosh(Ï * s + r0)
+ ];
+ }
+ // Special case for u0 ~= u1.
+ return [
+ ux0 + t * dx,
+ uy0 + t * dy,
+ w0 * Math.exp(Ï * s)
+ ];
+ }
+
+ interpolate.duration = S * 1000;
+
+ return interpolate;
+};
d3.behavior.zoom = function() {
- var translate = [0, 0],
+ var view = {x: 0, y: 0, k: 1},
translate0, // translate when we started zooming (to avoid drift)
- scale = 1,
+ center, // desired position of translate0 after zooming
+ size = [960, 500], // viewport size; required for zoom interpolation
scaleExtent = d3_behavior_zoomInfinity,
mousedown = "mousedown.zoom",
mousemove = "mousemove.zoom",
mouseup = "mouseup.zoom",
- event = d3_eventDispatch(zoom, "zoom"),
+ mousewheelTimer,
+ touchstart = "touchstart.zoom",
+ touchtime, // time of last touchstart (to detect double-tap)
+ event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
x0,
x1,
y0,
- y1,
- touchtime; // time of last touchstart (to detect double-tap)
+ y1;
- function zoom() {
- this.on(mousedown, mousedowned)
+ function zoom(g) {
+ g .on(mousedown, mousedowned)
.on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
.on(mousemove, mousewheelreset)
.on("dblclick.zoom", dblclicked)
- .on("touchstart.zoom", touchstarted);
+ .on(touchstart, touchstarted);
}
- zoom.translate = function(x) {
- if (!arguments.length) return translate;
- translate = x.map(Number);
+ zoom.event = function(g) {
+ g.each(function() {
+ var event_ = event.of(this, arguments),
+ view1 = view;
+ if (d3_transitionInheritId) {
+ d3.select(this).transition()
+ .each("start.zoom", function() {
+ view = this.__chart__ || {x: 0, y: 0, k: 1}; // pre-transition state
+ zoomstarted(event_);
+ })
+ .tween("zoom:zoom", function() {
+ var dx = size[0],
+ dy = size[1],
+ cx = dx / 2,
+ cy = dy / 2,
+ i = d3.interpolateZoom(
+ [(cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k],
+ [(cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k]
+ );
+ return function(t) {
+ var l = i(t), k = dx / l[2];
+ this.__chart__ = view = {x: cx - l[0] * k, y: cy - l[1] * k, k: k};
+ zoomed(event_);
+ };
+ })
+ .each("end.zoom", function() {
+ zoomended(event_);
+ });
+ } else {
+ this.__chart__ = view;
+ zoomstarted(event_);
+ zoomed(event_);
+ zoomended(event_);
+ }
+ });
+ }
+
+ zoom.translate = function(_) {
+ if (!arguments.length) return [view.x, view.y];
+ view = {x: +_[0], y: +_[1], k: view.k}; // copy-on-write
rescale();
return zoom;
};
- zoom.scale = function(x) {
- if (!arguments.length) return scale;
- scale = +x;
+ zoom.scale = function(_) {
+ if (!arguments.length) return view.k;
+ view = {x: view.x, y: view.y, k: +_}; // copy-on-write
rescale();
return zoom;
};
- zoom.scaleExtent = function(x) {
+ zoom.scaleExtent = function(_) {
if (!arguments.length) return scaleExtent;
- scaleExtent = x == null ? d3_behavior_zoomInfinity : x.map(Number);
+ scaleExtent = _ == null ? d3_behavior_zoomInfinity : [+_[0], +_[1]];
+ return zoom;
+ };
+
+ zoom.center = function(_) {
+ if (!arguments.length) return center;
+ center = _ && [+_[0], +_[1]];
+ return zoom;
+ };
+
+ zoom.size = function(_) {
+ if (!arguments.length) return size;
+ size = _ && [+_[0], +_[1]];
return zoom;
};
@@ -1664,8 +1825,7 @@ d3.behavior.zoom = function() {
if (!arguments.length) return x1;
x1 = z;
x0 = z.copy();
- translate = [0, 0];
- scale = 1;
+ view = {x: 0, y: 0, k: 1}; // copy-on-write
return zoom;
};
@@ -1673,37 +1833,44 @@ d3.behavior.zoom = function() {
if (!arguments.length) return y1;
y1 = z;
y0 = z.copy();
- translate = [0, 0];
- scale = 1;
+ view = {x: 0, y: 0, k: 1}; // copy-on-write
return zoom;
};
function location(p) {
- return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
+ return [(p[0] - view.x) / view.k, (p[1] - view.y) / view.k];
}
function point(l) {
- return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
+ return [l[0] * view.k + view.x, l[1] * view.k + view.y];
}
function scaleTo(s) {
- scale = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
+ view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
}
function translateTo(p, l) {
l = point(l);
- translate[0] += p[0] - l[0];
- translate[1] += p[1] - l[1];
+ view.x += p[0] - l[0];
+ view.y += p[1] - l[1];
}
function rescale() {
- if (x1) x1.domain(x0.range().map(function(x) { return (x - translate[0]) / scale; }).map(x0.invert));
- if (y1) y1.domain(y0.range().map(function(y) { return (y - translate[1]) / scale; }).map(y0.invert));
+ if (x1) x1.domain(x0.range().map(function(x) { return (x - view.x) / view.k; }).map(x0.invert));
+ if (y1) y1.domain(y0.range().map(function(y) { return (y - view.y) / view.k; }).map(y0.invert));
+ }
+
+ function zoomstarted(event) {
+ event({type: "zoomstart"});
}
- function dispatch(event) {
+ function zoomed(event) {
rescale();
- event({type: "zoom", scale: scale, translate: translate});
+ event({type: "zoom", scale: view.k, translate: [view.x, view.y]});
+ }
+
+ function zoomended(event) {
+ event({type: "zoomend"});
}
function mousedowned() {
@@ -1715,62 +1882,92 @@ d3.behavior.zoom = function() {
l = location(d3.mouse(target)),
dragRestore = d3_event_dragSuppress();
+ d3_selection_interrupt.call(target);
+ zoomstarted(event_);
+
function moved() {
dragged = 1;
translateTo(d3.mouse(target), l);
- dispatch(event_);
+ zoomed(event_);
}
function ended() {
w.on(mousemove, d3_window === target ? mousewheelreset : null).on(mouseup, null);
dragRestore(dragged && d3.event.target === eventTarget);
+ zoomended(event_);
}
}
+ // These closures persist for as long as at least one touch is active.
function touchstarted() {
var target = this,
event_ = event.of(target, arguments),
- touches = d3.touches(target),
- locations = {},
+ locations0 = {}, // touchstart locations
distance0 = 0, // distance² between initial touches
- scale0 = scale, // scale when we started touching
- now = Date.now(),
- name = "zoom-" + d3.event.changedTouches[0].identifier,
- touchmove = "touchmove." + name,
- touchend = "touchend." + name,
+ scale0, // scale when we started touching
+ eventId = d3.event.changedTouches[0].identifier,
+ touchmove = "touchmove.zoom-" + eventId,
+ touchend = "touchend.zoom-" + eventId,
w = d3.select(d3_window).on(touchmove, moved).on(touchend, ended),
- t = d3.select(target).on(mousedown, null), // prevent duplicate events
+ t = d3.select(target).on(mousedown, null).on(touchstart, started), // prevent duplicate events
dragRestore = d3_event_dragSuppress();
- touches.forEach(function(t) { locations[t.identifier] = location(t); });
+ d3_selection_interrupt.call(target);
+ started();
+ zoomstarted(event_);
+
+ // Updates locations of any touches in locations0.
+ function relocate() {
+ var touches = d3.touches(target);
+ scale0 = view.k;
+ touches.forEach(function(t) {
+ if (t.identifier in locations0) locations0[t.identifier] = location(t);
+ });
+ return touches;
+ }
- if (touches.length === 1) {
- if (now - touchtime < 500) { // dbltap
- var p = touches[0], l = location(touches[0]);
- scaleTo(scale * 2);
- translateTo(p, l);
- d3_eventPreventDefault();
- dispatch(event_);
+ // Temporarily override touchstart while gesture is active.
+ function started() {
+ // Only track touches started on the target element.
+ var changed = d3.event.changedTouches;
+ for (var i = 0, n = changed.length; i < n; ++i) {
+ locations0[changed[i].identifier] = null;
+ }
+
+ var touches = relocate(),
+ now = Date.now();
+
+ if (touches.length === 1) {
+ if (now - touchtime < 500) { // dbltap
+ var p = touches[0], l = locations0[p.identifier];
+ scaleTo(view.k * 2);
+ translateTo(p, l);
+ d3_eventPreventDefault();
+ zoomed(event_);
+ }
+ touchtime = now;
+ } else if (touches.length > 1) {
+ var p = touches[0], q = touches[1],
+ dx = p[0] - q[0], dy = p[1] - q[1];
+ distance0 = dx * dx + dy * dy;
}
- touchtime = now;
- } else if (touches.length > 1) {
- var p = touches[0], q = touches[1],
- dx = p[0] - q[0], dy = p[1] - q[1];
- distance0 = dx * dx + dy * dy;
}
function moved() {
var touches = d3.touches(target),
- p0 = touches[0],
- l0 = locations[p0.identifier];
-
- if (p1 = touches[1]) {
- var p1, l1 = locations[p1.identifier],
- scale1 = d3.event.scale;
- if (scale1 == null) {
- var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1;
- scale1 = distance0 && Math.sqrt(distance1 / distance0);
+ p0, l0,
+ p1, l1;
+ for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
+ p1 = touches[i];
+ if (l1 = locations0[p1.identifier]) {
+ if (l0) break;
+ p0 = p1, l0 = l1;
}
+ }
+
+ if (l1) {
+ var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1,
+ scale1 = distance0 && Math.sqrt(distance1 / distance0);
p0 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
l0 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
scaleTo(scale1 * scale0);
@@ -1778,22 +1975,42 @@ d3.behavior.zoom = function() {
touchtime = null;
translateTo(p0, l0);
- dispatch(event_);
+ zoomed(event_);
}
function ended() {
+ // If there are any globally-active touches remaining, remove the ended
+ // touches from locations0.
+ if (d3.event.touches.length) {
+ var changed = d3.event.changedTouches;
+ for (var i = 0, n = changed.length; i < n; ++i) {
+ delete locations0[changed[i].identifier];
+ }
+ // If locations0 is not empty, then relocate and continue listening for
+ // touchmove and touchend.
+ for (var identifier in locations0) {
+ return void relocate(); // locations may have detached due to rotation
+ }
+ }
+ // Otherwise, remove touchmove and touchend listeners.
w.on(touchmove, null).on(touchend, null);
- t.on(mousedown, mousedowned);
+ t.on(mousedown, mousedowned).on(touchstart, touchstarted);
dragRestore();
+ zoomended(event_);
}
}
function mousewheeled() {
+ var event_ = event.of(this, arguments);
+ if (mousewheelTimer) clearTimeout(mousewheelTimer);
+ else d3_selection_interrupt.call(this), zoomstarted(event_);
+ mousewheelTimer = setTimeout(function() { mousewheelTimer = null; zoomended(event_); }, 50);
d3_eventPreventDefault();
- if (!translate0) translate0 = location(d3.mouse(this));
- scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale);
- translateTo(d3.mouse(this), translate0);
- dispatch(event.of(this, arguments));
+ var point = center || d3.mouse(this);
+ if (!translate0) translate0 = location(point);
+ scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
+ translateTo(point, translate0);
+ zoomed(event_);
}
function mousewheelreset() {
@@ -1801,10 +2018,15 @@ d3.behavior.zoom = function() {
}
function dblclicked() {
- var p = d3.mouse(this), l = location(p), k = Math.log(scale) / Math.LN2;
+ var event_ = event.of(this, arguments),
+ p = d3.mouse(this),
+ l = location(p),
+ k = Math.log(view.k) / Math.LN2;
+ zoomstarted(event_);
scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1));
translateTo(p, l);
- dispatch(event.of(this, arguments));
+ zoomed(event_);
+ zoomended(event_);
}
return d3.rebind(zoom, event, "on");
@@ -1837,8 +2059,8 @@ d3.timer = function(callback, delay, then) {
if (n < 3) then = Date.now();
// Add the callback to the tail of the queue.
- var time = then + delay, timer = {callback: callback, time: time, next: null};
- if (d3_timer_queueTail) d3_timer_queueTail.next = timer;
+ var time = then + delay, timer = {c: callback, t: time, f: false, n: null};
+ if (d3_timer_queueTail) d3_timer_queueTail.n = timer;
else d3_timer_queueHead = timer;
d3_timer_queueTail = timer;
@@ -1870,20 +2092,12 @@ d3.timer.flush = function() {
d3_timer_sweep();
};
-function d3_timer_replace(callback, delay, then) {
- var n = arguments.length;
- if (n < 2) delay = 0;
- if (n < 3) then = Date.now();
- d3_timer_active.callback = callback;
- d3_timer_active.time = then + delay;
-}
-
function d3_timer_mark() {
var now = Date.now();
d3_timer_active = d3_timer_queueHead;
while (d3_timer_active) {
- if (now >= d3_timer_active.time) d3_timer_active.flush = d3_timer_active.callback(now - d3_timer_active.time);
- d3_timer_active = d3_timer_active.next;
+ if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
+ d3_timer_active = d3_timer_active.n;
}
return now;
}
@@ -1895,45 +2109,16 @@ function d3_timer_sweep() {
t1 = d3_timer_queueHead,
time = Infinity;
while (t1) {
- if (t1.flush) {
- t1 = t0 ? t0.next = t1.next : d3_timer_queueHead = t1.next;
+ if (t1.f) {
+ t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
} else {
- if (t1.time < time) time = t1.time;
- t1 = (t0 = t1).next;
+ if (t1.t < time) time = t1.t;
+ t1 = (t0 = t1).n;
}
}
d3_timer_queueTail = t0;
return time;
}
-var Ï = Math.PI,
- ε = 1e-6,
- ε2 = ε * ε,
- d3_radians = Ï / 180,
- d3_degrees = 180 / Ï;
-
-function d3_sgn(x) {
- return x > 0 ? 1 : x < 0 ? -1 : 0;
-}
-
-function d3_acos(x) {
- return x > 1 ? 0 : x < -1 ? Ï : Math.acos(x);
-}
-
-function d3_asin(x) {
- return x > 1 ? Ï / 2 : x < -1 ? -Ï / 2 : Math.asin(x);
-}
-
-function d3_sinh(x) {
- return (Math.exp(x) - Math.exp(-x)) / 2;
-}
-
-function d3_cosh(x) {
- return (Math.exp(x) + Math.exp(-x)) / 2;
-}
-
-function d3_haversin(x) {
- return (x = Math.sin(x / 2)) * x;
-}
d3.geo = {};
function d3_identity(d) {
return d;
@@ -1950,13 +2135,13 @@ function d3_geo_spherical(cartesian) {
}
function d3_geo_sphericalEqual(a, b) {
- return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε;
+ return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
}
// General spherical polygon clipping algorithm: takes a polygon, cuts it into
// visible line segments and rejoins the segments by interpolating along the
// clip edge.
-function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) {
+function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
var subject = [],
clip = [];
@@ -1975,14 +2160,14 @@ function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) {
return;
}
- var a = {point: p0, points: segment, other: null, visited: false, entry: true, subject: true},
- b = {point: p0, points: [p0], other: a, visited: false, entry: false, subject: false};
- a.other = b;
+ var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
+ b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
+ a.o = b;
subject.push(a);
clip.push(b);
- a = {point: p1, points: [p1], other: null, visited: false, entry: false, subject: true};
- b = {point: p1, points: [p1], other: a, visited: false, entry: true, subject: false};
- a.other = b;
+ a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
+ b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
+ a.o = b;
subject.push(a);
clip.push(b);
});
@@ -1991,41 +2176,42 @@ function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) {
d3_geo_clipPolygonLinkCircular(clip);
if (!subject.length) return;
- if (inside) for (var i = 1, e = !inside(clip[0].point), n = clip.length; i < n; ++i) {
- clip[i].entry = (e = !e);
+ for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
+ clip[i].e = entry = !entry;
}
var start = subject[0],
- current,
points,
point;
while (1) {
// Find first unvisited intersection.
- current = start;
- while (current.visited) if ((current = current.next) === start) return;
- points = current.points;
+ var current = start,
+ isSubject = true;
+ while (current.v) if ((current = current.n) === start) return;
+ points = current.z;
listener.lineStart();
do {
- current.visited = current.other.visited = true;
- if (current.entry) {
- if (current.subject) {
- for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]);
+ current.v = current.o.v = true;
+ if (current.e) {
+ if (isSubject) {
+ for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
} else {
- interpolate(current.point, current.next.point, 1, listener);
+ interpolate(current.x, current.n.x, 1, listener);
}
- current = current.next;
+ current = current.n;
} else {
- if (current.subject) {
- points = current.prev.points;
- for (var i = points.length; --i >= 0;) listener.point((point = points[i])[0], point[1]);
+ if (isSubject) {
+ points = current.p.z;
+ for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
} else {
- interpolate(current.point, current.prev.point, -1, listener);
+ interpolate(current.x, current.p.x, -1, listener);
}
- current = current.prev;
+ current = current.p;
}
- current = current.other;
- points = current.points;
- } while (!current.visited);
+ current = current.o;
+ points = current.z;
+ isSubject = !isSubject;
+ } while (!current.v);
listener.lineEnd();
}
}
@@ -2037,17 +2223,27 @@ function d3_geo_clipPolygonLinkCircular(array) {
a = array[0],
b;
while (++i < n) {
- a.next = b = array[i];
- b.prev = a;
+ a.n = b = array[i];
+ b.p = a;
a = b;
}
- a.next = b = array[0];
- b.prev = a;
+ a.n = b = array[0];
+ b.p = a;
}
-function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
- return function(listener) {
- var line = clipLine(listener);
+function d3_geo_clipPolygonIntersection(point, points, other, entry) {
+ this.x = point;
+ this.z = points;
+ this.o = other; // another intersection
+ this.e = entry; // is an entry?
+ this.v = false; // visited
+ this.n = this.p = null; // next & previous
+}
+
+function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
+ return function(rotate, listener) {
+ var line = clipLine(listener),
+ rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
var clip = {
point: point,
@@ -2067,9 +2263,10 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
clip.lineEnd = lineEnd;
segments = d3.merge(segments);
+ var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
if (segments.length) {
- d3_geo_clipPolygon(segments, d3_geo_clipSort, null, interpolate, listener);
- } else if (polygonContains(polygon)) {
+ d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
+ } else if (clipStartInside) {
listener.lineStart();
interpolate(null, null, 1, listener);
listener.lineEnd();
@@ -2086,8 +2283,14 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
}
};
- function point(λ, Ï) { if (pointVisible(λ, Ï)) listener.point(λ, Ï); }
- function pointLine(λ, Ï) { line.point(λ, Ï); }
+ function point(λ, Ï) {
+ var point = rotate(λ, Ï);
+ if (pointVisible(λ = point[0], Ï = point[1])) listener.point(λ, Ï);
+ }
+ function pointLine(λ, Ï) {
+ var point = rotate(λ, Ï);
+ line.point(point[0], point[1]);
+ }
function lineStart() { clip.point = pointLine; line.lineStart(); }
function lineEnd() { clip.point = point; line.lineEnd(); }
@@ -2099,8 +2302,9 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
ring;
function pointRing(λ, Ï) {
- ringListener.point(λ, Ï);
ring.push([λ, Ï]);
+ var point = rotate(λ, Ï);
+ ringListener.point(point[0], point[1]);
}
function ringStart() {
@@ -2172,8 +2376,8 @@ function d3_geo_clipBufferListener() {
// Intersection points are sorted along the clip edge. For both antimeridian
// cutting and circle clipping, the same comparison is used.
function d3_geo_clipSort(a, b) {
- return ((a = a.point)[0] < 0 ? a[1] - Ï / 2 - ε : Ï / 2 - a[1])
- - ((b = b.point)[0] < 0 ? b[1] - Ï / 2 - ε : Ï / 2 - b[1]);
+ return ((a = a.x)[0] < 0 ? a[1] - halfÏ - ε : halfÏ - a[1])
+ - ((b = b.x)[0] < 0 ? b[1] - halfÏ - ε : halfÏ - b[1]);
}
// Adds floating point numbers with twice the normal precision.
// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
@@ -2239,12 +2443,12 @@ var d3_geo_streamGeometryType = {
listener.sphere();
},
Point: function(object, listener) {
- var coordinate = object.coordinates;
- listener.point(coordinate[0], coordinate[1]);
+ object = object.coordinates;
+ listener.point(object[0], object[1], object[2]);
},
MultiPoint: function(object, listener) {
- var coordinates = object.coordinates, i = -1, n = coordinates.length, coordinate;
- while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]);
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
},
LineString: function(object, listener) {
d3_geo_streamLine(object.coordinates, listener, 0);
@@ -2269,7 +2473,7 @@ var d3_geo_streamGeometryType = {
function d3_geo_streamLine(coordinates, listener, closed) {
var i = -1, n = coordinates.length - closed, coordinate;
listener.lineStart();
- while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]);
+ while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
listener.lineEnd();
}
@@ -2394,8 +2598,6 @@ function d3_geo_pointInPolygon(point, polygon) {
parallel = point[1],
meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0],
polarAngle = 0,
- polar = false,
- southPole = false,
winding = 0;
d3_geo_areaRingSum.reset();
@@ -2418,12 +2620,11 @@ function d3_geo_pointInPolygon(point, polygon) {
sinÏ = Math.sin(Ï),
cosÏ = Math.cos(Ï),
dλ = λ - λ0,
- antimeridian = Math.abs(dλ) > Ï,
+ antimeridian = abs(dλ) > Ï,
k = sinÏ0 * sinÏ;
d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosÏ0 * cosÏ + k * Math.cos(dλ)));
- if (Math.abs(Ï) < ε) southPole = true;
- polarAngle += antimeridian ? dλ + (dλ >= 0 ? 2 : -2) * Ï : dλ;
+ polarAngle += antimeridian ? dλ + (dλ >= 0 ? Ï : -Ï): dλ;
// Are the longitudes either side of the point's meridian, and are the
// latitudes smaller than the parallel?
@@ -2433,34 +2634,34 @@ function d3_geo_pointInPolygon(point, polygon) {
var intersection = d3_geo_cartesianCross(meridianNormal, arc);
d3_geo_cartesianNormalize(intersection);
var Ïarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
- if (parallel > Ïarc) {
+ if (parallel > Ïarc || parallel === Ïarc && (arc[0] || arc[1])) {
winding += antimeridian ^ dλ >= 0 ? 1 : -1;
}
}
if (!j++) break;
λ0 = λ, sinÏ0 = sinÏ, cosÏ0 = cosÏ, point0 = point;
}
- if (Math.abs(polarAngle) > ε) polar = true;
}
// First, determine whether the South pole is inside or outside:
//
// It is inside if:
- // * the polygon doesn't wind around it, and its area is negative (counter-clockwise).
- // * otherwise, if the polygon winds around it in a clockwise direction.
+ // * the polygon winds around it in a clockwise direction.
+ // * the polygon does not (cumulatively) wind around it, but has a negative
+ // (counter-clockwise) area.
//
// Second, count the (signed) number of times a segment crosses a meridian
// from the point to the South pole. If it is zero, then the point is the
// same side as the South pole.
- return (!southPole && !polar && d3_geo_areaRingSum < 0 || polarAngle < -ε) ^ (winding & 1);
+ return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ (winding & 1);
}
var d3_geo_clipAntimeridian = d3_geo_clip(
d3_true,
d3_geo_clipAntimeridianLine,
d3_geo_clipAntimeridianInterpolate,
- d3_geo_clipAntimeridianPolygonContains);
+ [-Ï, -Ï / 2]);
// Takes a line and cuts into visible segments. Return values:
// 0: there were intersections or the line was empty.
@@ -2480,9 +2681,9 @@ function d3_geo_clipAntimeridianLine(listener) {
},
point: function(λ1, Ï1) {
var sλ1 = λ1 > 0 ? Ï : -Ï,
- dλ = Math.abs(λ1 - λ0);
- if (Math.abs(dλ - Ï) < ε) { // line crosses a pole
- listener.point(λ0, Ï0 = (Ï0 + Ï1) / 2 > 0 ? Ï / 2 : -Ï / 2);
+ dλ = abs(λ1 - λ0);
+ if (abs(dλ - Ï) < ε) { // line crosses a pole
+ listener.point(λ0, Ï0 = (Ï0 + Ï1) / 2 > 0 ? halfÏ : -halfÏ);
listener.point(sλ0, Ï0);
listener.lineEnd();
listener.lineStart();
@@ -2491,8 +2692,8 @@ function d3_geo_clipAntimeridianLine(listener) {
clean = 0;
} else if (sλ0 !== sλ1 && dλ >= Ï) { // line crosses antimeridian
// handle degeneracies
- if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
- if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
+ if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
+ if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
Ï0 = d3_geo_clipAntimeridianIntersect(λ0, Ï0, λ1, Ï1);
listener.point(sλ0, Ï0);
listener.lineEnd();
@@ -2516,7 +2717,7 @@ function d3_geo_clipAntimeridianIntersect(λ0, Ï0, λ1, Ï1) {
var cosÏ0,
cosÏ1,
sinλ0_λ1 = Math.sin(λ0 - λ1);
- return Math.abs(sinλ0_λ1) > ε
+ return abs(sinλ0_λ1) > ε
? Math.atan((Math.sin(Ï0) * (cosÏ1 = Math.cos(Ï1)) * Math.sin(λ1)
- Math.sin(Ï1) * (cosÏ0 = Math.cos(Ï0)) * Math.sin(λ0))
/ (cosÏ0 * cosÏ1 * sinλ0_λ1))
@@ -2526,7 +2727,7 @@ function d3_geo_clipAntimeridianIntersect(λ0, Ï0, λ1, Ï1) {
function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
var Ï;
if (from == null) {
- Ï = direction * Ï / 2;
+ Ï = direction * halfÏ;
listener.point(-Ï, Ï);
listener.point( 0, Ï);
listener.point( Ï, Ï);
@@ -2536,8 +2737,8 @@ function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
listener.point(-Ï, -Ï);
listener.point(-Ï, 0);
listener.point(-Ï, Ï);
- } else if (Math.abs(from[0] - to[0]) > ε) {
- var s = (from[0] < to[0] ? 1 : -1) * Ï;
+ } else if (abs(from[0] - to[0]) > ε) {
+ var s = from[0] < to[0] ? Ï : -Ï;
Ï = direction * s / 2;
listener.point(-s, Ï);
listener.point( 0, Ï);
@@ -2547,12 +2748,6 @@ function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
}
}
-var d3_geo_clipAntimeridianPoint = [-Ï, 0];
-
-function d3_geo_clipAntimeridianPolygonContains(polygon) {
- return d3_geo_pointInPolygon(d3_geo_clipAntimeridianPoint, polygon);
-}
-
function d3_geo_equirectangular(λ, Ï) {
return [λ, Ï];
}
@@ -2577,17 +2772,23 @@ d3.geo.rotation = function(rotate) {
return forward;
};
+function d3_geo_identityRotation(λ, Ï) {
+ return [λ > Ï ? λ - Ï : λ < -Ï ? λ + Ï : λ, Ï];
+}
+
+d3_geo_identityRotation.invert = d3_geo_equirectangular;
+
// Note: |δλ| must be < 2Ï
function d3_geo_rotation(δλ, δÏ, δγ) {
return δλ ? (Î´Ï || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationÏγ(δÏ, δγ))
: d3_geo_rotationλ(δλ))
: (Î´Ï || δγ ? d3_geo_rotationÏγ(δÏ, δγ)
- : d3_geo_equirectangular);
+ : d3_geo_identityRotation);
}
function d3_geo_forwardRotationλ(δλ) {
return function(λ, Ï) {
- return λ += δλ, [λ > Ï ? λ - 2 * Ï : λ < -Ï ? λ + 2 * Ï : λ, Ï];
+ return λ += δλ, [λ > Ï ? λ - Ï : λ < -Ï ? λ + Ï : λ, Ï];
};
}
@@ -2678,16 +2879,16 @@ function d3_geo_circleInterpolate(radius, precision) {
var cr = Math.cos(radius),
sr = Math.sin(radius);
return function(from, to, direction, listener) {
+ var step = direction * precision;
if (from != null) {
from = d3_geo_circleAngle(cr, from);
to = d3_geo_circleAngle(cr, to);
- if (direction > 0 ? from < to: from > to) from += direction * 2 * Ï;
+ if (direction > 0 ? from < to: from > to) from += direction * Ï;
} else {
- from = radius + direction * 2 * Ï;
- to = radius;
+ from = radius + direction * Ï;
+ to = radius - .5 * step;
}
- var point;
- for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) {
+ for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
listener.point((point = d3_geo_spherical([
cr,
-sr * Math.cos(t),
@@ -2710,11 +2911,10 @@ function d3_geo_circleAngle(cr, point) {
function d3_geo_clipCircle(radius) {
var cr = Math.cos(radius),
smallRadius = cr > 0,
- point = [radius, 0],
- notHemisphere = Math.abs(cr) > ε, // TODO optimise for this common case
+ notHemisphere = abs(cr) > ε, // TODO optimise for this common case
interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
- return d3_geo_clip(visible, clipLine, interpolate, polygonContains);
+ return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-Ï, radius - Ï]);
function visible(λ, Ï) {
return Math.cos(λ) * Math.cos(Ï) > cr;
@@ -2848,7 +3048,7 @@ function d3_geo_clipCircle(radius) {
z;
if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
var δλ = λ1 - λ0,
- polar = Math.abs(δλ - Ï) < ε,
+ polar = abs(δλ - Ï) < ε,
meridian = polar || δλ < ε;
if (!polar && Ï1 < Ï0) z = Ï0, Ï0 = Ï1, Ï1 = z;
@@ -2856,7 +3056,7 @@ function d3_geo_clipCircle(radius) {
// Check that the first point is between a and b.
if (meridian
? polar
- ? Ï0 + Ï1 > 0 ^ q[1] < (Math.abs(q[0] - λ0) < ε ? Ï0 : Ï1)
+ ? Ï0 + Ï1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? Ï0 : Ï1)
: Ï0 <= q[1] && q[1] <= Ï1
: δλ > Ï ^ (λ0 <= q[0] && q[0] <= λ1)) {
var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
@@ -2876,18 +3076,101 @@ function d3_geo_clipCircle(radius) {
else if (Ï > r) code |= 8; // above
return code;
}
+}
- function polygonContains(polygon) {
- return d3_geo_pointInPolygon(point, polygon);
- }
+// LiangâBarsky line clipping.
+function d3_geom_clipLine(x0, y0, x1, y1) {
+ return function(line) {
+ var a = line.a,
+ b = line.b,
+ ax = a.x,
+ ay = a.y,
+ bx = b.x,
+ by = b.y,
+ t0 = 0,
+ t1 = 1,
+ dx = bx - ax,
+ dy = by - ay,
+ r;
+
+ r = x0 - ax;
+ if (!dx && r > 0) return;
+ r /= dx;
+ if (dx < 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ } else if (dx > 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ }
+
+ r = x1 - ax;
+ if (!dx && r < 0) return;
+ r /= dx;
+ if (dx < 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ } else if (dx > 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ }
+
+ r = y0 - ay;
+ if (!dy && r > 0) return;
+ r /= dy;
+ if (dy < 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ } else if (dy > 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ }
+
+ r = y1 - ay;
+ if (!dy && r < 0) return;
+ r /= dy;
+ if (dy < 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ } else if (dy > 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ }
+
+ if (t0 > 0) line.a = {x: ax + t0 * dx, y: ay + t0 * dy};
+ if (t1 < 1) line.b = {x: ax + t1 * dx, y: ay + t1 * dy};
+ return line;
+ };
}
-var d3_geo_clipViewMAX = 1e9;
+var d3_geo_clipExtentMAX = 1e9;
+
+d3.geo.clipExtent = function() {
+ var x0, y0, x1, y1,
+ stream,
+ clip,
+ clipExtent = {
+ stream: function(output) {
+ if (stream) stream.valid = false;
+ stream = clip(output);
+ stream.valid = true; // allow caching by d3.geo.path
+ return stream;
+ },
+ extent: function(_) {
+ if (!arguments.length) return [[x0, y0], [x1, y1]];
+ clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
+ if (stream) stream.valid = false, stream = null;
+ return clipExtent;
+ }
+ };
+ return clipExtent.extent([[0, 0], [960, 500]]);
+};
-function d3_geo_clipView(x0, y0, x1, y1) {
+function d3_geo_clipExtent(x0, y0, x1, y1) {
return function(listener) {
var listener_ = listener,
bufferListener = d3_geo_clipBufferListener(),
+ clipLine = d3_geom_clipLine(x0, y0, x1, y1),
segments,
polygon,
ring;
@@ -2900,28 +3183,30 @@ function d3_geo_clipView(x0, y0, x1, y1) {
listener = bufferListener;
segments = [];
polygon = [];
+ clean = true;
},
polygonEnd: function() {
listener = listener_;
- if ((segments = d3.merge(segments)).length) {
+ segments = d3.merge(segments);
+ var clipStartInside = insidePolygon([x0, y1]),
+ inside = clean && clipStartInside,
+ visible = segments.length;
+ if (inside || visible) {
listener.polygonStart();
- d3_geo_clipPolygon(segments, compare, inside, interpolate, listener);
+ if (inside) {
+ listener.lineStart();
+ interpolate(null, null, 1, listener);
+ listener.lineEnd();
+ }
+ if (visible) {
+ d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
+ }
listener.polygonEnd();
- } else if (insidePolygon([x0, y0])) {
- listener.polygonStart(), listener.lineStart();
- interpolate(null, null, 1, listener);
- listener.lineEnd(), listener.polygonEnd();
}
segments = polygon = ring = null;
}
};
- function inside(point) {
- var a = corner(point, -1),
- i = insidePolygon([a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0]);
- return i;
- }
-
function insidePolygon(p) {
var wn = 0, // the winding number counter
n = polygon.length,
@@ -2958,17 +3243,18 @@ function d3_geo_clipView(x0, y0, x1, y1) {
}
}
- function visible(x, y) {
+ function pointVisible(x, y) {
return x0 <= x && x <= x1 && y0 <= y && y <= y1;
}
function point(x, y) {
- if (visible(x, y)) listener.point(x, y);
+ if (pointVisible(x, y)) listener.point(x, y);
}
var x__, y__, v__, // first point
x_, y_, v_, // previous point
- first;
+ first,
+ clean;
function lineStart() {
clip.point = linePoint;
@@ -2992,9 +3278,9 @@ function d3_geo_clipView(x0, y0, x1, y1) {
}
function linePoint(x, y) {
- x = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, x));
- y = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, y));
- var v = visible(x, y);
+ x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
+ y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
+ var v = pointVisible(x, y);
if (polygon) ring.push([x, y]);
if (first) {
x__ = x, y__ = y, v__ = v;
@@ -3006,18 +3292,19 @@ function d3_geo_clipView(x0, y0, x1, y1) {
} else {
if (v && v_) listener.point(x, y);
else {
- var a = [x_, y_],
- b = [x, y];
- if (clipLine(a, b)) {
+ var l = {a: {x: x_, y: y_}, b: {x: x, y: y}};
+ if (clipLine(l)) {
if (!v_) {
listener.lineStart();
- listener.point(a[0], a[1]);
+ listener.point(l.a.x, l.a.y);
}
- listener.point(b[0], b[1]);
+ listener.point(l.b.x, l.b.y);
if (!v) listener.lineEnd();
+ clean = false;
} else if (v) {
listener.lineStart();
listener.point(x, y);
+ clean = false;
}
}
}
@@ -3028,14 +3315,14 @@ function d3_geo_clipView(x0, y0, x1, y1) {
};
function corner(p, direction) {
- return Math.abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3
- : Math.abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1
- : Math.abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0
- : direction > 0 ? 3 : 2; // Math.abs(p[1] - y1) < ε
+ return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3
+ : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1
+ : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0
+ : direction > 0 ? 3 : 2; // abs(p[1] - y1) < ε
}
function compare(a, b) {
- return comparePoints(a.point, b.point);
+ return comparePoints(a.x, b.x);
}
function comparePoints(a, b) {
@@ -3047,47 +3334,6 @@ function d3_geo_clipView(x0, y0, x1, y1) {
: ca === 2 ? a[1] - b[1]
: b[0] - a[0];
}
-
- // LiangâBarsky line clipping.
- function clipLine(a, b) {
- var dx = b[0] - a[0],
- dy = b[1] - a[1],
- t = [0, 1];
-
- if (Math.abs(dx) < ε && Math.abs(dy) < ε) return x0 <= a[0] && a[0] <= x1 && y0 <= a[1] && a[1] <= y1;
-
- if (d3_geo_clipViewT(x0 - a[0], dx, t) &&
- d3_geo_clipViewT(a[0] - x1, -dx, t) &&
- d3_geo_clipViewT(y0 - a[1], dy, t) &&
- d3_geo_clipViewT(a[1] - y1, -dy, t)) {
- if (t[1] < 1) {
- b[0] = a[0] + t[1] * dx;
- b[1] = a[1] + t[1] * dy;
- }
- if (t[0] > 0) {
- a[0] += t[0] * dx;
- a[1] += t[0] * dy;
- }
- return true;
- }
-
- return false;
- }
-}
-
-function d3_geo_clipViewT(num, denominator, t) {
- if (Math.abs(denominator) < ε) return num <= 0;
-
- var u = num / denominator;
-
- if (denominator > 0) {
- if (u > t[1]) return false;
- if (u > t[0]) t[0] = u;
- } else {
- if (u < t[0]) return false;
- if (u < t[1]) t[1] = u;
- }
- return true;
}
function d3_geo_compose(a, b) {
@@ -3330,7 +3576,7 @@ d3.geo.bounds = (function() {
var dλ = λ - λ_,
s = dλ > 0 ? 1 : -1,
λi = inflection[0] * d3_degrees * s,
- antimeridian = Math.abs(dλ) > 180;
+ antimeridian = abs(dλ) > 180;
if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
var Ïi = inflection[1] * d3_degrees;
if (Ïi > Ï1) Ï1 = Ïi;
@@ -3375,7 +3621,7 @@ d3.geo.bounds = (function() {
function ringPoint(λ, Ï) {
if (p0) {
var dλ = λ - λ_;
- dλSum += Math.abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
+ dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
} else λ__ = λ, Ï__ = Ï;
d3_geo_area.point(λ, Ï);
linePoint(λ, Ï);
@@ -3388,7 +3634,7 @@ d3.geo.bounds = (function() {
function ringEnd() {
ringPoint(λ__, Ï__);
d3_geo_area.lineEnd();
- if (Math.abs(dλSum) > ε) λ0 = -(λ1 = 180);
+ if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
range[0] = λ0, range[1] = λ1;
p0 = null;
}
@@ -3601,7 +3847,7 @@ var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
},
polygonEnd: function() {
d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
- d3_geo_pathAreaSum += Math.abs(d3_geo_pathAreaPolygon / 2);
+ d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
}
};
@@ -3806,7 +4052,7 @@ function d3_geo_pathContext(context) {
function point(x, y) {
context.moveTo(x, y);
- context.arc(x, y, pointRadius, 0, 2 * Ï);
+ context.arc(x, y, pointRadius, 0, Ï);
}
function pointLineStart(x, y) {
@@ -3898,7 +4144,7 @@ function d3_geo_resample(project) {
c = c0 + c1,
m = Math.sqrt(a * a + b * b + c * c),
Ï2 = Math.asin(c /= m),
- λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a),
+ λ2 = abs(abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a),
p = project(λ2, Ï2),
x2 = p[0],
y2 = p[1],
@@ -3906,7 +4152,7 @@ function d3_geo_resample(project) {
dy2 = y2 - y0,
dz = dy * dx2 - dx * dy2;
if (dz * dz / d2 > δ2 // perpendicular projected distance
- || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end
+ || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 // midpoint close to an end
|| a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
stream.point(x2, y2);
@@ -3924,6 +4170,29 @@ function d3_geo_resample(project) {
return resample;
}
+d3.geo.transform = function(methods) {
+ return {
+ stream: function(stream) {
+ var transform = new d3_geo_transform(stream);
+ for (var k in methods) transform[k] = methods[k];
+ return transform;
+ }
+ };
+};
+
+function d3_geo_transform(stream) {
+ this.stream = stream;
+}
+
+d3_geo_transform.prototype = {
+ point: function(x, y) { this.stream.point(x, y); },
+ sphere: function() { this.stream.sphere(); },
+ lineStart: function() { this.stream.lineStart(); },
+ lineEnd: function() { this.stream.lineEnd(); },
+ polygonStart: function() { this.stream.polygonStart(); },
+ polygonEnd: function() { this.stream.polygonEnd(); }
+};
+
d3.geo.path = function() {
var pointRadius = 4.5,
projection,
@@ -3992,17 +4261,11 @@ d3.geo.path = function() {
};
function d3_geo_pathProjectStream(project) {
- var resample = d3_geo_resample(function(λ, Ï) { return project([λ * d3_degrees, Ï * d3_degrees]); });
+ var resample = d3_geo_resample(function(x, y) { return project([x * d3_degrees, y * d3_degrees]); });
return function(stream) {
- stream = resample(stream);
- return {
- point: function(λ, Ï) { stream.point(λ * d3_radians, Ï * d3_radians); },
- sphere: function() { stream.sphere(); },
- lineStart: function() { stream.lineStart(); },
- lineEnd: function() { stream.lineEnd(); },
- polygonStart: function() { stream.polygonStart(); },
- polygonEnd: function() { stream.polygonEnd(); }
- };
+ var transform = new d3_geo_transform(stream = resample(stream));
+ transform.point = function(x, y) { stream.point(x * d3_radians, y * d3_radians); };
+ return transform;
};
}
@@ -4041,7 +4304,7 @@ function d3_geo_projectionMutator(projectAt) {
projection.stream = function(output) {
if (stream) stream.valid = false;
- stream = d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(output))));
+ stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
stream.valid = true; // allow caching by d3.geo.path
return stream;
};
@@ -4055,7 +4318,7 @@ function d3_geo_projectionMutator(projectAt) {
projection.clipExtent = function(_) {
if (!arguments.length) return clipExtent;
clipExtent = _;
- postclip = _ == null ? d3_identity : d3_geo_clipView(_[0][0], _[0][1], _[1][0], _[1][1]);
+ postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
return invalidate();
};
@@ -4098,10 +4361,7 @@ function d3_geo_projectionMutator(projectAt) {
}
function invalidate() {
- if (stream) {
- stream.valid = false;
- stream = null;
- }
+ if (stream) stream.valid = false, stream = null;
return projection;
}
@@ -4112,18 +4372,12 @@ function d3_geo_projectionMutator(projectAt) {
};
}
-function d3_geo_projectionRadiansRotate(rotate, stream) {
- return {
- point: function(x, y) {
- y = rotate(x * d3_radians, y * d3_radians), x = y[0];
- stream.point(x > Ï ? x - 2 * Ï : x < -Ï ? x + 2 * Ï : x, y[1]);
- },
- sphere: function() { stream.sphere(); },
- lineStart: function() { stream.lineStart(); },
- lineEnd: function() { stream.lineEnd(); },
- polygonStart: function() { stream.polygonStart(); },
- polygonEnd: function() { stream.polygonEnd(); }
+function d3_geo_projectionRadians(stream) {
+ var transform = new d3_geo_transform(stream);
+ transform.point = function(λ, Ï) {
+ stream.point(λ * d3_radians, Ï * d3_radians);
};
+ return transform;
}
function d3_geo_mercator(λ, Ï) {
@@ -4131,7 +4385,7 @@ function d3_geo_mercator(λ, Ï) {
}
d3_geo_mercator.invert = function(x, y) {
- return [x, 2 * Math.atan(Math.exp(y)) - Ï / 2];
+ return [x, 2 * Math.atan(Math.exp(y)) - halfÏ];
};
function d3_geo_mercatorProjection(project) {
@@ -4303,7 +4557,7 @@ d3.ease = function(name) {
m = i >= 0 ? name.substring(i + 1) : "in";
t = d3_ease.get(t) || d3_ease_default;
m = d3_ease_mode.get(m) || d3_identity;
- return d3_ease_clamp(m(t.apply(null, Array.prototype.slice.call(arguments, 1))));
+ return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
};
function d3_ease_clamp(f) {
@@ -4347,7 +4601,7 @@ function d3_ease_poly(e) {
}
function d3_ease_sin(t) {
- return 1 - Math.cos(t * Ï / 2);
+ return 1 - Math.cos(t * halfÏ);
}
function d3_ease_exp(t) {
@@ -4361,10 +4615,10 @@ function d3_ease_circle(t) {
function d3_ease_elastic(a, p) {
var s;
if (arguments.length < 2) p = 0.45;
- if (arguments.length) s = p / (2 * Ï) * Math.asin(1 / a);
+ if (arguments.length) s = p / Ï * Math.asin(1 / a);
else a = 1, s = p / 4;
return function(t) {
- return 1 + a * Math.pow(2, 10 * -t) * Math.sin((t - s) * 2 * Ï / p);
+ return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * Ï / p);
};
}
@@ -5379,7 +5633,7 @@ function d3_transition_text(b) {
d3_transitionPrototype.remove = function() {
return this.each("end.transition", function() {
var p;
- if (!this.__transition__ && (p = this.parentNode)) p.removeChild(this);
+ if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this);
});
};
@@ -5393,15 +5647,15 @@ d3_transitionPrototype.ease = function(value) {
d3_transitionPrototype.delay = function(value) {
var id = this.id;
return d3_selection_each(this, typeof value === "function"
- ? function(node, i, j) { node.__transition__[id].delay = value.call(node, node.__data__, i, j) | 0; }
- : (value |= 0, function(node) { node.__transition__[id].delay = value; }));
+ ? function(node, i, j) { node.__transition__[id].delay = +value.call(node, node.__data__, i, j); }
+ : (value = +value, function(node) { node.__transition__[id].delay = value; }));
};
d3_transitionPrototype.duration = function(value) {
var id = this.id;
return d3_selection_each(this, typeof value === "function"
- ? function(node, i, j) { node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j) | 0); }
- : (value = Math.max(1, value | 0), function(node) { node.__transition__[id].duration = value; }));
+ ? function(node, i, j) { node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j)); }
+ : (value = Math.max(1, value), function(node) { node.__transition__[id].duration = value; }));
};
d3_transitionPrototype.each = function(type, listener) {
@@ -5471,10 +5725,12 @@ function d3_transitionNode(node, i, id, inherit) {
ease = transition.ease,
delay = transition.delay,
duration = transition.duration,
+ timer = d3_timer_active,
tweened = [];
- if (delay <= elapsed) return start(elapsed);
- d3_timer_replace(start, delay, time);
+ timer.t = delay + time;
+ if (delay <= elapsed) return start(elapsed - delay);
+ timer.c = start;
function start(elapsed) {
if (lock.active > id) return stop();
@@ -5487,14 +5743,16 @@ function d3_transitionNode(node, i, id, inherit) {
}
});
- if (tick(elapsed)) return 1;
- d3_timer_replace(tick, 0, time);
+ d3.timer(function() { // defer to end of current frame
+ timer.c = tick(elapsed || 1) ? d3_true : tick;
+ return 1;
+ }, 0, time);
}
function tick(elapsed) {
if (lock.active !== id) return stop();
- var t = (elapsed - delay) / duration,
+ var t = elapsed / duration,
e = ease(t),
n = tweened.length;
@@ -5503,9 +5761,8 @@ function d3_transitionNode(node, i, id, inherit) {
}
if (t >= 1) {
- stop();
transition.event && transition.event.end.call(node, d, i);
- return 1;
+ return stop();
}
}
@@ -5529,7 +5786,7 @@ function d3_xhrType(response) {
function d3_xhr(url, mimeType, response, callback) {
var xhr = {},
- dispatch = d3.dispatch("progress", "load", "error"),
+ dispatch = d3.dispatch("beforesend", "progress", "load", "error"),
headers = {},
request = new XMLHttpRequest,
responseType = null;
@@ -5611,6 +5868,7 @@ function d3_xhr(url, mimeType, response, callback) {
if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
if (responseType != null) request.responseType = responseType;
if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); });
+ dispatch.beforesend.call(xhr, request);
request.send(data == null ? null : data);
return xhr;
};
@@ -5690,25 +5948,26 @@ d3.combobox = function() {
var parent = this.parentNode,
sibling = this.nextSibling;
- var carat = d3.select(parent).selectAll('.combobox-carat')
+ var caret = d3.select(parent).selectAll('.combobox-caret')
.filter(function(d) { return d === input.node(); })
.data([input.node()]);
- carat.enter().insert('div', function() { return sibling; })
- .attr('class', 'combobox-carat');
+ caret.enter().insert('div', function() { return sibling; })
+ .attr('class', 'combobox-caret');
- carat
+ caret
.on('mousedown', function () {
// prevent the form element from blurring. it blurs
// on mousedown
d3.event.stopPropagation();
d3.event.preventDefault();
input.node().focus();
+ fetch('', render);
});
});
function focus() {
- fetch(render);
+ fetch(value(), render);
}
function blur() {
@@ -5798,7 +6057,7 @@ d3.combobox = function() {
}
function change() {
- fetch(function() {
+ fetch(value(), function() {
autocomplete();
render();
});
@@ -5823,8 +6082,8 @@ d3.combobox = function() {
return value;
}
- function fetch(cb) {
- fetcher.call(input, value(), function(_) {
+ function fetch(v, cb) {
+ fetcher.call(input, v, function(_) {
suggestions = _;
cb();
});
@@ -5849,7 +6108,7 @@ d3.combobox = function() {
}
function render() {
- if (suggestions.length && document.activeElement === input.node()) {
+ if (suggestions.length > 1 && document.activeElement === input.node()) {
show();
} else {
hide();
@@ -15119,6 +15378,7 @@ if (typeof exports === 'object') {
}).call(function() {
return this || (typeof window !== 'undefined' ? window : global);
}());
+/* jshint ignore:start */
(function () {
'use strict';
window.iD = function () {
@@ -15147,7 +15407,9 @@ window.iD = function () {
else storage.setItem(k, v);
} catch(e) {
// localstorage quota exceeded
+ /* jshint devel:true */
if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
+ /* jshint devel:false */
}
};
@@ -15230,7 +15492,7 @@ window.iD = function () {
var result = fn.apply(history, arguments);
debouncedSave();
return result;
- }
+ };
}
context.perform = withDebouncedSave(history.perform);
@@ -15318,9 +15580,52 @@ window.iD = function () {
};
/* Projection */
- context.projection = d3.geo.mercator()
- .scale(512 / Math.PI)
- .precision(0);
+ function rawMercator() {
+ var project = d3.geo.mercator.raw,
+ k = 512 / Math.PI, // scale
+ x = 0, y = 0, // translate
+ clipExtent = [[0, 0], [0, 0]];
+
+ function projection(point) {
+ point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
+ return [point[0] * k + x, y - point[1] * k];
+ }
+
+ projection.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.scale = function(_) {
+ if (!arguments.length) return k;
+ k = +_;
+ return projection;
+ };
+
+ projection.translate = function(_) {
+ if (!arguments.length) return [x, y];
+ x = +_[0];
+ y = +_[1];
+ return projection;
+ };
+
+ projection.clipExtent = function(_) {
+ if (!arguments.length) return clipExtent;
+ clipExtent = _;
+ return projection;
+ };
+
+ projection.stream = d3.geo.transform({
+ point: function(x, y) {
+ x = projection([x, y]);
+ this.stream.point(x[0], x[1]);
+ }
+ }).stream;
+
+ return projection;
+ }
+
+ context.projection = rawMercator();
/* Background */
var background = iD.Background(context);
@@ -15388,13 +15693,13 @@ window.iD = function () {
return d3.rebind(context, dispatch, 'on');
};
-iD.version = '1.2.1';
+iD.version = '1.3.2';
(function() {
var detected = {};
var ua = navigator.userAgent,
- msie = new RegExp("MSIE ([0-9]{1,}[\\.0-9]{0,})");
+ msie = new RegExp('MSIE ([0-9]{1,}[\\.0-9]{0,})');
if (msie.exec(ua) !== null) {
var rv = parseFloat(RegExp.$1);
@@ -15638,7 +15943,7 @@ iD.util.entityOrMemberSelector = function(ids, graph) {
var entity = graph.hasEntity(id);
if (entity && entity.type === 'relation') {
entity.members.forEach(function(member) {
- s += ',.' + member.id
+ s += ',.' + member.id;
});
}
});
@@ -15723,7 +16028,7 @@ iD.util.editDistance = function(a, b) {
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)) {
+ 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
@@ -15751,6 +16056,7 @@ iD.util.fastMouse = function(container) {
};
};
+/* jshint -W103 */
iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
iD.util.asyncMap = function(inputs, func, callback) {
@@ -15774,6 +16080,70 @@ iD.util.wrap = function(index, length) {
index += Math.ceil(-index/length)*length;
return index % length;
};
+// 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.
+
+iD.util.SessionMutex = function(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;
+};
+iD.util.SuggestNames = function(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 = iD.util.editDistance(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);
+ };
+};
iD.geo = {};
iD.geo.roundCoords = function(c) {
@@ -15797,6 +16167,11 @@ iD.geo.sphericalDistance = function(a, b) {
return 6.3710E6 * Math.sqrt((x * x) + (y * y)) * Math.PI/180;
};
+iD.geo.edgeEqual = function(a, b) {
+ return (a[0] === b[0] && a[1] === b[1]) ||
+ (a[0] === b[1] && a[1] === b[0]);
+};
+
// Choose the edge with the minimal distance from `point` to its orthogonal
// projection onto that edge, if such a projection exists, or the distance to
// the closest vertex on that edge. Returns an object with the `index` of the
@@ -15861,7 +16236,7 @@ iD.geo.pointInPolygon = function(point, polygon) {
var xi = polygon[i][0], yi = polygon[i][1];
var xj = polygon[j][0], yj = polygon[j][1];
- var intersect = ((yi > y) != (yj > y)) &&
+ var intersect = ((yi > y) !== (yj > y)) &&
(x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
@@ -15927,7 +16302,7 @@ _.extend(iD.geo.Extent.prototype, {
[this[1][0], this[1][1]],
[this[1][0], this[0][1]],
[this[0][0], this[0][1]]
- ]
+ ];
},
intersects: function(obj) {
@@ -16188,7 +16563,7 @@ iD.actions.AddMember = function(relationId, member, memberIndex) {
}
return graph.replace(relation.addMember(member, memberIndex));
- }
+ };
};
iD.actions.AddMidpoint = function(midpoint, node) {
return function(graph) {
@@ -16200,10 +16575,7 @@ iD.actions.AddMidpoint = function(midpoint, node) {
parents.forEach(function(way) {
for (var i = 0; i < way.nodes.length - 1; i++) {
- if ((way.nodes[i] === midpoint.edge[0] &&
- way.nodes[i + 1] === midpoint.edge[1]) ||
- (way.nodes[i] === midpoint.edge[1] &&
- way.nodes[i + 1] === midpoint.edge[0])) {
+ if (iD.geo.edgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
// Add only one midpoint on doubled-back segments,
@@ -16225,7 +16597,7 @@ iD.actions.AddVertex = function(wayId, nodeId, index) {
iD.actions.ChangeMember = function(relationId, member, memberIndex) {
return function(graph) {
return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
- }
+ };
};
iD.actions.ChangePreset = function(entityId, oldPreset, newPreset) {
return function(graph) {
@@ -16251,7 +16623,7 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) {
var action = function(graph) {
var way = graph.entity(wayId),
nodes = _.uniq(graph.childNodes(way)),
- keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length != 1; }),
+ keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
points = nodes.map(function(n) { return projection(n.loc); }),
keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
centroid = d3.geom.polygon(points).centroid(),
@@ -16265,7 +16637,7 @@ iD.actions.Circularize = function(wayId, projection, maxAngle) {
keyPoints = [points[0]];
}
- if (keyNodes.length == 1) {
+ if (keyNodes.length === 1) {
var index = nodes.indexOf(keyNodes[0]),
oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
@@ -16375,6 +16747,7 @@ iD.actions.Connect = function(nodeIds) {
for (var i = 0; i < nodeIds.length - 1; i++) {
var node = graph.entity(nodeIds[i]);
+ /*jshint -W083 */
graph.parentWays(node).forEach(function(parent) {
if (!parent.areAdjacent(node.id, survivor.id)) {
graph = graph.replace(parent.replaceNode(node.id, survivor.id));
@@ -16384,6 +16757,7 @@ iD.actions.Connect = function(nodeIds) {
graph.parentRelations(node).forEach(function(parent) {
graph = graph.replace(parent.replaceMember(node, survivor));
});
+ /*jshint +W083 */
survivor = survivor.mergeTags(node.tags);
graph = iD.actions.DeleteNode(node.id)(graph);
@@ -16589,7 +16963,7 @@ iD.actions.DiscardTags = function(difference) {
difference.created().forEach(discardTags);
return graph;
- }
+ };
};
// Disconect the ways at the given node.
//
@@ -16615,7 +16989,7 @@ iD.actions.Disconnect = function(nodeId, newNodeId) {
replacements.forEach(function(replacement) {
var newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
graph = graph.replace(newNode);
- graph = graph.replace(replacement.way.updateNode(newNode.id, replacement.index));
+ graph = graph.replace(graph.entity(replacement.wayID).updateNode(newNode.id, replacement.index));
});
return graph;
@@ -16634,7 +17008,7 @@ iD.actions.Disconnect = function(nodeId, newNodeId) {
parent.nodes.forEach(function(waynode, index) {
if (waynode === nodeId) {
- candidates.push({way: parent, index: index});
+ candidates.push({wayID: parent.id, index: index});
}
});
});
@@ -16828,7 +17202,7 @@ iD.actions.MergePolygon = function(ids, newRelationId) {
return _.any(contained[i]);
}
- function filterContained(d, i) {
+ function filterContained(d) {
return d.filter(isContained);
}
@@ -16985,7 +17359,7 @@ iD.actions.Orthogonalize = function(wayId, projection) {
for (i = 0; i < points.length; i++) {
// only move the points that actually moved
- if (originalPoints[i][0] != points[i][0] || originalPoints[i][1] != points[i][1]) {
+ if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
graph = graph.replace(graph.entity(nodes[i].id)
.move(projection.invert(points[i])));
}
@@ -16995,8 +17369,8 @@ iD.actions.Orthogonalize = function(wayId, projection) {
for (i = 0; i < points.length; i++) {
var node = nodes[i];
- if (graph.parentWays(node).length > 1 ||
- graph.parentRelations(node).length ||
+ if (graph.parentWays(node).length > 1 ||
+ graph.parentRelations(node).length ||
node.hasInterestingTags()) {
continue;
@@ -17150,9 +17524,9 @@ iD.actions.Reverse = function(wayId) {
}
function reverseValue(key, value) {
- if (key === "incline" && numeric.test(value)) {
+ if (key === 'incline' && numeric.test(value)) {
return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
- } else if (key === "incline" || key === "direction") {
+ } else if (key === 'incline' || key === 'direction') {
return {up: 'down', down: 'up'}[value] || value;
} else {
return {left: 'right', right: 'left'}[value] || value;
@@ -17250,13 +17624,13 @@ iD.actions.Split = function(nodeId, newWayIds) {
// calculate lengths
length = 0;
- for (i = wrap(idxA+1); i != idxA; i = wrap(i+1)) {
+ for (i = wrap(idxA+1); i !== idxA; i = wrap(i+1)) {
length += dist(nodes[i], nodes[wrap(i-1)]);
lengths[i] = length;
}
length = 0;
- for (i = wrap(idxA-1); i != idxA; i = wrap(i-1)) {
+ for (i = wrap(idxA-1); i !== idxA; i = wrap(i-1)) {
length += dist(nodes[i], nodes[wrap(i+1)]);
if (length < lengths[i])
lengths[i] = length;
@@ -17413,22 +17787,22 @@ iD.actions.Straighten = function(wayId, projection) {
i;
for (i = 1; i < points.length-1; i++) {
- var node = nodes[i],
+ var node = nodes[i],
point = points[i];
- if (graph.parentWays(node).length > 1 ||
- graph.parentRelations(node).length ||
+ if (graph.parentWays(node).length > 1 ||
+ graph.parentRelations(node).length ||
node.hasInterestingTags()) {
var u = positionAlongWay(point, startPoint, endPoint),
p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
- p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
+ p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
graph = graph.replace(graph.entity(node.id)
.move(projection.invert([p0, p1])));
} else {
// safe to delete
- if (toDelete.indexOf(node) == -1) {
+ if (toDelete.indexOf(node) === -1) {
toDelete.push(node);
}
}
@@ -17452,7 +17826,7 @@ iD.actions.Straighten = function(wayId, projection) {
i;
for (i = 1; i < points.length-1; i++) {
- var point = points[i],
+ var point = points[i],
u = positionAlongWay(point, startPoint, endPoint),
p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]),
@@ -17526,7 +17900,7 @@ iD.behavior.drag = function() {
d3.event.preventDefault();
}
- var event = d3.dispatch("start", "move", "end"),
+ var event = d3.dispatch('start', 'move', 'end'),
origin = null,
selector = '',
filter = null,
@@ -17534,10 +17908,10 @@ iD.behavior.drag = function() {
event.of = function(thiz, argumentz) {
return function(e1) {
+ var e0 = e1.sourceEvent = d3.event;
+ e1.target = drag;
+ d3.event = e1;
try {
- var e0 = e1.sourceEvent = d3.event;
- e1.target = drag;
- d3.event = e1;
event[e1.type].apply(thiz, argumentz);
} finally {
d3.event = e0;
@@ -17545,7 +17919,7 @@ iD.behavior.drag = function() {
};
};
- var d3_event_userSelectProperty = iD.util.prefixCSSProperty("UserSelect"),
+ var d3_event_userSelectProperty = iD.util.prefixCSSProperty('UserSelect'),
d3_event_userSelectSuppress = d3_event_userSelectProperty ?
function () {
var selection = d3.selection(),
@@ -17556,9 +17930,9 @@ iD.behavior.drag = function() {
};
} :
function (type) {
- var w = d3.select(window).on("selectstart." + type, d3_eventCancel);
+ var w = d3.select(window).on('selectstart.' + type, d3_eventCancel);
return function () {
- w.on("selectstart." + type, null);
+ w.on('selectstart.' + type, null);
};
};
@@ -17569,12 +17943,12 @@ iD.behavior.drag = function() {
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
offset,
origin_ = point(),
- moved = 0,
- selectEnable = d3_event_userSelectSuppress(touchId != null ? "drag-" + touchId : "drag");
+ started = false,
+ selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag');
var w = d3.select(window)
- .on(touchId !== null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
- .on(touchId !== null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
+ .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);
@@ -17598,41 +17972,41 @@ iD.behavior.drag = function() {
dx = p[0] - origin_[0],
dy = p[1] - origin_[1];
- if (!moved) {
+ if (!started) {
+ started = true;
event_({
- type: "start"
+ type: 'start'
});
}
- moved |= dx | dy;
origin_ = p;
d3_eventCancel();
event_({
- type: "move",
+ type: 'move',
point: [p[0] + offset[0], p[1] + offset[1]],
delta: [dx, dy]
});
}
function dragend() {
- if (moved) {
+ if (started) {
event_({
- type: "end"
+ type: 'end'
});
d3_eventCancel();
- if (d3.event.target === eventTarget) w.on("click.drag", click, true);
+ if (d3.event.target === eventTarget) w.on('click.drag', click, true);
}
- w.on(touchId !== null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
- .on(touchId !== null ? "touchend.drag-" + touchId : "mouseup.drag", null);
+ w.on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null)
+ .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null);
selectEnable();
}
function click() {
d3_eventCancel();
- w.on("click.drag", null);
+ w.on('click.drag', null);
}
}
@@ -17653,13 +18027,13 @@ iD.behavior.drag = function() {
};
}
- selection.on("mousedown.drag" + selector, delegate)
- .on("touchstart.drag" + selector, delegate);
+ selection.on('mousedown.drag' + selector, delegate)
+ .on('touchstart.drag' + selector, delegate);
}
drag.off = function(selection) {
- selection.on("mousedown.drag" + selector, null)
- .on("touchstart.drag" + selector, null);
+ selection.on('mousedown.drag' + selector, null)
+ .on('touchstart.drag' + selector, null);
};
drag.delegate = function(_) {
@@ -17682,8 +18056,8 @@ iD.behavior.drag = function() {
drag.cancel = function() {
d3.select(window)
- .on("mousemove.drag", null)
- .on("mouseup.drag", null);
+ .on('mousemove.drag', null)
+ .on('mouseup.drag', null);
return drag;
};
@@ -17700,7 +18074,7 @@ iD.behavior.drag = function() {
return drag;
};
- return d3.rebind(drag, event, "on");
+ return d3.rebind(drag, event, 'on');
};
iD.behavior.Draw = function(context) {
var event = d3.dispatch('move', 'click', 'clickWay',
@@ -17728,8 +18102,7 @@ iD.behavior.Draw = function(context) {
})[0] : d3.mouse(p);
}
- var eventTarget = d3.event.target,
- element = d3.select(this),
+ var element = d3.select(this),
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
time = +new Date(),
pos = point();
@@ -17971,12 +18344,13 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
// Connect the way to an existing way.
drawWay.addWay = function(loc, edge) {
+ var previousEdge = startIndex ?
+ [way.nodes[startIndex], way.nodes[startIndex - 1]] :
+ [way.nodes[0], way.nodes[1]];
// Avoid creating duplicate segments
- if (!isArea) {
- if (edge[0] === way.nodes[way.nodes.length - 1] ||
- edge[1] === way.nodes[way.nodes.length - 1]) return;
- }
+ if (!isArea && iD.geo.edgeEqual(edge, previousEdge))
+ return;
var newNode = iD.Node({ loc: loc });
@@ -18063,7 +18437,7 @@ iD.behavior.Hash = function(context) {
var parser = function(map, s) {
var q = iD.util.stringQs(s);
- var args = (q.map || '').split("/").map(Number);
+ 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)) {
@@ -18118,7 +18492,7 @@ iD.behavior.Hash = function(context) {
d3.select(window)
.on('hashchange.hash', null);
- location.hash = "";
+ location.hash = '';
};
return hash;
@@ -18132,7 +18506,7 @@ iD.behavior.Hash = function(context) {
Only one of these elements can have the :hover pseudo-class, but all of them will
have the .hover class.
*/
-iD.behavior.Hover = function(context) {
+iD.behavior.Hover = function() {
var dispatch = d3.dispatch('hover'),
selection,
altDisables,
@@ -18206,7 +18580,7 @@ iD.behavior.Hover = function(context) {
function mousedown() {
down = true;
d3.select(window)
- .on('mouseup.hover', mouseup)
+ .on('mouseup.hover', mouseup);
}
function mouseup() {
@@ -18239,7 +18613,7 @@ iD.behavior.Hover = function(context) {
d3.select(window)
.on('keydown.hover', null)
.on('keyup.hover', null)
- .on('mouseup.hover', null)
+ .on('mouseup.hover', null);
};
hover.altDisables = function(_) {
@@ -18387,25 +18761,25 @@ iD.behavior.Tail = function() {
var text,
container,
xmargin = 25,
- tooltip_size = [0, 0],
- selection_size = [0, 0],
+ tooltipSize = [0, 0],
+ selectionSize = [0, 0],
transformProp = iD.util.prefixCSSProperty('Transform');
function tail(selection) {
if (!text) return;
d3.select(window)
- .on('resize.tail', function() { selection_size = selection.dimensions(); });
+ .on('resize.tail', function() { selectionSize = selection.dimensions(); });
function show() {
container.style('display', 'block');
- tooltip_size = container.dimensions();
+ tooltipSize = container.dimensions();
}
function mousemove() {
if (container.style('display') === 'none') show();
- var xoffset = ((d3.event.clientX + tooltip_size[0] + xmargin) > selection_size[0]) ?
- -tooltip_size[0] - xmargin : xmargin;
+ var xoffset = ((d3.event.clientX + tooltipSize[0] + xmargin) > selectionSize[0]) ?
+ -tooltipSize[0] - xmargin : xmargin;
container.classed('left', xoffset > 0);
container.style(transformProp, 'translate(' +
(~~d3.event.clientX + xoffset) + 'px,' +
@@ -18440,8 +18814,8 @@ iD.behavior.Tail = function() {
container
.on('mousemove.tail', mousemove);
- tooltip_size = container.dimensions();
- selection_size = selection.dimensions();
+ tooltipSize = container.dimensions();
+ selectionSize = selection.dimensions();
}
tail.off = function(selection) {
@@ -18628,7 +19002,7 @@ iD.modes.AddPoint = function(context) {
.newFeature(true));
}
- function addWay(loc, edge) {
+ function addWay(loc) {
add(loc);
}
@@ -18672,7 +19046,7 @@ iD.modes.Browse = function(context) {
});
// Get focus on the body.
- if (document.activeElement) {
+ if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur();
}
@@ -18806,7 +19180,7 @@ iD.modes.DragNode = function(context) {
var d = datum();
if (d.type === 'node' && d.id !== entity.id) {
loc = d.loc;
- } else if (d.type === 'way') {
+ } else if (d.type === 'way' && !d3.select(d3.event.sourceEvent.target).classed('fill')) {
loc = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection).loc;
}
@@ -18866,7 +19240,7 @@ iD.modes.DragNode = function(context) {
}
var behavior = iD.behavior.drag()
- .delegate("g.node, g.point, g.midpoint")
+ .delegate('g.node, g.point, g.midpoint')
.surface(context.surface().node())
.origin(origin)
.on('start', start)
@@ -19193,18 +19567,12 @@ iD.modes.RotateWay = function(context, wayId) {
iD.modes.Save = function(context) {
var ui = iD.ui.Commit(context)
.on('cancel', cancel)
- .on('fix', fix)
.on('save', save);
function cancel() {
context.enter(iD.modes.Browse(context));
}
- function fix(d) {
- context.map().zoomTo(d.entity);
- context.enter(iD.modes.Select(context, [d.entity.id]));
- }
-
function save(e) {
var loading = iD.ui.Loading(context)
.message(t('save.uploading'))
@@ -19263,7 +19631,7 @@ iD.modes.Save = function(context) {
context.install(behavior);
});
- context.connection().authenticate(function(err) {
+ context.connection().authenticate(function() {
context.ui().sidebar.show(ui);
});
};
@@ -19451,6 +19819,11 @@ iD.modes.Select = function(context, selectedIDs) {
context.surface()
.on('dblclick.select', dblclick);
}, 200);
+
+ if (selectedIDs.length > 1) {
+ var entities = iD.ui.SelectionList(context, selectedIDs);
+ context.ui().sidebar.show(entities);
+ }
};
mode.exit = function() {
@@ -19474,10 +19847,11 @@ iD.modes.Select = function(context, selectedIDs) {
context.surface()
.call(radialMenu.close)
.on('dblclick.select', null)
- .selectAll(".selected")
+ .selectAll('.selected')
.classed('selected', false);
context.map().on('drawn.select', null);
+ context.ui().sidebar.hide();
};
return mode;
@@ -19509,7 +19883,7 @@ iD.operations.Circularize = function(selectedIDs, context) {
t('operations.circularize.description.' + geometry);
};
- operation.id = "circularize";
+ operation.id = 'circularize';
operation.keys = [t('operations.circularize.key')];
operation.title = t('operations.circularize.title');
@@ -19558,7 +19932,7 @@ iD.operations.Continue = function(selectedIDs, context) {
t('operations.continue.description');
};
- operation.id = "continue";
+ operation.id = 'continue';
operation.keys = [t('operations.continue.key')];
operation.title = t('operations.continue.title');
@@ -19628,7 +20002,7 @@ iD.operations.Delete = function(selectedIDs, context) {
t('operations.delete.description');
};
- operation.id = "delete";
+ operation.id = 'delete';
operation.keys = [iD.ui.cmd('ââ«'), iD.ui.cmd('ââ¦')];
operation.title = t('operations.delete.title');
@@ -19665,7 +20039,7 @@ iD.operations.Disconnect = function(selectedIDs, context) {
t('operations.disconnect.description');
};
- operation.id = "disconnect";
+ operation.id = 'disconnect';
operation.keys = [t('operations.disconnect.key')];
operation.title = t('operations.disconnect.title');
@@ -19717,7 +20091,7 @@ iD.operations.Merge = function(selectedIDs, context) {
return t('operations.merge.description');
};
- operation.id = "merge";
+ operation.id = 'merge';
operation.keys = [t('operations.merge.key')];
operation.title = t('operations.merge.title');
@@ -19745,7 +20119,7 @@ iD.operations.Move = function(selectedIDs, context) {
t('operations.move.description');
};
- operation.id = "move";
+ operation.id = 'move';
operation.keys = [t('operations.move.key')];
operation.title = t('operations.move.title');
@@ -19780,7 +20154,7 @@ iD.operations.Orthogonalize = function(selectedIDs, context) {
t('operations.orthogonalize.description.' + geometry);
};
- operation.id = "orthogonalize";
+ operation.id = 'orthogonalize';
operation.keys = [t('operations.orthogonalize.key')];
operation.title = t('operations.orthogonalize.title');
@@ -19808,7 +20182,7 @@ iD.operations.Reverse = function(selectedIDs, context) {
return t('operations.reverse.description');
};
- operation.id = "reverse";
+ operation.id = 'reverse';
operation.keys = [t('operations.reverse.key')];
operation.title = t('operations.reverse.title');
@@ -19835,7 +20209,7 @@ iD.operations.Rotate = function(selectedIDs, context) {
return t('operations.rotate.description');
};
- operation.id = "rotate";
+ operation.id = 'rotate';
operation.keys = [t('operations.rotate.key')];
operation.title = t('operations.rotate.title');
@@ -19889,7 +20263,7 @@ iD.operations.Split = function(selectedIDs, context) {
}
};
- operation.id = "split";
+ operation.id = 'split';
operation.keys = [t('operations.split.key')];
operation.title = t('operations.split.title');
@@ -19923,7 +20297,7 @@ iD.operations.Straighten = function(selectedIDs, context) {
t('operations.straighten.description');
};
- operation.id = "straighten";
+ operation.id = 'straighten';
operation.keys = [t('operations.straighten.key')];
operation.title = t('operations.straighten.title');
@@ -19965,7 +20339,7 @@ iD.Connection = function() {
};
connection.userURL = function(username) {
- return url + "/user/" + username;
+ return url + '/user/' + username;
};
connection.loadFromURL = function(url, callback) {
@@ -19983,7 +20357,7 @@ iD.Connection = function() {
url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
function(err, entities) {
event.load(err, {data: entities});
- if (callback) callback(err, entities && entities[id]);
+ if (callback) callback(err, entities && _.find(entities, function(e) { return e.id === id; }));
});
};
@@ -20068,15 +20442,13 @@ iD.Connection = function() {
var root = dom.childNodes[0],
children = root.childNodes,
- entities = {};
+ entities = [];
- var i, o, l;
- for (i = 0, l = children.length; i < l; i++) {
+ for (var i = 0, l = children.length; i < l; i++) {
var child = children[i],
parser = parsers[child.nodeName];
if (parser) {
- o = parser(child);
- entities[o.id] = o;
+ entities.push(parser(child));
}
}
@@ -20246,7 +20618,7 @@ iD.Connection = function() {
extent: iD.geo.Extent(
projection.invert([x, y + ts]),
projection.invert([x + ts, y]))
- }
+ };
});
function bboxUrl(tile) {
@@ -20413,13 +20785,53 @@ iD.Difference = function(base, head) {
return result;
};
- difference.addParents = function(entities) {
+ difference.summary = function() {
+ var relevant = {};
- for (var i in entities) {
- addParents(head.parentWays(entities[i]), entities);
- addParents(head.parentRelations(entities[i]), entities);
+ function addEntity(entity, graph, changeType) {
+ relevant[entity.id] = {
+ entity: entity,
+ graph: graph,
+ changeType: changeType
+ };
}
- return entities;
+
+ 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');
+ }
+ }
+
+ _.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 = !_.isEqual(change.base.loc, change.head.loc),
+ retagged = !_.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 d3.values(relevant);
};
difference.complete = function(extent) {
@@ -20568,18 +20980,12 @@ iD.Entity.prototype = {
resolver.parentRelations(this).length > 0;
},
- area: function(resolver) {
- return resolver.transient(this, 'area', function() {
- return d3.geo.area(this.asGeoJSON(resolver, true));
- });
- },
-
hasInterestingTags: function() {
return _.keys(this.tags).some(function(key) {
- return key != 'attribution' &&
- key != 'created_by' &&
- key != 'source' &&
- key != 'odbl' &&
+ return key !== 'attribution' &&
+ key !== 'created_by' &&
+ key !== 'source' &&
+ key !== 'odbl' &&
key.indexOf('tiger:') !== 0;
});
},
@@ -20591,8 +20997,8 @@ iD.Entity.prototype = {
iD.data.deprecated.forEach(function(d) {
var match = _.pairs(d.old)[0];
tags.forEach(function(t) {
- if (t[0] == match[0] &&
- (t[1] == match[1] || match[1] == '*')) {
+ if (t[0] === match[0] &&
+ (t[1] === match[1] || match[1] === '*')) {
deprecated[t[0]] = t[1];
}
});
@@ -20612,17 +21018,10 @@ iD.Graph = function(other, mutable) {
this.inherited = true;
} else {
- if (Array.isArray(other)) {
- var entities = {};
- for (var i = 0; i < other.length; i++) {
- entities[other[i].id] = other[i];
- }
- other = entities;
- }
this.entities = Object.create({});
this._parentWays = Object.create({});
this._parentRels = Object.create({});
- this.rebase(other || {});
+ this.rebase(other || []);
}
this.transients = {};
@@ -20711,11 +21110,12 @@ iD.Graph.prototype = {
// Merging of data only needed if graph is the base graph
if (!this.inherited) {
- for (i in entities) {
- if (!base.entities[i]) {
- base.entities[i] = entities[i];
- this._updateCalculated(undefined, entities[i],
- base.parentWays, base.parentRels);
+ for (i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+ if (!base.entities[entity.id]) {
+ base.entities[entity.id] = entity;
+ this._updateCalculated(undefined, entity,
+ base.parentWays, base.parentRels);
}
}
}
@@ -20783,7 +21183,6 @@ iD.Graph.prototype = {
ways.push(entity.id);
parentWays[added[i]] = ways;
}
- } else if (type === 'node') {
} else if (type === 'relation') {
@@ -20846,24 +21245,6 @@ iD.Graph.prototype = {
return this;
},
- hasAllChildren: function(entity) {
- // we're only checking changed entities, since we assume fetched data
- // must have all children present
- var i;
- if (this.entities.hasOwnProperty(entity.id)) {
- if (entity.type === 'way') {
- for (i = 0; i < entity.nodes.length; i++) {
- if (!this.entities[entity.nodes[i]]) return false;
- }
- } else if (entity.type === 'relation') {
- for (i = 0; i < entity.members.length; i++) {
- if (!this.entities[entity.members[i].id]) return false;
- }
- }
- }
- return true;
- },
-
// Obliterates any existing entities
load: function(entities) {
var base = this.base();
@@ -20881,7 +21262,7 @@ iD.History = function(context) {
var stack, index, tree,
imageryUsed = ['Bing'],
dispatch = d3.dispatch('change', 'undone', 'redone'),
- lock = false;
+ lock = iD.util.SessionMutex('lock');
function perform(actions) {
actions = Array.prototype.slice.call(actions);
@@ -20921,17 +21302,11 @@ iD.History = function(context) {
},
merge: function(entities, extent) {
-
- var base = stack[0].graph.base(),
- newentities = Object.keys(entities).filter(function(i) {
- return !base.entities[i];
- });
-
for (var i = 0; i < stack.length; i++) {
stack[i].graph.rebase(entities);
}
- tree.rebase(newentities);
+ tree.rebase(entities);
dispatch.change(undefined, extent);
},
@@ -21037,10 +21412,6 @@ iD.History = function(context) {
return this.difference().length() > 0;
},
- numChanges: function() {
- return this.difference().length();
- },
-
imageryUsed: function(sources) {
if (sources) {
imageryUsed = sources;
@@ -21116,14 +21487,18 @@ iD.History = function(context) {
stack = h.stack.map(function(d) {
var entities = {}, entity;
- d.modified && d.modified.forEach(function(key) {
- entity = allEntities[key];
- entities[entity.id] = entity;
- });
+ if (d.modified) {
+ d.modified.forEach(function(key) {
+ entity = allEntities[key];
+ entities[entity.id] = entity;
+ });
+ }
- d.deleted && d.deleted.forEach(function(id) {
- entities[id] = undefined;
- });
+ if (d.deleted) {
+ d.deleted.forEach(function(id) {
+ entities[id] = undefined;
+ });
+ }
return {
graph: iD.Graph(stack[0].graph).load(entities),
@@ -21152,39 +21527,37 @@ iD.History = function(context) {
},
save: function() {
- if (!lock) return history;
- context.storage(getKey('lock'), null);
- context.storage(getKey('saved_history'), this.toJSON() || null);
+ if (lock.locked()) context.storage(getKey('saved_history'), history.toJSON() || null);
return history;
},
clearSaved: function() {
- if (!lock) return;
- context.storage(getKey('saved_history'), null);
+ if (lock.locked()) context.storage(getKey('saved_history'), null);
+ return history;
},
lock: function() {
- if (context.storage(getKey('lock'))) return false;
- context.storage(getKey('lock'), true);
- lock = true;
- return lock;
+ return lock.lock();
+ },
+
+ unlock: function() {
+ lock.unlock();
},
// is iD not open in another window and it detects that
// there's a history stored in localStorage that's recoverable?
restorableChanges: function() {
- return lock && !!context.storage(getKey('saved_history'));
+ return lock.locked() && !!context.storage(getKey('saved_history'));
},
// load history from a version stored in localStorage
restore: function() {
- if (!lock) return;
+ if (!lock.locked()) return;
var json = context.storage(getKey('saved_history'));
- if (json) this.fromJSON(json);
+ if (json) history.fromJSON(json);
context.storage(getKey('saved_history', null));
-
},
_getKey: getKey
@@ -21206,7 +21579,7 @@ iD.Node = iD.Entity.node = function iD_Node() {
iD.Node.prototype = Object.create(iD.Entity.prototype);
_.extend(iD.Node.prototype, {
- type: "node",
+ type: 'node',
extent: function() {
return new iD.geo.Extent(this.loc);
@@ -21252,12 +21625,8 @@ _.extend(iD.Node.prototype, {
asGeoJSON: function() {
return {
- type: 'Feature',
- properties: this.tags,
- geometry: {
- type: 'Point',
- coordinates: this.loc
- }
+ type: 'Point',
+ coordinates: this.loc
};
}
});
@@ -21272,7 +21641,7 @@ iD.Relation = iD.Entity.relation = function iD_Relation() {
iD.Relation.prototype = Object.create(iD.Entity.prototype);
_.extend(iD.Relation.prototype, {
- type: "relation",
+ type: 'relation',
members: [],
extent: function(resolver) {
@@ -21303,7 +21672,7 @@ _.extend(iD.Relation.prototype, {
indexedMembers: function() {
var result = new Array(this.members.length);
for (var i = 0; i < this.members.length; i++) {
- result[i] = _.extend({}, this.members[i], {index: i})
+ result[i] = _.extend({}, this.members[i], {index: i});
}
return result;
},
@@ -21404,12 +21773,8 @@ _.extend(iD.Relation.prototype, {
return resolver.transient(this, 'GeoJSON', function () {
if (this.isMultipolygon()) {
return {
- type: 'Feature',
- properties: this.tags,
- geometry: {
- type: 'MultiPolygon',
- coordinates: this.multipolygon(resolver)
- }
+ type: 'MultiPolygon',
+ coordinates: this.multipolygon(resolver)
};
} else {
return {
@@ -21423,6 +21788,12 @@ _.extend(iD.Relation.prototype, {
});
},
+ area: function(resolver) {
+ return resolver.transient(this, 'area', function() {
+ return d3.geo.area(this.asGeoJSON(resolver));
+ });
+ },
+
isMultipolygon: function() {
return this.tags.type === 'multipolygon';
},
@@ -21499,14 +21870,9 @@ _.extend(iD.Relation.prototype, {
return result;
}
});
-iD.Tree = function(graph) {
-
+iD.Tree = function(head) {
var rtree = rbush(),
- head = graph,
- queuedCreated = [],
- queuedModified = [],
- rectangles = {},
- rebased;
+ rectangles = {};
function extentRectangle(extent) {
return [
@@ -21524,90 +21890,69 @@ iD.Tree = function(graph) {
return rect;
}
- function remove(entity) {
- rtree.remove(rectangles[entity.id]);
- delete rectangles[entity.id];
- }
-
- function bulkInsert(entities) {
- for (var i = 0, rects = []; i < entities.length; i++) {
- rects.push(entityRectangle(entities[i]));
- }
- rtree.load(rects);
- }
-
- function bulkReinsert(entities) {
- entities.forEach(remove);
- bulkInsert(entities);
- }
-
- var tree = {
-
- rebase: function(entities) {
- for (var i = 0, inserted = []; i < entities.length; i++) {
- if (!graph.entities.hasOwnProperty(entities[i])) {
- inserted.push(graph.entity(entities[i]));
- }
+ function updateParents(entity, insertions) {
+ head.parentWays(entity).forEach(function(parent) {
+ if (rectangles[parent.id]) {
+ rtree.remove(rectangles[parent.id]);
+ insertions.push(entityRectangle(parent));
}
- bulkInsert(inserted);
- rebased = true;
- return tree;
- },
+ });
- intersects: function(extent, g) {
+ head.parentRelations(entity).forEach(function(parent) {
+ if (rectangles[parent.id]) {
+ rtree.remove(rectangles[parent.id]);
+ insertions.push(entityRectangle(parent));
+ }
+ updateParents(parent, insertions);
+ });
+ }
- head = g;
+ var tree = {};
- if (graph !== head || rebased) {
- var diff = iD.Difference(graph, head),
- modified = {};
+ tree.rebase = function(entities) {
+ var insertions = [];
- diff.modified().forEach(function(d) {
- var loc = graph.entities[d.id].loc;
- if (!loc || loc[0] !== d.loc[0] || loc[1] !== d.loc[1]) {
- modified[d.id] = d;
- }
- });
+ entities.forEach(function(entity) {
+ if (head.entities.hasOwnProperty(entity.id) || rectangles[entity.id])
+ return;
- var created = diff.created().concat(queuedCreated);
- modified = d3.values(diff.addParents(modified))
- // some parents might be created, not modified
- .filter(function(d) { return !!graph.hasEntity(d.id); })
- .concat(queuedModified);
- queuedCreated = [];
- queuedModified = [];
+ insertions.push(entityRectangle(entity));
+ updateParents(entity, insertions);
+ });
- var reinserted = [],
- inserted = [];
+ rtree.load(insertions);
- modified.forEach(function(d) {
- if (head.hasAllChildren(d)) reinserted.push(d);
- else queuedModified.push(d);
- });
+ return tree;
+ };
- created.forEach(function(d) {
- if (head.hasAllChildren(d)) inserted.push(d);
- else queuedCreated.push(d);
- });
+ tree.intersects = function(extent, graph) {
+ if (graph !== head) {
+ var diff = iD.Difference(head, graph),
+ insertions = [];
- bulkReinsert(reinserted);
- bulkInsert(inserted);
+ head = graph;
- diff.deleted().forEach(remove);
+ diff.deleted().forEach(function(entity) {
+ rtree.remove(rectangles[entity.id]);
+ delete rectangles[entity.id];
+ });
- graph = head;
- rebased = false;
- }
+ diff.modified().forEach(function(entity) {
+ rtree.remove(rectangles[entity.id]);
+ insertions.push(entityRectangle(entity));
+ updateParents(entity, insertions);
+ });
- return rtree.search(extentRectangle(extent)).map(function (rect) {
- return graph.entities[rect.id];
+ diff.created().forEach(function(entity) {
+ insertions.push(entityRectangle(entity));
});
- },
- graph: function() {
- return graph;
+ rtree.load(insertions);
}
+ return rtree.search(extentRectangle(extent)).map(function(rect) {
+ return head.entity(rect.id);
+ });
};
return tree;
@@ -21623,13 +21968,18 @@ iD.Way = iD.Entity.way = function iD_Way() {
iD.Way.prototype = Object.create(iD.Entity.prototype);
_.extend(iD.Way.prototype, {
- type: "way",
+ type: 'way',
nodes: [],
extent: function(resolver) {
return resolver.transient(this, 'extent', function() {
return this.nodes.reduce(function(extent, id) {
- return extent.extend(resolver.entity(id).extent(resolver));
+ var node = resolver.hasEntity(id);
+ if (node) {
+ return extent.extend(node.extent());
+ } else {
+ return extent;
+ }
}, iD.geo.Extent());
});
},
@@ -21725,13 +22075,13 @@ _.extend(iD.Way.prototype, {
for (var i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i];
- if (node != id && nodes[nodes.length - 1] != node) {
+ if (node !== id && nodes[nodes.length - 1] !== node) {
nodes.push(node);
}
}
// Preserve circularity
- if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] != nodes[0]) {
+ if (this.nodes.length > 1 && this.first() === id && this.last() === id && nodes[nodes.length - 1] !== nodes[0]) {
nodes.push(nodes[0]);
}
@@ -21755,41 +22105,46 @@ _.extend(iD.Way.prototype, {
return r;
},
- asGeoJSON: function(resolver, polygon) {
+ asGeoJSON: function(resolver) {
return resolver.transient(this, 'GeoJSON', function() {
+ var coordinates = _.pluck(resolver.childNodes(this), 'loc');
+ if (this.isArea() && this.isClosed()) {
+ return {
+ type: 'Polygon',
+ coordinates: [coordinates]
+ };
+ } else {
+ return {
+ type: 'LineString',
+ coordinates: coordinates
+ };
+ }
+ });
+ },
+
+ area: function(resolver) {
+ return resolver.transient(this, 'area', function() {
var nodes = resolver.childNodes(this);
- if (this.isArea() && polygon && nodes.length >= 4) {
- if (!this.isClosed()) {
- nodes = nodes.concat([nodes[0]]);
- }
+ if (!this.isClosed() && nodes.length) {
+ nodes = nodes.concat([nodes[0]]);
+ }
- var json = {
- type: 'Feature',
- properties: this.tags,
- geometry: {
- type: 'Polygon',
- coordinates: [_.pluck(nodes, 'loc')]
- }
- };
+ var json = {
+ type: 'Polygon',
+ coordinates: [_.pluck(nodes, 'loc')]
+ };
- // Heuristic for detecting counterclockwise winding order. Assumes
- // that OpenStreetMap polygons are not hemisphere-spanning.
- if (d3.geo.area(json) > 2 * Math.PI) {
- json.geometry.coordinates[0] = json.geometry.coordinates[0].reverse();
- }
+ var area = d3.geo.area(json);
- return json;
- } else {
- return {
- type: 'Feature',
- properties: this.tags,
- geometry: {
- type: 'LineString',
- coordinates: _.pluck(nodes, 'loc')
- }
- };
+ // Heuristic for detecting counterclockwise winding order. Assumes
+ // that OpenStreetMap polygons are not hemisphere-spanning.
+ if (d3.geo.area(json) > 2 * Math.PI) {
+ json.coordinates[0] = json.coordinates[0].reverse();
+ area = d3.geo.area(json);
}
+
+ return isNaN(area) ? 0 : area;
});
}
});
@@ -21835,6 +22190,8 @@ iD.Background = function(context) {
}
});
+ backgroundSources.unshift(iD.BackgroundSource.None());
+
function findSource(id) {
return _.find(backgroundSources, function(d) {
return d.id && d.id === id;
@@ -21847,7 +22204,7 @@ iD.Background = function(context) {
q = iD.util.stringQs(location.hash.substring(1));
var id = b.id;
- if (!id && b.name === 'Custom') {
+ if (id === 'custom') {
id = 'custom:' + b.template;
}
@@ -21865,17 +22222,12 @@ iD.Background = function(context) {
location.replace('#' + iD.util.qsString(q, true));
- var imageryUsed = [];
- if (b.name === 'Custom') {
- imageryUsed.push('Custom (' + b.template + ')');
- } else {
- imageryUsed.push(b.id || b.name);
- }
+ var imageryUsed = [b.imageryUsed()];
overlayLayers.forEach(function (d) {
var source = d.source();
if (!source.isLocatorOverlay()) {
- imageryUsed.push(source.id || source.name);
+ imageryUsed.push(source.imageryUsed());
}
});
@@ -21904,7 +22256,7 @@ iD.Background = function(context) {
gpx.call(gpxLayer);
var overlays = selection.selectAll('.overlay-layer')
- .data(overlayLayers, function(d) { return d.source().name });
+ .data(overlayLayers, function(d) { return d.source().name(); });
overlays.enter().insert('div', '.layer-data')
.attr('class', 'layer-layer overlay-layer');
@@ -21943,7 +22295,7 @@ iD.Background = function(context) {
};
background.bing = function() {
- background.baseLayerSource(findSource("Bing"));
+ background.baseLayerSource(findSource('Bing'));
};
background.hasGpxLayer = function() {
@@ -21954,6 +22306,23 @@ iD.Background = function(context) {
return background.hasGpxLayer() && gpxLayer.enable();
};
+ function toDom(x) {
+ return (new DOMParser()).parseFromString(x, 'text/xml');
+ }
+
+ background.gpxLayerFiles = function(fileList) {
+ var f = fileList[0],
+ reader = new FileReader();
+
+ reader.onload = function(e) {
+ gpxLayer.geojson(toGeoJSON.gpx(toDom(e.target.result)));
+ dispatch.change();
+ context.map().pan([0, 0]);
+ };
+
+ reader.readAsText(f);
+ };
+
background.zoomToGpxLayer = function() {
if (background.hasGpxLayer()) {
context.map()
@@ -21968,10 +22337,14 @@ iD.Background = function(context) {
background.showsLayer = function(d) {
return d === baseLayer.source() ||
- (d.name === 'Custom' && baseLayer.source().name === 'Custom') ||
+ (d.id === 'custom' && baseLayer.source().id === 'custom') ||
overlayLayers.some(function(l) { return l.source() === d; });
};
+ background.overlayLayerSources = function() {
+ return overlayLayers.map(function (l) { return l.source(); });
+ };
+
background.toggleOverlayLayer = function(d) {
var layer;
@@ -22012,12 +22385,9 @@ iD.Background = function(context) {
chosen = q.background || q.layer;
if (chosen && chosen.indexOf('custom:') === 0) {
- background.baseLayerSource(iD.BackgroundSource({
- template: chosen.replace(/^custom:/, ''),
- name: 'Custom'
- }));
+ background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
} else {
- background.baseLayerSource(findSource(chosen) || findSource("Bing"));
+ background.baseLayerSource(findSource(chosen) || findSource('Bing'));
}
var locator = _.find(backgroundSources, function(d) {
@@ -22038,7 +22408,8 @@ iD.Background = function(context) {
};
iD.BackgroundSource = function(data) {
var source = _.clone(data),
- offset = [0, 0];
+ offset = [0, 0],
+ name = source.name;
source.scaleExtent = data.scaleExtent || [0, 20];
@@ -22054,6 +22425,14 @@ iD.BackgroundSource = function(data) {
return source;
};
+ source.name = function() {
+ return name;
+ };
+
+ source.imageryUsed = function() {
+ return source.id || name;
+ };
+
source.url = function(coord) {
return data.template
.replace('{x}', coord[0])
@@ -22080,7 +22459,7 @@ iD.BackgroundSource = function(data) {
};
source.isLocatorOverlay = function() {
- return source.name === 'Locator Overlay';
+ return name === 'Locator Overlay';
};
source.copyrightNotices = function() {};
@@ -22113,7 +22492,7 @@ iD.BackgroundSource.Bing = function(data, dispatch) {
dispatch.change();
});
- var template = "http://ecn.t{t}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z",
+ var template = 'http://ecn.t{t}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z',
subdomains = [0, 1, 2, 3];
bing.url = function(coord) {
@@ -22145,12 +22524,40 @@ iD.BackgroundSource.Bing = function(data, dispatch) {
}).join(', ');
};
- bing.logo = "bing_maps.png";
- bing.terms_url = "http://opengeodata.org/microsoft-imagery-details";
+ bing.logo = 'bing_maps.png';
+ bing.terms_url = 'http://opengeodata.org/microsoft-imagery-details';
return bing;
};
-iD.GpxLayer = function(context, dispatch) {
+
+iD.BackgroundSource.None = function() {
+ var source = iD.BackgroundSource({id: 'none', template: ''});
+
+ source.name = function() {
+ return t('background.none');
+ };
+
+ source.imageryUsed = function() {
+ return 'None';
+ };
+
+ return source;
+};
+
+iD.BackgroundSource.Custom = function(template) {
+ var source = iD.BackgroundSource({id: 'custom', template: template});
+
+ source.name = function() {
+ return t('background.custom');
+ };
+
+ source.imageryUsed = function() {
+ return 'Custom (' + template + ')';
+ };
+
+ return source;
+};
+iD.GpxLayer = function(context) {
var projection,
gj = {},
enable = true,
@@ -22205,10 +22612,6 @@ iD.GpxLayer = function(context, dispatch) {
}
}
- function toDom(x) {
- return (new DOMParser()).parseFromString(x, 'text/xml');
- }
-
render.projection = function(_) {
if (!arguments.length) return projection;
projection = _;
@@ -22247,16 +22650,7 @@ iD.GpxLayer = function(context, dispatch) {
d3.event.stopPropagation();
d3.event.preventDefault();
if (!iD.detect().filedrop) return;
- var f = d3.event.dataTransfer.files[0],
- reader = new FileReader();
-
- reader.onload = function(e) {
- render.geojson(toGeoJSON.gpx(toDom(e.target.result)));
- dispatch.change();
- context.map().pan([0, 0]);
- };
-
- reader.readAsText(f);
+ context.background().gpxLayerFiles(d3.event.dataTransfer.files);
})
.on('dragenter.localgpx', over)
.on('dragexit.localgpx', over)
@@ -22282,9 +22676,9 @@ iD.Map = function(context) {
points = iD.svg.Points(roundedProjection, context),
vertices = iD.svg.Vertices(roundedProjection, context),
lines = iD.svg.Lines(projection),
- areas = iD.svg.Areas(roundedProjection),
+ areas = iD.svg.Areas(projection),
midpoints = iD.svg.Midpoints(roundedProjection, context),
- labels = iD.svg.Labels(roundedProjection, context),
+ labels = iD.svg.Labels(projection, context),
supersurface, surface,
mouse,
mousemove;
@@ -22309,7 +22703,7 @@ iD.Map = function(context) {
map.surface = surface = dataLayer.append('svg')
.on('mousedown.zoom', function() {
- if (d3.event.button == 2) {
+ if (d3.event.button === 2) {
d3.event.stopPropagation();
}
}, true)
@@ -22480,7 +22874,8 @@ iD.Map = function(context) {
var zoom = String(~~map.zoom());
if (surface.attr('data-zoom') !== zoom) {
- surface.attr('data-zoom', zoom);
+ surface.attr('data-zoom', zoom)
+ .classed('low-zoom', zoom <= 16);
}
if (!difference) {
@@ -22521,7 +22916,7 @@ iD.Map = function(context) {
map.mouse = function() {
var e = mousemove || d3.event, s;
- while (s = e.sourceEvent) e = s;
+ while ((s = e.sourceEvent)) e = s;
return mouse(e);
};
@@ -22535,10 +22930,10 @@ iD.Map = function(context) {
return map;
};
- function setZoom(z, force) {
- if (z === map.zoom() && !force)
+ function setZoom(_, force) {
+ if (_ === map.zoom() && !force)
return false;
- var scale = 256 * Math.pow(2, z),
+ var scale = 256 * Math.pow(2, _),
center = pxCenter(),
l = pointLocation(center);
scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
@@ -22553,15 +22948,16 @@ iD.Map = function(context) {
return true;
}
- function setCenter(loc) {
- var t = projection.translate(),
- c = pxCenter(),
- ll = projection(loc);
- if (ll[0] === c[0] && ll[1] === c[1])
+ function setCenter(_) {
+ var c = map.center();
+ if (_[0] === c[0] && _[1] === c[1])
return false;
+ var t = projection.translate(),
+ pxC = pxCenter(),
+ ll = projection(_);
projection.translate([
- t[0] - ll[0] + c[0],
- t[1] - ll[1] + c[1]]);
+ t[0] - ll[0] + pxC[0],
+ t[1] - ll[1] + pxC[1]]);
zoom.translate(projection.translate());
return true;
}
@@ -22645,7 +23041,7 @@ iD.Map = function(context) {
d3.timer(function() {
if (stop) return true;
map.center(iD.geo.interp(from, loc, (t += 1) / 10));
- return t == 10;
+ return t === 10;
}, 20);
return map;
};
@@ -22757,6 +23153,7 @@ iD.TileLayer = function() {
if (source.validZoom(z)) {
tile().forEach(function(d) {
addSource(d);
+ if (d[3] === '') return;
requests.push(d);
if (cache[d[3]] === false && lookUp(d)) {
requests.push(addSource(lookUp(d)));
@@ -22865,29 +23262,27 @@ iD.svg = {
};
},
+ Round: function () {
+ return d3.geo.transform({
+ point: function(x, y) { return this.stream.point(Math.floor(x), Math.floor(y)); }
+ });
+ },
+
Path: function(projection, graph, polygon) {
var cache = {},
- path = d3.geo.path().projection(projection);
-
- function result(entity) {
- if (entity.id in cache) return cache[entity.id];
+ round = iD.svg.Round().stream,
+ clip = d3.geo.clipExtent().extent(projection.clipExtent()).stream,
+ project = projection.stream,
+ path = d3.geo.path()
+ .projection({stream: function(output) { return polygon ? project(round(output)) : project(clip(round(output))); }});
- var buffer = '';
-
- path.context({
- beginPath: function() {},
- moveTo: function(x, y) { buffer += 'M' + Math.floor(x) + ',' + Math.floor(y); },
- lineTo: function(x, y) { buffer += 'L' + Math.floor(x) + ',' + Math.floor(y); },
- arc: function() {},
- closePath: function() { buffer += 'Z'; }
- });
-
- path(entity.asGeoJSON(graph, polygon));
-
- return cache[entity.id] = buffer;
- }
-
- return result;
+ return function(entity) {
+ if (entity.id in cache) {
+ return cache[entity.id];
+ } else {
+ return cache[entity.id] = path(entity.asGeoJSON(graph)); // jshint ignore:line
+ }
+ };
},
OneWaySegments: function(projection, graph, dt) {
@@ -22962,7 +23357,8 @@ iD.svg = {
}
};
iD.svg.Areas = function(projection) {
- // Patterns only work in Firefox when set directly on element
+ // 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 = {
wetland: 'wetland',
beach: 'beach',
@@ -22997,7 +23393,8 @@ iD.svg.Areas = function(projection) {
var entity = entities[i];
if (entity.geometry(graph) !== 'area') continue;
- if (multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph)) {
+ multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph);
+ if (multipolygon) {
areas[multipolygon.id] = {
entity: multipolygon.mergeTags(entity.tags),
area: Math.abs(entity.area(graph))
@@ -23024,23 +23421,28 @@ iD.svg.Areas = function(projection) {
fill: areas
};
+ var paths = surface.selectAll('.layer-shadow, .layer-stroke, .layer-fill')
+ .selectAll('path.area')
+ .filter(filter)
+ .data(function(layer) { return data[layer]; }, iD.Entity.key);
+
+ // Remove exiting areas first, so they aren't included in the `fills`
+ // array used for sorting below (https://github.com/systemed/iD/issues/1903).
+ paths.exit()
+ .remove();
+
+ var fills = surface.selectAll('.layer-fill path.area')[0];
+
var bisect = d3.bisector(function(node) {
return -node.__data__.area(graph);
}).left;
- var fills = surface.selectAll('.layer-fill path.area')[0];
-
function sortedByArea(entity) {
if (this.__data__ === 'fill') {
return fills[bisect(fills, -entity.area(graph))];
}
}
- var paths = surface.selectAll('.layer-shadow, .layer-stroke, .layer-fill')
- .selectAll('path.area')
- .filter(filter)
- .data(function(layer) { return data[layer]; }, iD.Entity.key);
-
paths.enter()
.insert('path', sortedByArea)
.each(function(entity) {
@@ -23056,12 +23458,10 @@ iD.svg.Areas = function(projection) {
paths
.attr('d', path);
-
- paths.exit()
- .remove();
};
};
iD.svg.Labels = function(projection, context) {
+ var path = d3.geo.path().projection(projection);
// Replace with dict and iterate over entities tags instead?
var label_stack = [
@@ -23096,11 +23496,11 @@ iD.svg.Labels = function(projection, context) {
var font_sizes = label_stack.map(function(d) {
var style = iD.util.getStyle('text.' + d[0] + '.tag-' + d[1]),
- m = style && style.cssText.match("font-size: ([0-9]{1,2})px;");
+ m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
if (m) return parseInt(m[1], 10);
style = iD.util.getStyle('text.' + d[0]);
- m = style && style.cssText.match("font-size: ([0-9]{1,2})px;");
+ m = style && style.cssText.match('font-size: ([0-9]{1,2})px;');
if (m) return parseInt(m[1], 10);
return default_size;
@@ -23153,19 +23553,18 @@ iD.svg.Labels = function(projection, context) {
}
function drawLineLabels(group, entities, filter, classes, labels) {
-
var texts = group.selectAll('text.' + classes)
.filter(filter)
.data(entities, iD.Entity.key);
- var tp = texts.enter()
+ texts.enter()
.append('text')
.attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
.append('textPath')
.attr('class', 'textpath');
- var tps = texts.selectAll('.textpath')
+ texts.selectAll('.textpath')
.filter(filter)
.data(entities, iD.Entity.key)
.attr({
@@ -23175,11 +23574,9 @@ iD.svg.Labels = function(projection, context) {
.text(iD.util.displayName);
texts.exit().remove();
-
}
function drawLinePaths(group, entities, filter, classes, labels) {
-
var halos = group.selectAll('path')
.filter(filter)
.data(entities, iD.Entity.key);
@@ -23431,8 +23828,7 @@ iD.svg.Labels = function(projection, context) {
}
function getAreaLabel(entity, width, height) {
- var path = d3.geo.path().projection(projection),
- centroid = path.centroid(entity.asGeoJSON(graph, true)),
+ var centroid = path.centroid(entity.asGeoJSON(graph, true)),
extent = entity.extent(graph),
entitywidth = projection(extent[1])[0] - projection(extent[0])[0],
rect;
@@ -23769,7 +24165,7 @@ iD.svg.Restrictions = function(context) {
}
drawRestrictions.turns = function (graph, selectedIDs) {
- if (selectedIDs.length != 1)
+ if (selectedIDs.length !== 1)
return [];
var from = graph.entity(selectedIDs[0]);
@@ -23810,7 +24206,7 @@ iD.svg.Restrictions = function(context) {
to: to,
restriction: restriction,
angle: Math.atan2(b[1] - a[1], b[0] - a[0])
- }
+ };
};
return drawRestrictions;
@@ -23838,7 +24234,7 @@ iD.svg.Surface = function(context) {
.data(data)
.enter().append('use')
.attr('id', function(d) { return d.key; })
- .attr('transform', function(d) { return "translate(-" + d.value[0] + ",-" + d.value[1] + ")"; })
+ .attr('transform', function(d) { return 'translate(-' + d.value[0] + ',-' + d.value[1] + ')'; })
.attr('xlink:href', '#' + id);
};
}
@@ -23934,11 +24330,15 @@ iD.svg.Surface = function(context) {
};
};
iD.svg.TagClasses = function() {
- var keys = d3.set([
- 'highway', 'railway', 'waterway', 'power', 'motorway', 'amenity',
- 'natural', 'landuse', 'building', 'oneway', 'bridge', 'boundary',
- 'tunnel', 'leisure', 'construction', 'place', 'aeroway'
- ]), tagClassRe = /^tag-/,
+ var primary = [
+ 'highway', 'railway', 'waterway', 'aeroway', 'motorway',
+ 'boundary', 'power', 'amenity', 'natural', 'landuse',
+ 'building', 'leisure', 'place'
+ ],
+ secondary = [
+ 'oneway', 'bridge', 'tunnel', 'construction'
+ ],
+ tagClassRe = /^tag-/,
tags = function(entity) { return entity.tags; };
var tagClasses = function(selection) {
@@ -23951,10 +24351,21 @@ iD.svg.TagClasses = function() {
return name.length && !tagClassRe.test(name);
}).join(' ');
- var t = tags(entity);
- for (var k in t) {
- if (!keys.has(k) || t[k] === 'no') continue;
- classes += ' tag-' + k + ' tag-' + k + '-' + t[k];
+ var t = tags(entity), i, k, v;
+
+ for (i = 0; i < primary.length; i++) {
+ k = primary[i];
+ v = t[k];
+ if (!v || v === 'no') continue;
+ classes += ' tag-' + k + ' tag-' + k + '-' + v;
+ break;
+ }
+
+ for (i = 0; i < secondary.length; i++) {
+ k = secondary[i];
+ v = t[k];
+ if (!v || v === 'no') continue;
+ classes += ' tag-' + k + ' tag-' + k + '-' + v;
}
classes = classes.trim();
@@ -24035,9 +24446,10 @@ iD.svg.Vertices = function(projection, context) {
var icons = {};
function icon(entity) {
if (entity.id in icons) return icons[entity.id];
- return icons[entity.id] = (zoom !== 0 &&
+ icons[entity.id] = zoom !== 0 &&
entity.hasInterestingTags() &&
- context.presets().match(entity, graph).icon);
+ context.presets().match(entity, graph).icon;
+ return icons[entity.id];
}
function circle(klass) {
@@ -24050,7 +24462,7 @@ iD.svg.Vertices = function(projection, context) {
this.setAttribute('cx', c);
this.setAttribute('cy', -c);
this.setAttribute('r', r);
- }
+ };
}
var enter = groups.enter().append('g')
@@ -24095,7 +24507,7 @@ iD.svg.Vertices = function(projection, context) {
if (entity.id in selected ||
entity.hasInterestingTags() ||
entity.isIntersection(graph)) {
- vertices.push(entity)
+ vertices.push(entity);
}
}
@@ -24124,8 +24536,7 @@ iD.svg.Vertices = function(projection, context) {
};
iD.ui = function(context) {
function render(container) {
- var history = context.history(),
- map = context.map();
+ var map = context.map();
if (iD.detect().opera) container.classed('opera', true);
@@ -24153,7 +24564,7 @@ iD.ui = function(context) {
.attr('id', 'map')
.call(map);
- var spacer = bar.append('div')
+ bar.append('div')
.attr('class', 'spacer col4');
var limiter = bar.append('div')
@@ -24175,14 +24586,12 @@ iD.ui = function(context) {
.attr('class', 'spinner')
.call(iD.ui.Spinner(context));
- content.append('div')
- .attr('class', 'attribution')
- .attr('tabindex', -1)
+ content
.call(iD.ui.Attribution(context));
content.append('div')
.style('display', 'none')
- .attr('class', 'help-wrap fillL col5 content');
+ .attr('class', 'help-wrap map-overlay fillL col5 content');
var controls = bar.append('div')
.attr('class', 'map-controls');
@@ -24250,6 +24659,10 @@ iD.ui = function(context) {
return context.save();
};
+ window.onunload = function() {
+ context.history().unlock();
+ };
+
d3.select(window).on('resize.editor', function() {
map.dimensions(m.dimensions());
});
@@ -24363,20 +24776,28 @@ iD.ui.Account = function(context) {
iD.ui.Attribution = function(context) {
var selection;
- function update() {
- if (!context.background().baseLayerSource()) {
- selection.html('');
- return;
- }
+ function attribution(data, klass) {
+ var div = selection.selectAll('.' + klass)
+ .data([0]);
+
+ div.enter()
+ .append('div')
+ .attr('class', klass);
- var attribution = selection.selectAll('.provided-by')
- .data([context.background().baseLayerSource()], function(d) { return d.name; });
+ var background = div.selectAll('.attribution')
+ .data(data, function(d) { return d.name(); });
- attribution.enter()
+ background.enter()
.append('span')
- .attr('class', 'provided-by')
+ .attr('class', 'attribution')
.each(function(d) {
- var source = d.terms_text || d.id || d.name;
+ if (d.terms_html) {
+ d3.select(this)
+ .html(d.terms_html);
+ return;
+ }
+
+ var source = d.terms_text || d.id || d.name();
if (d.logo) {
source = '';
@@ -24394,10 +24815,10 @@ iD.ui.Attribution = function(context) {
}
});
- attribution.exit()
+ background.exit()
.remove();
- var copyright = attribution.selectAll('.copyright-notice')
+ var copyright = background.selectAll('.copyright-notice')
.data(function(d) {
var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
return notice ? [notice] : [];
@@ -24413,6 +24834,13 @@ iD.ui.Attribution = function(context) {
.remove();
}
+ function update() {
+ attribution([context.background().baseLayerSource()], 'base-layer-attribution');
+ attribution(context.background().overlayLayerSources().filter(function (s) {
+ return s.validZoom(context.map().zoom());
+ }), 'overlay-layer-attribution');
+ }
+
return function(select) {
selection = select;
@@ -24427,15 +24855,18 @@ iD.ui.Attribution = function(context) {
};
iD.ui.Background = function(context) {
var key = 'b',
- opacities = [1, 0.5, 0],
+ opacities = [1, 0.75, 0.5, 0.25],
directions = [
['left', [1, 0]],
['top', [0, -1]],
['right', [-1, 0]],
['bottom', [0, 1]]],
- opacityDefault = (context.storage('background-opacity') != undefined) ?
+ opacityDefault = (context.storage('background-opacity') !== null) ?
(+context.storage('background-opacity')) : 0.5;
+ // Can be 0 from <1.3.0 use or due to issue #1923.
+ if (opacityDefault === 0) opacityDefault = 0.5;
+
function background(selection) {
function setOpacity(d) {
@@ -24455,7 +24886,7 @@ iD.ui.Background = function(context) {
return context.background().showsLayer(d);
}
- content.selectAll('label.layer, label.custom_layer')
+ content.selectAll('.layer, .custom_layer')
.classed('active', active)
.selectAll('input')
.property('checked', active);
@@ -24470,14 +24901,13 @@ iD.ui.Background = function(context) {
function clickCustom() {
d3.event.preventDefault();
var template = window.prompt(t('background.custom_prompt'));
- if (!template) {
+ if (!template || template.indexOf('google.com') !== -1 ||
+ template.indexOf('googleapis.com') !== -1 ||
+ template.indexOf('google.ru') !== -1) {
selectLayer();
return;
}
- context.background().baseLayerSource(iD.BackgroundSource({
- template: template,
- name: 'Custom'
- }));
+ context.background().baseLayerSource(iD.BackgroundSource.Custom(template));
selectLayer();
}
@@ -24497,33 +24927,33 @@ iD.ui.Background = function(context) {
.sources(context.map().extent())
.filter(filter);
- var layerLinks = layerList.selectAll('label.layer')
- .data(sources, function(d) { return d.name; });
+ var layerLinks = layerList.selectAll('li.layer')
+ .data(sources, function(d) { return d.name(); });
- var layerInner = layerLinks.enter()
- .insert('label', '.custom_layer')
+ var enter = layerLinks.enter()
+ .insert('li', '.custom_layer')
.attr('class', 'layer');
// only set tooltips for layers with tooltips
- layerInner
- .filter(function(d) { return d.description; })
+ enter.filter(function(d) { return d.description; })
.call(bootstrap.tooltip()
.title(function(d) { return d.description; })
- .placement('left'));
+ .placement('top'));
- layerInner.append('input')
+ var label = enter.append('label');
+
+ label.append('input')
.attr('type', type)
.attr('name', 'layers')
- .attr('value', function(d) { return d.name; })
.on('change', change);
- layerInner.append('span')
- .text(function(d) { return d.name; });
+ label.append('span')
+ .text(function(d) { return d.name(); });
layerLinks.exit()
.remove();
- layerList.style('display', layerList.selectAll('label.layer').data().length > 0 ? 'block' : 'none');
+ layerList.style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
}
function update() {
@@ -24564,7 +24994,7 @@ iD.ui.Background = function(context) {
}
var content = selection.append('div')
- .attr('class', 'fillL map-overlay content hide'),
+ .attr('class', 'fillL map-overlay col3 content hide'),
tooltip = bootstrap.tooltip()
.placement('left')
.html(true)
@@ -24575,9 +25005,7 @@ iD.ui.Background = function(context) {
function toggle() {
if (d3.event) d3.event.preventDefault();
tooltip.hide(button);
- var visible = !button.classed('active');
- setVisible(visible);
- if (visible) content.selectAll('.toggle-list label:first-child').node().focus();
+ setVisible(!button.classed('active'));
}
function setVisible(show) {
@@ -24590,16 +25018,16 @@ iD.ui.Background = function(context) {
return d3.event.stopPropagation();
});
content.style('display', 'block')
- .style('left', '0px')
+ .style('right', '-300px')
.transition()
.duration(200)
- .style('left', '-260px');
+ .style('right', '0px');
} else {
content.style('display', 'block')
- .style('left', '-260px')
+ .style('right', '0px')
.transition()
.duration(200)
- .style('left', '0px')
+ .style('right', '-300px')
.each('end', function() {
d3.select(this).style('display', 'none');
});
@@ -24634,66 +25062,82 @@ iD.ui.Background = function(context) {
return t('background.percent_brightness', { opacity: (d * 100) });
})
.on('click.set-opacity', setOpacity)
- .html("