Skip to content

Commit

Permalink
Demote JSDOM to development dependency; fix #2190.
Browse files Browse the repository at this point in the history
Code that previously assumed a global document or window now uses the related
node’s ownerDocument or ownerDocument.defaultView as appropriate.

If no related node is available, the corresponding code will crash; however, the
rest of D3 will work just fine. For example, you can’t use d3.select(string)
unless a global document is available; it just doesn’t make sense. Use
d3.select(node) instead, followed by selection.select(string).

Code that previously checked for a global on the window (e.g., XDomainRequest)
now uses the global context (`this`) rather than the window.
  • Loading branch information
mbostock committed Feb 7, 2015
1 parent ca0b7cb commit 9c3df31
Show file tree
Hide file tree
Showing 22 changed files with 261 additions and 201 deletions.
196 changes: 114 additions & 82 deletions d3.js

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions d3.min.js

Large diffs are not rendered by default.

15 changes: 3 additions & 12 deletions index.js
@@ -1,18 +1,9 @@
var document = require("jsdom").jsdom(),
globals = {};
var globals = {};

// Stash old globals.
// Stash old global.
if ("d3" in global) globals.d3 = global.d3;
if ("window" in global) globals.window = global.window;
if ("document" in global) globals.document = global.document;

// Set temporary globals to pretend we’re in a browser.
global.window = document.parentWindow;
global.document = document;

module.exports = require("./d3");

