UNPKG

d3-jsnext

Version:
823 lines (701 loc) 23.7 kB
import { d3_document, d3_documentElement, d3_window } from '../core/document'; import { d3_array } from '../core/array'; import { d3_subclass } from '../core/subclass'; import { d3_noop } from '../core/noop'; import { d3_ascending } from '../arrays/ascending'; import { d3_Map, d3$map } from '../arrays/map'; import { d3_collapse } from '../format/collapse'; import { d3_vendorSymbol } from '../core/vendor'; import { d3$requote } from '../format/requote'; import { d3$ns } from '../core/ns'; var d3$selectAll; var d3$select; var d3_selection_enter; var d3$selection; function d3_selection(groups) { d3_subclass(groups, d3_selectionPrototype); return groups; } var d3_select = function(s, n) { return n.querySelector(s); }, d3_selectAll = function(s, n) { return n.querySelectorAll(s); }, d3_selectMatches = function(n, s) { var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; d3_selectMatches = function(n, s) { return d3_selectMatcher.call(n, s); }; return d3_selectMatches(n, s); }; // Prefer Sizzle, if available. if (typeof Sizzle === "function") { d3_select = function(s, n) { return Sizzle(s, n)[0] || null; }; d3_selectAll = Sizzle; d3_selectMatches = Sizzle.matchesSelector; } d3$selection = function() { return d3$select(d3_document.documentElement); }; var d3_selectionPrototype = d3$selection.prototype = []; d3_selectionPrototype.select = function(selector) { var subgroups = [], subgroup, subnode, group, node; selector = d3_selection_selector(selector); for (var j = -1, m = this.length; ++j < m;) { subgroups.push(subgroup = []); subgroup.parentNode = (group = this[j]).parentNode; for (var i = -1, n = group.length; ++i < n;) { if (node = group[i]) { subgroup.push(subnode = selector.call(node, node.__data__, i, j)); if (subnode && "__data__" in node) subnode.__data__ = node.__data__; } else { subgroup.push(null); } } } return d3_selection(subgroups); }; function d3_selection_selector(selector) { return typeof selector === "function" ? selector : function() { return d3_select(selector, this); }; } d3_selectionPrototype.selectAll = function(selector) { var subgroups = [], subgroup, node; selector = d3_selection_selectorAll(selector); for (var j = -1, m = this.length; ++j < m;) { for (var group = this[j], i = -1, n = group.length; ++i < n;) { if (node = group[i]) { subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); subgroup.parentNode = node; } } } return d3_selection(subgroups); }; function d3_selection_selectorAll(selector) { return typeof selector === "function" ? selector : function() { return d3_selectAll(selector, this); }; } d3_selectionPrototype.attr = function(name, value) { if (arguments.length < 2) { // For attr(string), return the attribute value for the first node. if (typeof name === "string") { var node = this.node(); name = d3$ns.qualify(name); return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); } // For attr(object), the object specifies the names and values of the // attributes to set or remove. The values may be functions that are // evaluated for each element. for (value in name) this.each(d3_selection_attr(value, name[value])); return this; } return this.each(d3_selection_attr(name, value)); }; function d3_selection_attr(name, value) { name = d3$ns.qualify(name); // For attr(string, null), remove the attribute with the specified name. function attrNull() { this.removeAttribute(name); } function attrNullNS() { this.removeAttributeNS(name.space, name.local); } // For attr(string, string), set the attribute with the specified name. function attrConstant() { this.setAttribute(name, value); } function attrConstantNS() { this.setAttributeNS(name.space, name.local, value); } // For attr(string, function), evaluate the function for each element, and set // or remove the attribute as appropriate. function attrFunction() { var x = value.apply(this, arguments); if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); } function attrFunctionNS() { var x = value.apply(this, arguments); if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); } return value == null ? (name.local ? attrNullNS : attrNull) : (typeof value === "function" ? (name.local ? attrFunctionNS : attrFunction) : (name.local ? attrConstantNS : attrConstant)); } d3_selectionPrototype.classed = function(name, value) { if (arguments.length < 2) { // For classed(string), return true only if the first node has the specified // class or classes. Note that even if the browser supports DOMTokenList, it // probably doesn't support it on SVG elements (which can be animated). if (typeof name === "string") { var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; if (value = node.classList) { while (++i < n) if (!value.contains(name[i])) return false; } else { value = node.getAttribute("class"); while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; } return true; } // For classed(object), the object specifies the names of classes to add or // remove. The values may be functions that are evaluated for each element. for (value in name) this.each(d3_selection_classed(value, name[value])); return this; } // Otherwise, both a name and a value are specified, and are handled as below. return this.each(d3_selection_classed(name, value)); }; function d3_selection_classedRe(name) { return new RegExp("(?:^|\\s+)" + d3$requote(name) + "(?:\\s+|$)", "g"); } function d3_selection_classes(name) { return (name + "").trim().split(/^|\s+/); } // Multiple class names are allowed (e.g., "foo bar"). function d3_selection_classed(name, value) { name = d3_selection_classes(name).map(d3_selection_classedName); var n = name.length; function classedConstant() { var i = -1; while (++i < n) name[i](this, value); } // When the value is a function, the function is still evaluated only once per // element even if there are multiple class names. function classedFunction() { var i = -1, x = value.apply(this, arguments); while (++i < n) name[i](this, x); } return typeof value === "function" ? classedFunction : classedConstant; } function d3_selection_classedName(name) { var re = d3_selection_classedRe(name); return function(node, value) { if (c = node.classList) return value ? c.add(name) : c.remove(name); var c = node.getAttribute("class") || ""; if (value) { re.lastIndex = 0; if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); } else { node.setAttribute("class", d3_collapse(c.replace(re, " "))); } }; } d3_selectionPrototype.style = function(name, value, priority) { var n = arguments.length; if (n < 3) { // For style(object) or style(object, string), the object specifies the // names and values of the attributes to set or remove. The values may be // functions that are evaluated for each element. The optional string // specifies the priority. if (typeof name !== "string") { if (n < 2) value = ""; for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); return this; } // For style(string), return the computed style value for the first node. if (n < 2) { var node = this.node(); return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); } // For style(string, string) or style(string, function), use the default // priority. The priority is ignored for style(string, null). priority = ""; } // Otherwise, a name, value and priority are specified, and handled as below. return this.each(d3_selection_style(name, value, priority)); }; function d3_selection_style(name, value, priority) { // For style(name, null) or style(name, null, priority), remove the style // property with the specified name. The priority is ignored. function styleNull() { this.style.removeProperty(name); } // For style(name, string) or style(name, string, priority), set the style // property with the specified name, using the specified priority. function styleConstant() { this.style.setProperty(name, value, priority); } // For style(name, function) or style(name, function, priority), evaluate the // function for each element, and set or remove the style property as // appropriate. When setting, use the specified priority. function styleFunction() { var x = value.apply(this, arguments); if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); } return value == null ? styleNull : (typeof value === "function" ? styleFunction : styleConstant); } d3_selectionPrototype.property = function(name, value) { if (arguments.length < 2) { // For property(string), return the property value for the first node. if (typeof name === "string") return this.node()[name]; // For property(object), the object specifies the names and values of the // properties to set or remove. The values may be functions that are // evaluated for each element. for (value in name) this.each(d3_selection_property(value, name[value])); return this; } // Otherwise, both a name and a value are specified, and are handled as below. return this.each(d3_selection_property(name, value)); }; function d3_selection_property(name, value) { // For property(name, null), remove the property with the specified name. function propertyNull() { delete this[name]; } // For property(name, string), set the property with the specified name. function propertyConstant() { this[name] = value; } // For property(name, function), evaluate the function for each element, and // set or remove the property as appropriate. function propertyFunction() { var x = value.apply(this, arguments); if (x == null) delete this[name]; else this[name] = x; } return value == null ? propertyNull : (typeof value === "function" ? propertyFunction : propertyConstant); } d3_selectionPrototype.text = function(value) { return arguments.length ? this.each(typeof value === "function" ? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null ? function() { this.textContent = ""; } : function() { this.textContent = value; }) : this.node().textContent; }; d3_selectionPrototype.html = function(value) { return arguments.length ? this.each(typeof value === "function" ? function() { var v = value.apply(this, arguments); this.innerHTML = v == null ? "" : v; } : value == null ? function() { this.innerHTML = ""; } : function() { this.innerHTML = value; }) : this.node().innerHTML; }; d3_selectionPrototype.append = function(name) { name = d3_selection_creator(name); return this.select(function() { return this.appendChild(name.apply(this, arguments)); }); }; function d3_selection_creator(name) { function create() { var document = this.ownerDocument, namespace = this.namespaceURI; return namespace ? document.createElementNS(namespace, name) : document.createElement(name); } function createNS() { return this.ownerDocument.createElementNS(name.space, name.local); } return typeof name === "function" ? name : (name = d3$ns.qualify(name)).local ? createNS : create; } d3_selectionPrototype.insert = function(name, before) { name = d3_selection_creator(name); before = d3_selection_selector(before); return this.select(function() { return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); }); }; // TODO remove(selector)? // TODO remove(node)? // TODO remove(function)? d3_selectionPrototype.remove = function() { return this.each(d3_selectionRemove); }; function d3_selectionRemove() { var parent = this.parentNode; if (parent) parent.removeChild(this); } d3_selectionPrototype.data = function(value, key) { var i = -1, n = this.length, group, node; // If no value is specified, return the first value. if (!arguments.length) { value = new Array(n = (group = this[0]).length); while (++i < n) { if (node = group[i]) { value[i] = node.__data__; } } return value; } function bind(group, groupData) { var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; if (key) { var nodeByKeyValue = new d3_Map, keyValues = new Array(n), keyValue; for (i = -1; ++i < n;) { if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) { exitNodes[i] = node; // duplicate selection key } else { nodeByKeyValue.set(keyValue, node); } keyValues[i] = keyValue; } for (i = -1; ++i < m;) { if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { enterNodes[i] = d3_selection_dataNode(nodeData); } else if (node !== true) { // no duplicate data key updateNodes[i] = node; node.__data__ = nodeData; } nodeByKeyValue.set(keyValue, true); } for (i = -1; ++i < n;) { if (nodeByKeyValue.get(keyValues[i]) !== true) { exitNodes[i] = group[i]; } } } else { for (i = -1; ++i < n0;) { node = group[i]; nodeData = groupData[i]; if (node) { node.__data__ = nodeData; updateNodes[i] = node; } else { enterNodes[i] = d3_selection_dataNode(nodeData); } } for (; i < m; ++i) { enterNodes[i] = d3_selection_dataNode(groupData[i]); } for (; i < n; ++i) { exitNodes[i] = group[i]; } } enterNodes.update = updateNodes; enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; enter.push(enterNodes); update.push(updateNodes); exit.push(exitNodes); } var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); if (typeof value === "function") { while (++i < n) { bind(group = this[i], value.call(group, group.parentNode.__data__, i)); } } else { while (++i < n) { bind(group = this[i], value); } } update.enter = function() { return enter; }; update.exit = function() { return exit; }; return update; }; function d3_selection_dataNode(data) { return {__data__: data}; } d3_selectionPrototype.datum = function(value) { return arguments.length ? this.property("__data__", value) : this.property("__data__"); }; d3_selectionPrototype.filter = function(filter) { var subgroups = [], subgroup, group, node; if (typeof filter !== "function") filter = d3_selection_filter(filter); for (var j = 0, m = this.length; j < m; j++) { subgroups.push(subgroup = []); subgroup.parentNode = (group = this[j]).parentNode; for (var i = 0, n = group.length; i < n; i++) { if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { subgroup.push(node); } } } return d3_selection(subgroups); }; function d3_selection_filter(selector) { return function() { return d3_selectMatches(this, selector); }; } d3_selectionPrototype.order = function() { for (var j = -1, m = this.length; ++j < m;) { for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0;) { if (node = group[i]) { if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); next = node; } } } return this; }; d3_selectionPrototype.sort = function(comparator) { comparator = d3_selection_sortComparator.apply(this, arguments); for (var j = -1, m = this.length; ++j < m;) this[j].sort(comparator); return this.order(); }; function d3_selection_sortComparator(comparator) { if (!arguments.length) comparator = d3_ascending; return function(a, b) { return a && b ? comparator(a.__data__, b.__data__) : !a - !b; }; } d3_selectionPrototype.on = function(type, listener, capture) { var n = arguments.length; if (n < 3) { // For on(object) or on(object, boolean), the object specifies the event // types and listeners to add or remove. The optional boolean specifies // whether the listener captures events. if (typeof type !== "string") { if (n < 2) listener = false; for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); return this; } // For on(string), return the listener for the first node. if (n < 2) return (n = this.node()["__on" + type]) && n._; // For on(string, function), use the default capture. capture = false; } // Otherwise, a type, listener and capture are specified, and handled as below. return this.each(d3_selection_on(type, listener, capture)); }; function d3_selection_on(type, listener, capture) { var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; if (i > 0) type = type.slice(0, i); var filter = d3_selection_onFilters.get(type); if (filter) type = filter, wrap = d3_selection_onFilter; function onRemove() { var l = this[name]; if (l) { this.removeEventListener(type, l, l.$); delete this[name]; } } function onAdd() { var l = wrap(listener, d3_array(arguments)); onRemove.call(this); this.addEventListener(type, this[name] = l, l.$ = capture); l._ = listener; } function removeAll() { var re = new RegExp("^__on([^.]+)" + d3$requote(type) + "$"), match; for (var name in this) { if (match = name.match(re)) { var l = this[name]; this.removeEventListener(match[1], l, l.$); delete this[name]; } } } return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; } var d3_selection_onFilters = d3$map({ mouseenter: "mouseover", mouseleave: "mouseout" }); 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) { var o = window.d3_event; // Events can be reentrant (e.g., focus). window.d3_event = e; argumentz[0] = this.__data__; try { listener.apply(this, argumentz); } finally { window.d3_event = o; } }; } function d3_selection_onFilter(listener, argumentz) { var l = d3_selection_onListener(listener, argumentz); return function(e) { var target = this, related = e.relatedTarget; if (!related || (related !== target && !(related.compareDocumentPosition(target) & 8))) { l.call(target, e); } }; } d3_selectionPrototype.each = function(callback) { return d3_selection_each(this, function(node, i, j) { callback.call(node, node.__data__, i, j); }); }; function d3_selection_each(groups, callback) { for (var j = 0, m = groups.length; j < m; j++) { for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { if (node = group[i]) callback(node, i, j); } } return groups; } d3_selectionPrototype.call = function(callback) { var args = d3_array(arguments); callback.apply(args[0] = this, args); return this; }; d3_selectionPrototype.empty = function() { return !this.node(); }; d3_selectionPrototype.node = function() { for (var j = 0, m = this.length; j < m; j++) { for (var group = this[j], i = 0, n = group.length; i < n; i++) { var node = group[i]; if (node) return node; } } return null; }; d3_selectionPrototype.size = function() { var n = 0; d3_selection_each(this, function() { ++n; }); return n; }; function d3_selection_enter(selection) { d3_subclass(selection, d3_selection_enterPrototype); return selection; } var d3_selection_enterPrototype = []; d3$selection$enter = d3_selection_enter; d3$selection$enter.prototype = d3_selection_enterPrototype; d3_selection_enterPrototype.append = d3_selectionPrototype.append; d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; d3_selection_enterPrototype.node = d3_selectionPrototype.node; d3_selection_enterPrototype.call = d3_selectionPrototype.call; d3_selection_enterPrototype.size = d3_selectionPrototype.size; d3_selection_enterPrototype.select = function(selector) { var subgroups = [], subgroup, subnode, upgroup, group, node; for (var j = -1, m = this.length; ++j < m;) { upgroup = (group = this[j]).update; subgroups.push(subgroup = []); subgroup.parentNode = group.parentNode; for (var i = -1, n = group.length; ++i < n;) { if (node = group[i]) { subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); subnode.__data__ = node.__data__; } else { subgroup.push(null); } } } return d3_selection(subgroups); }; d3_selection_enterPrototype.insert = function(name, before) { if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); return d3_selectionPrototype.insert.call(this, name, before); }; function d3_selection_enterInsertBefore(enter) { var i0, j0; return function(d, i, j) { var group = enter[j].update, n = group.length, node; if (j != j0) j0 = j, i0 = 0; if (i >= i0) i0 = i + 1; while (!(node = group[i0]) && ++i0 < n); return node; }; } // TODO fast singleton implementation? d3$select = function(node) { var group; if (typeof node === "string") { group = [d3_select(node, d3_document)]; group.parentNode = d3_document.documentElement; } else { group = [node]; group.parentNode = d3_documentElement(node); } return d3_selection([group]); }; d3$selectAll = function(nodes) { var group; if (typeof nodes === "string") { group = d3_array(d3_selectAll(nodes, d3_document)); group.parentNode = d3_document.documentElement; } else { group = nodes; group.parentNode = null; } return d3_selection([group]); }; export { d3$selectAll, d3$select, d3_selection_enter, d3$selection, d3_selection_enterInsertBefore, d3_selection_enterPrototype, d3_selection_each, d3_selection_onFilter, d3_selection_onListener, d3_selection_onFilters, d3_selection_on, d3_selection_sortComparator, d3_selection_filter, d3_selection_dataNode, d3_selectionRemove, d3_selection_creator, d3_selection_property, d3_selection_style, d3_selection_classedName, d3_selection_classed, d3_selection_classes, d3_selection_classedRe, d3_selection_attr, d3_selection_selectorAll, d3_selection_selector, d3_selectionPrototype, d3_select, d3_selectAll, d3_selectMatches, d3_selection };