import {default as gup, empty, identity} from './gup';
import {default as gup, empty, identity} from './gup';
One of the key ideas of d3-gup is that GUP factories can be composed, whereby the functions of each phase are composed together to form a single GUP factory that can then be bound to data and called against a selection. Not only do we compose the phase functions together but we also merge the remaining properties of the GUP factories in the hope that they don’t conflict with each other and the resulting GUP factory can be used as if it was any one of its component GUP factories.
These properties are not to be touched during the merge.
const nonEnumerableProps = /^(valueOf|isPrototypeOf|to(Locale)?String|propertyIsEnumerable|hasOwnProperty|pre|exit|enter|post|update|select)$/;
comp
does the bulk of the work to compose gups together for a given phase.
function comp(gups, name) {
Only enter
and select
functions make use of the return value.
let forward = name === "enter" || name === "select";
f
is the resulting function that calls each phase function in gups in
turn. f
is designed to be passed to the call
function, therefore the first argument will be a Selection.
let f = function(...args) {
for (let g of gups) {
Extract the phase function from g
and call it if it’s not a no-op
if (name in g && g[name] && (g = g[name]()) && g !== empty && g !== identity) {
this
and args are passed through
var result = g.apply(this, args);
The selection may have been transformed by either enter
or
select
, therefore we must carry it forward as the first argument.
if (forward) args[0] = result;
}
}
Only enter
and select
functions should return anything.
if (forward) return result;
};
return f;
}
compose
acts like Underscore’s extend
function followed by
Underscore’s compose
function, but returns a new GUP factory
(none of the sources are mutated). For the extend part the sources are read
from left to right; properties of the rightmost source may override the
properties of sources on the left. For the compose part sources are read
from right to left; the rightmost source will be the the innermost call and
called first, passing results to the source on it left.
export default function compose(...sources) {
let g = gup();
Perform the extend for each property in each source, assign it to g
.
for (let source of sources) {
for (let k in source) {
Skip the pharse functions the the non-enumerble properties of Object
.
if (!nonEnumerableProps.test(k)) {
g[k] = source[k];
}
}
}
Perform the compose for each of the five phase functions.
sources = sources.reverse();
g.select(comp(sources, "select"));
g.pre(comp(sources, "pre"));
g.exit(comp(sources, "exit"));
g.enter(comp(sources, "enter"));
g.post(comp(sources, "post"));
Returns the freshly minted GUP factory.
return g;
}