// Restore old globals.
// Restore old global.
if ("d3" in globals) global.d3 = globals.d3; else delete global.d3;
if ("window" in globals) global.window = globals.window; else delete global.window;
if ("document" in globals) global.document = globals.document; else delete global.document;
12 changes: 5 additions & 7 deletions package.json
Expand Up @@ -50,14 +50,12 @@
"spm": {
"main": "d3.js"
},
"dependencies": {
"jsdom": "1.0.0"
},
"devDependencies": {
"smash": "~0.0.12",
"uglify-js": "2.4.0",
"vows": "0.7.0",
"seedrandom": "2.3.1"
"jsdom": "3",
"seedrandom": "2",
"smash": "0.0",
"uglify-js": "2.4",
"vows": "0.8"
},
"scripts": {
"test": "vows; echo"
Expand Down
17 changes: 5 additions & 12 deletions src/behavior/drag.js
@@ -1,4 +1,5 @@
import "../core/document";
import "../core/identity";
import "../core/rebind";
import "../event/drag";
import "../event/event";
Expand All @@ -9,8 +10,8 @@ import "behavior";
d3.behavior.drag = function() {
var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"),
origin = null,
mousedown = dragstart(d3_noop, d3.mouse, d3_behavior_dragMouseSubject, "mousemove", "mouseup"),
touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_behavior_dragTouchSubject, "touchmove", "touchend");
mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"),
touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend");

function drag() {
this.on("mousedown.drag", mousedown)
Expand All @@ -27,8 +28,8 @@ d3.behavior.drag = function() {
dragId = id(),
dragName = ".drag" + (dragId == null ? "" : "-" + dragId),
dragOffset,
dragSubject = d3.select(subject()).on(move + dragName, moved).on(end + dragName, ended),
dragRestore = d3_event_dragSuppress(),
dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended),
dragRestore = d3_event_dragSuppress(target),
position0 = position(parent, dragId);

if (origin) {
Expand Down Expand Up @@ -85,11 +86,3 @@ d3.behavior.drag = function() {
function d3_behavior_dragTouchId() {
return d3.event.changedTouches[0].identifier;
}

function d3_behavior_dragTouchSubject() {
return d3.event.target;
}

function d3_behavior_dragMouseSubject() {
return d3_window;
}
24 changes: 14 additions & 10 deletions src/behavior/zoom.js
Expand Up @@ -29,6 +29,14 @@ d3.behavior.zoom = function() {
y0,
y1;

// Lazily determine the DOM’s support for Wheel events.
// https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel
if (!d3_behavior_zoomWheel) {
d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel")
: "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel")
: (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll");
}

function zoom(g) {
g .on(mousedown, mousedowned)
.on(d3_behavior_zoomWheel + ".zoom", mousewheeled)
Expand Down Expand Up @@ -183,9 +191,9 @@ d3.behavior.zoom = function() {
target = d3.event.target,
dispatch = event.of(that, arguments),
dragged = 0,
subject = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended),
subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended),
location0 = location(d3.mouse(that)),
dragRestore = d3_event_dragSuppress();
dragRestore = d3_event_dragSuppress(that);

d3_selection_interrupt.call(that);
zoomstarted(dispatch);
Expand Down Expand Up @@ -215,7 +223,7 @@ d3.behavior.zoom = function() {
touchend = "touchend" + zoomName,
targets = [],
subject = d3.select(that),
dragRestore = d3_event_dragSuppress();
dragRestore = d3_event_dragSuppress(that);

started();
zoomstarted(dispatch);
Expand Down Expand Up @@ -336,10 +344,6 @@ d3.behavior.zoom = function() {
return d3.rebind(zoom, event, "on");
};

var d3_behavior_zoomInfinity = [0, Infinity]; // default scale extent

// https://developer.mozilla.org/en-US/docs/Mozilla_event_reference/wheel
var d3_behavior_zoomDelta, d3_behavior_zoomWheel
= "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); }, "wheel")
: "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { return d3.event.wheelDelta; }, "mousewheel")
: (d3_behavior_zoomDelta = function() { return -d3.event.detail; }, "MozMousePixelScroll");
var d3_behavior_zoomInfinity = [0, Infinity], // default scale extent
d3_behavior_zoomDelta, // initialized lazily
d3_behavior_zoomWheel;
15 changes: 15 additions & 0 deletions src/compat/array.js
@@ -0,0 +1,15 @@
import "../core/array";
import "../core/document";

// Redefine d3_array if the browser doesn’t support slice-based conversion.
if (d3_document) {
try {
d3_array(d3_document.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;
};
}
}
1 change: 1 addition & 0 deletions src/compat/index.js
@@ -1,2 +1,3 @@
import "array";
import "date";
import "style";
37 changes: 20 additions & 17 deletions src/compat/style.js
@@ -1,20 +1,23 @@
import "../core/document";

try {
d3_document.createElement("div").style.setProperty("opacity", 0, "");
} catch (error) {
var d3_element_prototype = d3_window.Element.prototype,
d3_element_setAttribute = d3_element_prototype.setAttribute,
d3_element_setAttributeNS = d3_element_prototype.setAttributeNS,
d3_style_prototype = d3_window.CSSStyleDeclaration.prototype,
d3_style_setProperty = d3_style_prototype.setProperty;
d3_element_prototype.setAttribute = function(name, value) {
d3_element_setAttribute.call(this, name, value + "");
};
d3_element_prototype.setAttributeNS = function(space, local, value) {
d3_element_setAttributeNS.call(this, space, local, value + "");
};
d3_style_prototype.setProperty = function(name, value, priority) {
d3_style_setProperty.call(this, name, value + "", priority);
};
// Redefine style.setProperty et al. if the browser doesn’t coerce arguments.
if (d3_document) {
try {
d3_document.createElement("DIV").style.setProperty("opacity", 0, "");
} catch (error) {
var d3_element_prototype = this.Element.prototype,
d3_element_setAttribute = d3_element_prototype.setAttribute,
d3_element_setAttributeNS = d3_element_prototype.setAttributeNS,
d3_style_prototype = this.CSSStyleDeclaration.prototype,
d3_style_setProperty = d3_style_prototype.setProperty;
d3_element_prototype.setAttribute = function(name, value) {
d3_element_setAttribute.call(this, name, value + "");
};
d3_element_prototype.setAttributeNS = function(space, local, value) {
d3_element_setAttributeNS.call(this, space, local, value + "");
};
d3_style_prototype.setProperty = function(name, value, priority) {
d3_style_setProperty.call(this, name, value + "", priority);
};
}
}
19 changes: 6 additions & 13 deletions src/core/document.js
@@ -1,16 +1,9 @@
import "array";
var d3_document = this.document;

var d3_document = document,
d3_documentElement = d3_document.documentElement,
d3_window = window;
function d3_documentElement(node) {
return node && node.ownerDocument.documentElement;
}

// Redefine d3_array if the browser doesn’t support slice-based conversion.
try {
d3_array(d3_documentElement.childNodes)[0].nodeType;
} catch(e) {
d3_array = function(list) {
var i = list.length, array = new Array(i);
while (i--) array[i] = list[i];
return array;
};
function d3_window(node) {
return node && node.ownerDocument.defaultView;
}
15 changes: 11 additions & 4 deletions src/event/drag.js
Expand Up @@ -2,21 +2,28 @@ import "../core/document";
import "../core/vendor";
import "../selection/on";

var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"),
var d3_event_dragSelect,
d3_event_dragId = 0;

function d3_event_dragSuppress() {
function d3_event_dragSuppress(node) {
var name = ".dragsuppress-" + ++d3_event_dragId,
click = "click" + name,
w = d3.select(d3_window)
w = d3.select(d3_window(node))
.on("touchmove" + name, d3_eventPreventDefault)
.on("dragstart" + name, d3_eventPreventDefault)
.on("selectstart" + name, d3_eventPreventDefault);

if (d3_event_dragSelect == null) {
d3_event_dragSelect = "onselectstart" in node ? false
: d3_vendorSymbol(node.style, "userSelect");
}

if (d3_event_dragSelect) {
var style = d3_documentElement.style,
var style = d3_documentElement(node).style,
select = style[d3_event_dragSelect];
style[d3_event_dragSelect] = "none";
}

return function(suppressClick) {
w.on(name, null);
if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
Expand Down
29 changes: 16 additions & 13 deletions src/event/mouse.js
Expand Up @@ -6,25 +6,28 @@ d3.mouse = function(container) {
};

// https://bugs.webkit.org/show_bug.cgi?id=44083
var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0;
var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;

function d3_mousePoint(container, e) {
if (e.changedTouches) e = e.changedTouches[0];
var svg = container.ownerSVGElement || container;
if (svg.createSVGPoint) {
var point = svg.createSVGPoint();
if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) {
svg = d3.select("body").append("svg").style({
position: "absolute",
top: 0,
left: 0,
margin: 0,
padding: 0,
border: "none"
}, "important");
var ctm = svg[0][0].getScreenCTM();
d3_mouse_bug44083 = !(ctm.f || ctm.e);
svg.remove();
if (d3_mouse_bug44083 < 0) {
var window = d3_window(container);
if (window.scrollX || window.scrollY) {
svg = d3.select("body").append("svg").style({
position: "absolute",
top: 0,
left: 0,
margin: 0,
padding: 0,
border: "none"
}, "important");
var ctm = svg[0][0].getScreenCTM();
d3_mouse_bug44083 = !(ctm.f || ctm.e);
svg.remove();
}
}
if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY;
else point.x = e.clientX, point.y = e.clientY;
Expand Down
2 changes: 1 addition & 1 deletion src/event/timer.js
Expand Up @@ -6,7 +6,7 @@ var d3_timer_queueHead,
d3_timer_interval, // is an interval (or frame) active?
d3_timer_timeout, // is a timeout active?
d3_timer_active, // active timer object
d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); };
d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); };

// The timer will continue to fire until callback returns true.
d3.timer = function(callback, delay, then) {
Expand Down
8 changes: 5 additions & 3 deletions src/selection/on.js
Expand Up @@ -76,9 +76,11 @@ var d3_selection_onFilters = d3.map({
mouseleave: "mouseout"
});

d3_selection_onFilters.forEach(function(k) {
if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
});
if (d3_document) {
d3_selection_onFilters.forEach(function(k) {
if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
});
}

function d3_selection_onListener(listener, argumentz) {
return function(e) {
Expand Down
33 changes: 24 additions & 9 deletions src/selection/selection.js
Expand Up @@ -10,8 +10,13 @@ function d3_selection(groups) {

var d3_select = function(s, n) { return n.querySelector(s); },
d3_selectAll = function(s, n) { return n.querySelectorAll(s); },
d3_selectMatcher = d3_documentElement.matches || d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")],
d3_selectMatches = function(n, s) { return d3_selectMatcher.call(n, s); };
d3_selectMatches = function(n, s) {
var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
d3_selectMatches = function(n, s) {
return d3_selectMatcher.call(n, s);
};
return d3_selectMatches(n, s);
};

// Prefer Sizzle, if available.
if (typeof Sizzle === "function") {
Expand All @@ -21,7 +26,7 @@ if (typeof Sizzle === "function") {
}

d3.selection = function() {
return d3_selectionRoot;
return d3.select(d3_document.documentElement);
};

var d3_selectionPrototype = d3.selection.prototype = [];
Expand Down Expand Up @@ -52,15 +57,25 @@ import "enter";

// TODO fast singleton implementation?
d3.select = function(node) {
var group = [typeof node === "string" ? d3_select(node, d3_document) : node];
group.parentNode = d3_documentElement;
var group;
if (typeof node === "string") {
group = [d3_select(node, d3_document)];
group.parentNode = d3_document.documentElement;
} else {
group = [node];
group.parentNode = d3_documentElement(node);
}
return d3_selection([group]);
};

d3.selectAll = function(nodes) {
var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
group.parentNode = d3_documentElement;
var group;
if (typeof nodes === "string") {
group = d3_array(d3_selectAll(nodes, d3_document));
group.parentNode = d3_document.documentElement;
} else {
group = nodes;
group.parentNode = null;
}
return d3_selection([group]);
};

var d3_selectionRoot = d3.select(d3_documentElement);
5 changes: 4 additions & 1 deletion src/selection/style.js
Expand Up @@ -16,7 +16,10 @@ d3_selectionPrototype.style = function(name, value, priority) {
}

// For style(string), return the computed style value for the first node.
if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name);
if (n < 2) {
var node = this.node();
return node.ownerDocument.defaultView.getComputedStyle(node, null).getPropertyValue(name);
}

// For style(string, string) or style(string, function), use the default
// priority. The priority is ignored for style(string, null).
Expand Down
4 changes: 2 additions & 2 deletions src/svg/brush.js
Expand Up @@ -151,12 +151,12 @@ d3.svg.brush = function() {
resizingX = !/^(n|s)$/.test(resizing) && x,
resizingY = !/^(e|w)$/.test(resizing) && y,
dragging = eventTarget.classed("extent"),
dragRestore = d3_event_dragSuppress(),
dragRestore = d3_event_dragSuppress(target),
center,
origin = d3.mouse(target),
offset;

var w = d3.select(d3_window)
var w = d3.select(d3_window(target))
.on("keydown.brush", keydown)
.on("keyup.brush", keyup);

Expand Down

0 comments on commit 9c3df31

Please sign in to comment.