119 lines
3.5 KiB
JavaScript
119 lines
3.5 KiB
JavaScript
|
|
import {Selection} from "./index";
|
|||
|
|
import {EnterNode} from "./enter";
|
|||
|
|
import constant from "../constant";
|
|||
|
|
|
|||
|
|
var keyPrefix = "$"; // Protect against keys like “__proto__”.
|
|||
|
|
|
|||
|
|
function bindIndex(parent, group, enter, update, exit, data) {
|
|||
|
|
var i = 0,
|
|||
|
|
node,
|
|||
|
|
groupLength = group.length,
|
|||
|
|
dataLength = data.length;
|
|||
|
|
|
|||
|
|
// Put any non-null nodes that fit into update.
|
|||
|
|
// Put any null nodes into enter.
|
|||
|
|
// Put any remaining data into enter.
|
|||
|
|
for (; i < dataLength; ++i) {
|
|||
|
|
if (node = group[i]) {
|
|||
|
|
node.__data__ = data[i];
|
|||
|
|
update[i] = node;
|
|||
|
|
} else {
|
|||
|
|
enter[i] = new EnterNode(parent, data[i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Put any non-null nodes that don’t fit into exit.
|
|||
|
|
for (; i < groupLength; ++i) {
|
|||
|
|
if (node = group[i]) {
|
|||
|
|
exit[i] = node;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function bindKey(parent, group, enter, update, exit, data, key) {
|
|||
|
|
var i,
|
|||
|
|
node,
|
|||
|
|
nodeByKeyValue = {},
|
|||
|
|
groupLength = group.length,
|
|||
|
|
dataLength = data.length,
|
|||
|
|
keyValues = new Array(groupLength),
|
|||
|
|
keyValue;
|
|||
|
|
|
|||
|
|
// Compute the key for each node.
|
|||
|
|
// If multiple nodes have the same key, the duplicates are added to exit.
|
|||
|
|
for (i = 0; i < groupLength; ++i) {
|
|||
|
|
if (node = group[i]) {
|
|||
|
|
keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
|
|||
|
|
if (keyValue in nodeByKeyValue) {
|
|||
|
|
exit[i] = node;
|
|||
|
|
} else {
|
|||
|
|
nodeByKeyValue[keyValue] = node;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Compute the key for each datum.
|
|||
|
|
// If there a node associated with this key, join and add it to update.
|
|||
|
|
// If there is not (or the key is a duplicate), add it to enter.
|
|||
|
|
for (i = 0; i < dataLength; ++i) {
|
|||
|
|
keyValue = keyPrefix + key.call(parent, data[i], i, data);
|
|||
|
|
if (node = nodeByKeyValue[keyValue]) {
|
|||
|
|
update[i] = node;
|
|||
|
|
node.__data__ = data[i];
|
|||
|
|
nodeByKeyValue[keyValue] = null;
|
|||
|
|
} else {
|
|||
|
|
enter[i] = new EnterNode(parent, data[i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add any remaining nodes that were not bound to data to exit.
|
|||
|
|
for (i = 0; i < groupLength; ++i) {
|
|||
|
|
if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
|
|||
|
|
exit[i] = node;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function(value, key) {
|
|||
|
|
if (!value) {
|
|||
|
|
data = new Array(this.size()), j = -1;
|
|||
|
|
this.each(function(d) { data[++j] = d; });
|
|||
|
|
return data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var bind = key ? bindKey : bindIndex,
|
|||
|
|
parents = this._parents,
|
|||
|
|
groups = this._groups;
|
|||
|
|
|
|||
|
|
if (typeof value !== "function") value = constant(value);
|
|||
|
|
|
|||
|
|
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
|
|||
|
|
var parent = parents[j],
|
|||
|
|
group = groups[j],
|
|||
|
|
groupLength = group.length,
|
|||
|
|
data = value.call(parent, parent && parent.__data__, j, parents),
|
|||
|
|
dataLength = data.length,
|
|||
|
|
enterGroup = enter[j] = new Array(dataLength),
|
|||
|
|
updateGroup = update[j] = new Array(dataLength),
|
|||
|
|
exitGroup = exit[j] = new Array(groupLength);
|
|||
|
|
|
|||
|
|
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
|
|||
|
|
|
|||
|
|
// Now connect the enter nodes to their following update node, such that
|
|||
|
|
// appendChild can insert the materialized enter node before this node,
|
|||
|
|
// rather than at the end of the parent node.
|
|||
|
|
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
|
|||
|
|
if (previous = enterGroup[i0]) {
|
|||
|
|
if (i0 >= i1) i1 = i0 + 1;
|
|||
|
|
while (!(next = updateGroup[i1]) && ++i1 < dataLength);
|
|||
|
|
previous._next = next || null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
update = new Selection(update, parents);
|
|||
|
|
update._enter = enter;
|
|||
|
|
update._exit = exit;
|
|||
|
|
return update;
|
|||
|
|
}
|