Repository: d3/d3-transition
Branch: main
Commit: 7a3b625d8613
Files: 72
Total size: 124.7 KB
Directory structure:
gitextract_uicvwejc/
├── .eslintrc.json
├── .github/
│ ├── eslint.json
│ └── workflows/
│ └── node.js.yml
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── rollup.config.js
├── src/
│ ├── active.js
│ ├── index.js
│ ├── interrupt.js
│ ├── selection/
│ │ ├── index.js
│ │ ├── interrupt.js
│ │ └── transition.js
│ └── transition/
│ ├── attr.js
│ ├── attrTween.js
│ ├── delay.js
│ ├── duration.js
│ ├── ease.js
│ ├── easeVarying.js
│ ├── end.js
│ ├── filter.js
│ ├── index.js
│ ├── interpolate.js
│ ├── merge.js
│ ├── on.js
│ ├── remove.js
│ ├── schedule.js
│ ├── select.js
│ ├── selectAll.js
│ ├── selection.js
│ ├── style.js
│ ├── styleTween.js
│ ├── text.js
│ ├── textTween.js
│ ├── transition.js
│ └── tween.js
└── test/
├── .eslintrc.json
├── active-test.js
├── error-test.js
├── interrupt-test.js
├── jsdom.js
├── selection/
│ ├── interrupt-test.js
│ └── transition-test.js
└── transition/
├── attr-test.js
├── attrTween-test.js
├── call-test.js
├── delay-test.js
├── duration-test.js
├── each-test.js
├── ease-test.js
├── easeVarying-test.js
├── empty-test.js
├── filter-test.js
├── index-test.js
├── merge-test.js
├── node-test.js
├── nodes-test.js
├── on-test.js
├── remove-test.js
├── select-test.js
├── selectAll-test.js
├── selectChild-test.js
├── selectChildren-test.js
├── selection-test.js
├── size-test.js
├── style-test.js
├── styleTween-test.js
├── text-test.js
├── textTween-test.js
├── transition-test.js
└── tween-test.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc.json
================================================
{
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 8
},
"env": {
"es6": true
},
"rules": {
"no-cond-assign": 0
}
}
================================================
FILE: .github/eslint.json
================================================
{
"problemMatcher": [
{
"owner": "eslint-compact",
"pattern": [
{
"regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s(Error|Warning|Info)\\s-\\s(.+)\\s\\((.+)\\)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5,
"code": 6
}
]
}
]
}
================================================
FILE: .github/workflows/node.js.yml
================================================
# https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: yarn --frozen-lockfile
- run: |
echo ::add-matcher::.github/eslint.json
yarn run eslint src test --format=compact
- run: yarn test
================================================
FILE: .gitignore
================================================
*.sublime-workspace
.DS_Store
dist/
node_modules
npm-debug.log
================================================
FILE: LICENSE
================================================
Copyright 2010-2021 Mike Bostock
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
================================================
FILE: README.md
================================================
# d3-transition
<a href="https://d3js.org"><img src="https://github.com/d3/d3/raw/main/docs/public/logo.svg" width="256" height="256"></a>
A transition is a [selection](https://github.com/d3/d3-selection)-like interface for animating changes to the DOM. Instead of applying changes instantaneously, transitions smoothly interpolate the DOM from its current state to the desired target state over a given duration.
## Resources
- [Documentation](https://d3js.org/d3-transition)
- [Examples](https://observablehq.com/collection/@d3/d3-transition)
- [Releases](https://github.com/d3/d3-transition/releases)
- [Getting help](https://d3js.org/community)
================================================
FILE: package.json
================================================
{
"name": "d3-transition",
"version": "3.0.1",
"description": "Animated transitions for D3 selections.",
"homepage": "https://d3js.org/d3-transition/",
"repository": {
"type": "git",
"url": "https://github.com/d3/d3-transition.git"
},
"keywords": [
"d3",
"d3-module",
"dom",
"transition",
"animation"
],
"license": "ISC",
"author": {
"name": "Mike Bostock",
"url": "https://bost.ocks.org/mike"
},
"type": "module",
"files": [
"dist/**/*.js",
"src/**/*.js"
],
"module": "src/index.js",
"main": "src/index.js",
"jsdelivr": "dist/d3-transition.min.js",
"unpkg": "dist/d3-transition.min.js",
"exports": {
"umd": "./dist/d3-transition.min.js",
"default": "./src/index.js"
},
"sideEffects": [
"./src/index.js",
"./src/selection/index.js"
],
"dependencies": {
"d3-color": "1 - 3",
"d3-dispatch": "1 - 3",
"d3-ease": "1 - 3",
"d3-interpolate": "1 - 3",
"d3-timer": "1 - 3"
},
"devDependencies": {
"d3-selection": "2 - 3",
"eslint": "7",
"jsdom": "16",
"mocha": "9",
"rollup": "2",
"rollup-plugin-terser": "7"
},
"scripts": {
"test": "mocha 'test/**/*-test.js' && eslint src test",
"prepublishOnly": "rm -rf dist && yarn test && rollup -c && git push",
"postpublish": "git push --tags && cd ../d3.github.com && git pull && cp ../${npm_package_name}/dist/${npm_package_name}.js ${npm_package_name}.v${npm_package_version%%.*}.js && cp ../${npm_package_name}/dist/${npm_package_name}.min.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git add ${npm_package_name}.v${npm_package_version%%.*}.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git commit -m \"${npm_package_name} ${npm_package_version}\" && git push && cd -"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"d3-selection": "2 - 3"
}
}
================================================
FILE: rollup.config.js
================================================
import {readFileSync} from "fs";
import {terser} from "rollup-plugin-terser";
import * as meta from "./package.json";
// Extract copyrights from the LICENSE.
const copyright = readFileSync("./LICENSE", "utf-8")
.split(/\n/g)
.filter(line => /^Copyright\s+/.test(line))
.map(line => line.replace(/^Copyright\s+/, ""))
.join(", ");
const config = {
input: "src/index.js",
external: Object.keys({...meta.dependencies, ...meta.peerDependencies}).filter(key => /^d3-/.test(key)),
output: {
file: `dist/${meta.name}.js`,
name: "d3",
format: "umd",
indent: false,
extend: true,
banner: `// ${meta.homepage} v${meta.version} Copyright ${copyright}`,
globals: Object.assign({}, ...Object.keys({...meta.dependencies, ...meta.peerDependencies}).filter(key => /^d3-/.test(key)).map(key => ({[key]: "d3"})))
},
plugins: [],
onwarn(message, warn) {
if (message.code === "CIRCULAR_DEPENDENCY") return;
warn(message);
}
};
export default [
config,
{
...config,
output: {
...config.output,
file: `dist/${meta.name}.min.js`
},
plugins: [
...config.plugins,
terser({
output: {
preamble: config.output.banner
}
})
]
}
];
================================================
FILE: src/active.js
================================================
import {Transition} from "./transition/index.js";
import {SCHEDULED} from "./transition/schedule.js";
var root = [null];
export default function(node, name) {
var schedules = node.__transition,
schedule,
i;
if (schedules) {
name = name == null ? null : name + "";
for (i in schedules) {
if ((schedule = schedules[i]).state > SCHEDULED && schedule.name === name) {
return new Transition([[node]], root, name, +i);
}
}
}
return null;
}
================================================
FILE: src/index.js
================================================
import "./selection/index.js";
export {default as transition} from "./transition/index.js";
export {default as active} from "./active.js";
export {default as interrupt} from "./interrupt.js";
================================================
FILE: src/interrupt.js
================================================
import {STARTING, ENDING, ENDED} from "./transition/schedule.js";
export default function(node, name) {
var schedules = node.__transition,
schedule,
active,
empty = true,
i;
if (!schedules) return;
name = name == null ? null : name + "";
for (i in schedules) {
if ((schedule = schedules[i]).name !== name) { empty = false; continue; }
active = schedule.state > STARTING && schedule.state < ENDING;
schedule.state = ENDED;
schedule.timer.stop();
schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
delete schedules[i];
}
if (empty) delete node.__transition;
}
================================================
FILE: src/selection/index.js
================================================
import {selection} from "d3-selection";
import selection_interrupt from "./interrupt.js";
import selection_transition from "./transition.js";
selection.prototype.interrupt = selection_interrupt;
selection.prototype.transition = selection_transition;
================================================
FILE: src/selection/interrupt.js
================================================
import interrupt from "../interrupt.js";
export default function(name) {
return this.each(function() {
interrupt(this, name);
});
}
================================================
FILE: src/selection/transition.js
================================================
import {Transition, newId} from "../transition/index.js";
import schedule from "../transition/schedule.js";
import {easeCubicInOut} from "d3-ease";
import {now} from "d3-timer";
var defaultTiming = {
time: null, // Set on use.
delay: 0,
duration: 250,
ease: easeCubicInOut
};
function inherit(node, id) {
var timing;
while (!(timing = node.__transition) || !(timing = timing[id])) {
if (!(node = node.parentNode)) {
throw new Error(`transition ${id} not found`);
}
}
return timing;
}
export default function(name) {
var id,
timing;
if (name instanceof Transition) {
id = name._id, name = name._name;
} else {
id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
}
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
schedule(node, name, id, i, group, timing || inherit(node, id));
}
}
}
return new Transition(groups, this._parents, name, id);
}
================================================
FILE: src/transition/attr.js
================================================
import {interpolateTransformSvg as interpolateTransform} from "d3-interpolate";
import {namespace} from "d3-selection";
import {tweenValue} from "./tween.js";
import interpolate from "./interpolate.js";
function attrRemove(name) {
return function() {
this.removeAttribute(name);
};
}
function attrRemoveNS(fullname) {
return function() {
this.removeAttributeNS(fullname.space, fullname.local);
};
}
function attrConstant(name, interpolate, value1) {
var string00,
string1 = value1 + "",
interpolate0;
return function() {
var string0 = this.getAttribute(name);
return string0 === string1 ? null
: string0 === string00 ? interpolate0
: interpolate0 = interpolate(string00 = string0, value1);
};
}
function attrConstantNS(fullname, interpolate, value1) {
var string00,
string1 = value1 + "",
interpolate0;
return function() {
var string0 = this.getAttributeNS(fullname.space, fullname.local);
return string0 === string1 ? null
: string0 === string00 ? interpolate0
: interpolate0 = interpolate(string00 = string0, value1);
};
}
function attrFunction(name, interpolate, value) {
var string00,
string10,
interpolate0;
return function() {
var string0, value1 = value(this), string1;
if (value1 == null) return void this.removeAttribute(name);
string0 = this.getAttribute(name);
string1 = value1 + "";
return string0 === string1 ? null
: string0 === string00 && string1 === string10 ? interpolate0
: (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
};
}
function attrFunctionNS(fullname, interpolate, value) {
var string00,
string10,
interpolate0;
return function() {
var string0, value1 = value(this), string1;
if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
string0 = this.getAttributeNS(fullname.space, fullname.local);
string1 = value1 + "";
return string0 === string1 ? null
: string0 === string00 && string1 === string10 ? interpolate0
: (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
};
}
export default function(name, value) {
var fullname = namespace(name), i = fullname === "transform" ? interpolateTransform : interpolate;
return this.attrTween(name, typeof value === "function"
? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value))
: value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)
: (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value));
}
================================================
FILE: src/transition/attrTween.js
================================================
import {namespace} from "d3-selection";
function attrInterpolate(name, i) {
return function(t) {
this.setAttribute(name, i.call(this, t));
};
}
function attrInterpolateNS(fullname, i) {
return function(t) {
this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
};
}
function attrTweenNS(fullname, value) {
var t0, i0;
function tween() {
var i = value.apply(this, arguments);
if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
return t0;
}
tween._value = value;
return tween;
}
function attrTween(name, value) {
var t0, i0;
function tween() {
var i = value.apply(this, arguments);
if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
return t0;
}
tween._value = value;
return tween;
}
export default function(name, value) {
var key = "attr." + name;
if (arguments.length < 2) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error;
var fullname = namespace(name);
return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
}
================================================
FILE: src/transition/delay.js
================================================
import {get, init} from "./schedule.js";
function delayFunction(id, value) {
return function() {
init(this, id).delay = +value.apply(this, arguments);
};
}
function delayConstant(id, value) {
return value = +value, function() {
init(this, id).delay = value;
};
}
export default function(value) {
var id = this._id;
return arguments.length
? this.each((typeof value === "function"
? delayFunction
: delayConstant)(id, value))
: get(this.node(), id).delay;
}
================================================
FILE: src/transition/duration.js
================================================
import {get, set} from "./schedule.js";
function durationFunction(id, value) {
return function() {
set(this, id).duration = +value.apply(this, arguments);
};
}
function durationConstant(id, value) {
return value = +value, function() {
set(this, id).duration = value;
};
}
export default function(value) {
var id = this._id;
return arguments.length
? this.each((typeof value === "function"
? durationFunction
: durationConstant)(id, value))
: get(this.node(), id).duration;
}
================================================
FILE: src/transition/ease.js
================================================
import {get, set} from "./schedule.js";
function easeConstant(id, value) {
if (typeof value !== "function") throw new Error;
return function() {
set(this, id).ease = value;
};
}
export default function(value) {
var id = this._id;
return arguments.length
? this.each(easeConstant(id, value))
: get(this.node(), id).ease;
}
================================================
FILE: src/transition/easeVarying.js
================================================
import {set} from "./schedule.js";
function easeVarying(id, value) {
return function() {
var v = value.apply(this, arguments);
if (typeof v !== "function") throw new Error;
set(this, id).ease = v;
};
}
export default function(value) {
if (typeof value !== "function") throw new Error;
return this.each(easeVarying(this._id, value));
}
================================================
FILE: src/transition/end.js
================================================
import {set} from "./schedule.js";
export default function() {
var on0, on1, that = this, id = that._id, size = that.size();
return new Promise(function(resolve, reject) {
var cancel = {value: reject},
end = {value: function() { if (--size === 0) resolve(); }};
that.each(function() {
var schedule = set(this, id),
on = schedule.on;
// If this node shared a dispatch with the previous node,
// just assign the updated shared dispatch and we’re done!
// Otherwise, copy-on-write.
if (on !== on0) {
on1 = (on0 = on).copy();
on1._.cancel.push(cancel);
on1._.interrupt.push(cancel);
on1._.end.push(end);
}
schedule.on = on1;
});
// The selection was empty, resolve end immediately
if (size === 0) resolve();
});
}
================================================
FILE: src/transition/filter.js
================================================
import {matcher} from "d3-selection";
import {Transition} from "./index.js";
export default function(match) {
if (typeof match !== "function") match = matcher(match);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
subgroup.push(node);
}
}
}
return new Transition(subgroups, this._parents, this._name, this._id);
}
================================================
FILE: src/transition/index.js
================================================
import {selection} from "d3-selection";
import transition_attr from "./attr.js";
import transition_attrTween from "./attrTween.js";
import transition_delay from "./delay.js";
import transition_duration from "./duration.js";
import transition_ease from "./ease.js";
import transition_easeVarying from "./easeVarying.js";
import transition_filter from "./filter.js";
import transition_merge from "./merge.js";
import transition_on from "./on.js";
import transition_remove from "./remove.js";
import transition_select from "./select.js";
import transition_selectAll from "./selectAll.js";
import transition_selection from "./selection.js";
import transition_style from "./style.js";
import transition_styleTween from "./styleTween.js";
import transition_text from "./text.js";
import transition_textTween from "./textTween.js";
import transition_transition from "./transition.js";
import transition_tween from "./tween.js";
import transition_end from "./end.js";
var id = 0;
export function Transition(groups, parents, name, id) {
this._groups = groups;
this._parents = parents;
this._name = name;
this._id = id;
}
export default function transition(name) {
return selection().transition(name);
}
export function newId() {
return ++id;
}
var selection_prototype = selection.prototype;
Transition.prototype = transition.prototype = {
constructor: Transition,
select: transition_select,
selectAll: transition_selectAll,
selectChild: selection_prototype.selectChild,
selectChildren: selection_prototype.selectChildren,
filter: transition_filter,
merge: transition_merge,
selection: transition_selection,
transition: transition_transition,
call: selection_prototype.call,
nodes: selection_prototype.nodes,
node: selection_prototype.node,
size: selection_prototype.size,
empty: selection_prototype.empty,
each: selection_prototype.each,
on: transition_on,
attr: transition_attr,
attrTween: transition_attrTween,
style: transition_style,
styleTween: transition_styleTween,
text: transition_text,
textTween: transition_textTween,
remove: transition_remove,
tween: transition_tween,
delay: transition_delay,
duration: transition_duration,
ease: transition_ease,
easeVarying: transition_easeVarying,
end: transition_end,
[Symbol.iterator]: selection_prototype[Symbol.iterator]
};
================================================
FILE: src/transition/interpolate.js
================================================
import {color} from "d3-color";
import {interpolateNumber, interpolateRgb, interpolateString} from "d3-interpolate";
export default function(a, b) {
var c;
return (typeof b === "number" ? interpolateNumber
: b instanceof color ? interpolateRgb
: (c = color(b)) ? (b = c, interpolateRgb)
: interpolateString)(a, b);
}
================================================
FILE: src/transition/merge.js
================================================
import {Transition} from "./index.js";
export default function(transition) {
if (transition._id !== this._id) throw new Error;
for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group0[i] || group1[i]) {
merge[i] = node;
}
}
}
for (; j < m0; ++j) {
merges[j] = groups0[j];
}
return new Transition(merges, this._parents, this._name, this._id);
}
================================================
FILE: src/transition/on.js
================================================
import {get, set, init} from "./schedule.js";
function start(name) {
return (name + "").trim().split(/^|\s+/).every(function(t) {
var i = t.indexOf(".");
if (i >= 0) t = t.slice(0, i);
return !t || t === "start";
});
}
function onFunction(id, name, listener) {
var on0, on1, sit = start(name) ? init : set;
return function() {
var schedule = sit(this, id),
on = schedule.on;
// If this node shared a dispatch with the previous node,
// just assign the updated shared dispatch and we’re done!
// Otherwise, copy-on-write.
if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
schedule.on = on1;
};
}
export default function(name, listener) {
var id = this._id;
return arguments.length < 2
? get(this.node(), id).on.on(name)
: this.each(onFunction(id, name, listener));
}
================================================
FILE: src/transition/remove.js
================================================
function removeFunction(id) {
return function() {
var parent = this.parentNode;
for (var i in this.__transition) if (+i !== id) return;
if (parent) parent.removeChild(this);
};
}
export default function() {
return this.on("end.remove", removeFunction(this._id));
}
================================================
FILE: src/transition/schedule.js
================================================
import {dispatch} from "d3-dispatch";
import {timer, timeout} from "d3-timer";
var emptyOn = dispatch("start", "end", "cancel", "interrupt");
var emptyTween = [];
export var CREATED = 0;
export var SCHEDULED = 1;
export var STARTING = 2;
export var STARTED = 3;
export var RUNNING = 4;
export var ENDING = 5;
export var ENDED = 6;
export default function(node, name, id, index, group, timing) {
var schedules = node.__transition;
if (!schedules) node.__transition = {};
else if (id in schedules) return;
create(node, id, {
name: name,
index: index, // For context during callback.
group: group, // For context during callback.
on: emptyOn,
tween: emptyTween,
time: timing.time,
delay: timing.delay,
duration: timing.duration,
ease: timing.ease,
timer: null,
state: CREATED
});
}
export function init(node, id) {
var schedule = get(node, id);
if (schedule.state > CREATED) throw new Error("too late; already scheduled");
return schedule;
}
export function set(node, id) {
var schedule = get(node, id);
if (schedule.state > STARTED) throw new Error("too late; already running");
return schedule;
}
export function get(node, id) {
var schedule = node.__transition;
if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
return schedule;
}
function create(node, id, self) {
var schedules = node.__transition,
tween;
// Initialize the self timer when the transition is created.
// Note the actual delay is not known until the first callback!
schedules[id] = self;
self.timer = timer(schedule, 0, self.time);
function schedule(elapsed) {
self.state = SCHEDULED;
self.timer.restart(start, self.delay, self.time);
// If the elapsed delay is less than our first sleep, start immediately.
if (self.delay <= elapsed) start(elapsed - self.delay);
}
function start(elapsed) {
var i, j, n, o;
// If the state is not SCHEDULED, then we previously errored on start.
if (self.state !== SCHEDULED) return stop();
for (i in schedules) {
o = schedules[i];
if (o.name !== self.name) continue;
// While this element already has a starting transition during this frame,
// defer starting an interrupting transition until that transition has a
// chance to tick (and possibly end); see d3/d3-transition#54!
if (o.state === STARTED) return timeout(start);
// Interrupt the active transition, if any.
if (o.state === RUNNING) {
o.state = ENDED;
o.timer.stop();
o.on.call("interrupt", node, node.__data__, o.index, o.group);
delete schedules[i];
}
// Cancel any pre-empted transitions.
else if (+i < id) {
o.state = ENDED;
o.timer.stop();
o.on.call("cancel", node, node.__data__, o.index, o.group);
delete schedules[i];
}
}
// Defer the first tick to end of the current frame; see d3/d3#1576.
// Note the transition may be canceled after start and before the first tick!
// Note this must be scheduled before the start event; see d3/d3-transition#16!
// Assuming this is successful, subsequent callbacks go straight to tick.
timeout(function() {
if (self.state === STARTED) {
self.state = RUNNING;
self.timer.restart(tick, self.delay, self.time);
tick(elapsed);
}
});
// Dispatch the start event.
// Note this must be done before the tween are initialized.
self.state = STARTING;
self.on.call("start", node, node.__data__, self.index, self.group);
if (self.state !== STARTING) return; // interrupted
self.state = STARTED;
// Initialize the tween, deleting null tween.
tween = new Array(n = self.tween.length);
for (i = 0, j = -1; i < n; ++i) {
if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
tween[++j] = o;
}
}
tween.length = j + 1;
}
function tick(elapsed) {
var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
i = -1,
n = tween.length;
while (++i < n) {
tween[i].call(node, t);
}
// Dispatch the end event.
if (self.state === ENDING) {
self.on.call("end", node, node.__data__, self.index, self.group);
stop();
}
}
function stop() {
self.state = ENDED;
self.timer.stop();
delete schedules[id];
for (var i in schedules) return; // eslint-disable-line no-unused-vars
delete node.__transition;
}
}
================================================
FILE: src/transition/select.js
================================================
import {selector} from "d3-selection";
import {Transition} from "./index.js";
import schedule, {get} from "./schedule.js";
export default function(select) {
var name = this._name,
id = this._id;
if (typeof select !== "function") select = selector(select);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
if ("__data__" in node) subnode.__data__ = node.__data__;
subgroup[i] = subnode;
schedule(subgroup[i], name, id, i, subgroup, get(node, id));
}
}
}
return new Transition(subgroups, this._parents, name, id);
}
================================================
FILE: src/transition/selectAll.js
================================================
import {selectorAll} from "d3-selection";
import {Transition} from "./index.js";
import schedule, {get} from "./schedule.js";
export default function(select) {
var name = this._name,
id = this._id;
if (typeof select !== "function") select = selectorAll(select);
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
for (var children = select.call(node, node.__data__, i, group), child, inherit = get(node, id), k = 0, l = children.length; k < l; ++k) {
if (child = children[k]) {
schedule(child, name, id, k, children, inherit);
}
}
subgroups.push(children);
parents.push(node);
}
}
}
return new Transition(subgroups, parents, name, id);
}
================================================
FILE: src/transition/selection.js
================================================
import {selection} from "d3-selection";
var Selection = selection.prototype.constructor;
export default function() {
return new Selection(this._groups, this._parents);
}
================================================
FILE: src/transition/style.js
================================================
import {interpolateTransformCss as interpolateTransform} from "d3-interpolate";
import {style} from "d3-selection";
import {set} from "./schedule.js";
import {tweenValue} from "./tween.js";
import interpolate from "./interpolate.js";
function styleNull(name, interpolate) {
var string00,
string10,
interpolate0;
return function() {
var string0 = style(this, name),
string1 = (this.style.removeProperty(name), style(this, name));
return string0 === string1 ? null
: string0 === string00 && string1 === string10 ? interpolate0
: interpolate0 = interpolate(string00 = string0, string10 = string1);
};
}
function styleRemove(name) {
return function() {
this.style.removeProperty(name);
};
}
function styleConstant(name, interpolate, value1) {
var string00,
string1 = value1 + "",
interpolate0;
return function() {
var string0 = style(this, name);
return string0 === string1 ? null
: string0 === string00 ? interpolate0
: interpolate0 = interpolate(string00 = string0, value1);
};
}
function styleFunction(name, interpolate, value) {
var string00,
string10,
interpolate0;
return function() {
var string0 = style(this, name),
value1 = value(this),
string1 = value1 + "";
if (value1 == null) string1 = value1 = (this.style.removeProperty(name), style(this, name));
return string0 === string1 ? null
: string0 === string00 && string1 === string10 ? interpolate0
: (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
};
}
function styleMaybeRemove(id, name) {
var on0, on1, listener0, key = "style." + name, event = "end." + key, remove;
return function() {
var schedule = set(this, id),
on = schedule.on,
listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined;
// If this node shared a dispatch with the previous node,
// just assign the updated shared dispatch and we’re done!
// Otherwise, copy-on-write.
if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
schedule.on = on1;
};
}
export default function(name, value, priority) {
var i = (name += "") === "transform" ? interpolateTransform : interpolate;
return value == null ? this
.styleTween(name, styleNull(name, i))
.on("end.style." + name, styleRemove(name))
: typeof value === "function" ? this
.styleTween(name, styleFunction(name, i, tweenValue(this, "style." + name, value)))
.each(styleMaybeRemove(this._id, name))
: this
.styleTween(name, styleConstant(name, i, value), priority)
.on("end.style." + name, null);
}
================================================
FILE: src/transition/styleTween.js
================================================
function styleInterpolate(name, i, priority) {
return function(t) {
this.style.setProperty(name, i.call(this, t), priority);
};
}
function styleTween(name, value, priority) {
var t, i0;
function tween() {
var i = value.apply(this, arguments);
if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
return t;
}
tween._value = value;
return tween;
}
export default function(name, value, priority) {
var key = "style." + (name += "");
if (arguments.length < 2) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error;
return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
}
================================================
FILE: src/transition/text.js
================================================
import {tweenValue} from "./tween.js";
function textConstant(value) {
return function() {
this.textContent = value;
};
}
function textFunction(value) {
return function() {
var value1 = value(this);
this.textContent = value1 == null ? "" : value1;
};
}
export default function(value) {
return this.tween("text", typeof value === "function"
? textFunction(tweenValue(this, "text", value))
: textConstant(value == null ? "" : value + ""));
}
================================================
FILE: src/transition/textTween.js
================================================
function textInterpolate(i) {
return function(t) {
this.textContent = i.call(this, t);
};
}
function textTween(value) {
var t0, i0;
function tween() {
var i = value.apply(this, arguments);
if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
return t0;
}
tween._value = value;
return tween;
}
export default function(value) {
var key = "text";
if (arguments.length < 1) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error;
return this.tween(key, textTween(value));
}
================================================
FILE: src/transition/transition.js
================================================
import {Transition, newId} from "./index.js";
import schedule, {get} from "./schedule.js";
export default function() {
var name = this._name,
id0 = this._id,
id1 = newId();
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
var inherit = get(node, id0);
schedule(node, name, id1, i, group, {
time: inherit.time + inherit.delay + inherit.duration,
delay: 0,
duration: inherit.duration,
ease: inherit.ease
});
}
}
}
return new Transition(groups, this._parents, name, id1);
}
================================================
FILE: src/transition/tween.js
================================================
import {get, set} from "./schedule.js";
function tweenRemove(id, name) {
var tween0, tween1;
return function() {
var schedule = set(this, id),
tween = schedule.tween;
// If this node shared tween with the previous node,
// just assign the updated shared tween and we’re done!
// Otherwise, copy-on-write.
if (tween !== tween0) {
tween1 = tween0 = tween;
for (var i = 0, n = tween1.length; i < n; ++i) {
if (tween1[i].name === name) {
tween1 = tween1.slice();
tween1.splice(i, 1);
break;
}
}
}
schedule.tween = tween1;
};
}
function tweenFunction(id, name, value) {
var tween0, tween1;
if (typeof value !== "function") throw new Error;
return function() {
var schedule = set(this, id),
tween = schedule.tween;
// If this node shared tween with the previous node,
// just assign the updated shared tween and we’re done!
// Otherwise, copy-on-write.
if (tween !== tween0) {
tween1 = (tween0 = tween).slice();
for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {
if (tween1[i].name === name) {
tween1[i] = t;
break;
}
}
if (i === n) tween1.push(t);
}
schedule.tween = tween1;
};
}
export default function(name, value) {
var id = this._id;
name += "";
if (arguments.length < 2) {
var tween = get(this.node(), id).tween;
for (var i = 0, n = tween.length, t; i < n; ++i) {
if ((t = tween[i]).name === name) {
return t.value;
}
}
return null;
}
return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
}
export function tweenValue(transition, name, value) {
var id = transition._id;
transition.each(function() {
var schedule = set(this, id);
(schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
});
return function(node) {
return get(node, id).value[name];
};
}
================================================
FILE: test/.eslintrc.json
================================================
{
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 8
},
"env": {
"mocha": true,
"node": true,
"es6": true,
"browser": true
}
}
================================================
FILE: test/active-test.js
================================================
import assert from "assert";
import {select} from "d3-selection";
import {timeout} from "d3-timer";
import {active} from "../src/index.js";
import it from "./jsdom.js";
it("active(node) returns null if the specified node has no active transition with the null name", async () => {
const root = document.documentElement;
const s = select(root);
// No transitions pending.
assert.strictEqual(active(root), null);
// Two transitions created.
s.transition().delay(50).duration(50);
s.transition("foo").duration(50);
assert.strictEqual(active(root), null);
// One transition scheduled; one active with a different name.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root), null);
resolve();
}));
// No transitions remaining after the transition ends.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root), null);
resolve();
}, 100));
});
it("active(node, null) returns null if the specified node has no active transition with the null name", async () => {
const root = document.documentElement;
const s = select(root);
// No transitions pending.
assert.strictEqual(active(root, null), null);
// Two transitions created.
s.transition().delay(50).duration(50);
s.transition("foo").duration(50);
assert.strictEqual(active(root, null), null);
// One transition scheduled; one active with a different name.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root, null), null);
resolve();
}));
// No transitions remaining after the transition ends.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root, null), null);
resolve();
}, 100));
});
it("active(node, undefined) returns null if the specified node has no active transition with the null name", async () => {
const root = document.documentElement;
const s = select(root);
// No transitions pending.
assert.strictEqual(active(root, undefined), null);
// Two transitions created.
s.transition().delay(50).duration(50);
s.transition("foo").duration(50);
assert.strictEqual(active(root, undefined), null);
// One transition scheduled; one active with a different name.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root, undefined), null);
resolve();
}));
// No transitions remaining after the transition ends.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root, undefined), null);
resolve();
}, 100));
});
it("active(node, name) returns null if the specified node has no active transition with the specified name", async () => {
const root = document.documentElement;
const s = select(root);
// No transitions pending.
assert.strictEqual(active(root, "foo"), null);
// Two transitions created.
s.transition("foo").delay(50).duration(50);
s.transition().duration(50);
assert.strictEqual(active(root, null), null);
// One transition scheduled; one active with a different name.
assert.strictEqual(active(root, "foo"), null);
// One transition scheduled.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root, "foo"), null);
resolve();
}));
// No transitions remaining after the transition ends.
await new Promise(resolve => timeout(() => {
assert.strictEqual(active(root, "foo"), null);
resolve();
}, 100));
});
it("active(node) returns the active transition on the specified node with the null name", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition().on("start", check).tween("tween", tweened).on("end", check);
function check() {
const a = active(root);
assert.deepStrictEqual(a._groups, [[root]]);
assert.deepStrictEqual(a._parents, [null]);
assert.strictEqual(a._name, null);
assert.strictEqual(a._id, t._id);
}
function tweened() {
check();
return t => {
if (t >= 1) check();
};
}
await t.end();
});
it("active(node, name) returns the active transition on the specified node with the specified name", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition("foo").on("start", check).tween("tween", tweened).on("end", check);
function check() {
const a = active(root, "foo");
assert.deepStrictEqual(a._groups, [[root]]);
assert.deepStrictEqual(a._parents, [null]);
assert.strictEqual(a._name, "foo");
assert.strictEqual(a._id, t._id);
}
function tweened() {
check();
return t => {
if (t >= 1) check();
};
}
await t.end();
});
================================================
FILE: test/error-test.js
================================================
import assert from "assert";
import {select} from "d3-selection";
import {timeout} from "d3-timer";
import "../src/index.js";
import it from "./jsdom.js";
describe("with an uncaught error", () => {
let listeners;
beforeEach(() => {
listeners = process.listeners("uncaughtException");
process.removeAllListeners("uncaughtException");
process.once("uncaughtException", () => {});
});
afterEach(() => {
for (const listener of listeners) {
process.on("uncaughtException", listener);
}
});
it("transition.on(\"start\", error) terminates the transition", async () => {
const root = document.documentElement;
const s = select(root);
s.transition().on("start", () => { throw new Error; });
await new Promise(resolve => timeout(resolve));
assert.strictEqual(root.__transition, undefined);
});
it("transition.on(\"start\", error) with delay terminates the transition", async () => {
const root = document.documentElement;
const s = select(root);
s.transition().delay(50).on("start", () => { throw new Error; });
await new Promise(resolve => timeout(resolve, 50));
assert.strictEqual(root.__transition, undefined);
});
it("transition.tween(\"foo\", error) terminates the transition", async () => {
const root = document.documentElement;
const s = select(root);
s.transition().tween("foo", () => { throw new Error; });
await new Promise(resolve => timeout(resolve));
assert.strictEqual(root.__transition, undefined);
});
it("transition.tween(\"foo\", error) with delay terminates the transition", async () => {
const root = document.documentElement;
const s = select(root);
s.transition().delay(50).tween("foo", () => { throw new Error; });
await new Promise(resolve => timeout(resolve, 50));
assert.strictEqual(root.__transition, undefined);
});
it("transition.tween(\"foo\", deferredError) terminates the transition", async () => {
const root = document.documentElement;
const s = select(root);
s.transition().duration(50).tween("foo", () => { return function(t) { if (t === 1) throw new Error; }; });
await new Promise(resolve => timeout(resolve, 50));
assert.strictEqual(root.__transition, undefined);
});
it("transition.on(\"end\", error) terminates the transition", async () => {
const root = document.documentElement;
const s = select(root);
s.transition().delay(50).duration(50).on("end", () => { throw new Error; });
await new Promise(resolve => timeout(resolve, 100));
assert.strictEqual(root.__transition, undefined);
});
});
================================================
FILE: test/interrupt-test.js
================================================
import assert from "assert";
import {select} from "d3-selection";
import {interrupt} from "../src/index.js";
import it from "./jsdom.js";
it("interrupt(node) cancels any pending transitions on the specified node", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition();
const t2 = t1.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
interrupt(root);
assert.strictEqual(root.__transition, undefined);
});
it("selection.interrupt(name) only cancels pending transitions with the specified name", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition("foo");
const t2 = s.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
interrupt(root, "foo");
assert.strictEqual(t1._id in root.__transition, false);
assert.strictEqual(t2._id in root.__transition, true);
});
================================================
FILE: test/jsdom.js
================================================
import {JSDOM} from "jsdom";
export default function jsdomit(message, html, run) {
if (arguments.length < 3) run = html, html = "";
return it(message, async () => {
try {
const dom = new JSDOM(html);
global.window = dom.window;
global.document = dom.window.document;
await run();
} finally {
delete global.window;
delete global.document;
}
});
}
================================================
FILE: test/selection/interrupt-test.js
================================================
import assert from "assert";
import {select} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import {CREATED, ENDED, ENDING, SCHEDULED, STARTED, STARTING} from "../../src/transition/schedule.js";
import it from "../jsdom.js";
it("selection.interrupt() returns the selection", () => {
const s = select(document);
assert.strictEqual(s.interrupt(), s);
});
it("selection.interrupt() cancels any pending transitions on the selected elements", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition();
const t2 = t1.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
assert.strictEqual(s.interrupt(), s);
assert.strictEqual(root.__transition, undefined);
});
it("selection.interrupt() only cancels pending transitions with the null name", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition("foo");
const t2 = s.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
assert.strictEqual(s.interrupt(), s);
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, false);
});
it("selection.interrupt(null) only cancels pending transitions with the null name", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition("foo");
const t2 = s.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
assert.strictEqual(s.interrupt(null), s);
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, false);
});
it("selection.interrupt(undefined) only cancels pending transitions with the null name", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition("foo");
const t2 = s.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
assert.strictEqual(s.interrupt(undefined), s);
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, false);
});
it("selection.interrupt(name) only cancels pending transitions with the specified name", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition("foo");
const t2 = s.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
assert.strictEqual(s.interrupt("foo"), s);
assert.strictEqual(t1._id in root.__transition, false);
assert.strictEqual(t2._id in root.__transition, true);
});
it("selection.interrupt(name) coerces the name to a string", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition("foo");
const t2 = s.transition();
assert.strictEqual(t1._id in root.__transition, true);
assert.strictEqual(t2._id in root.__transition, true);
assert.strictEqual(s.interrupt({toString() { return "foo"; }}), s);
assert.strictEqual(t1._id in root.__transition, false);
assert.strictEqual(t2._id in root.__transition, true);
});
it("selection.interrupt() does nothing if there is no transition on the selected elements", () => {
const root = document.documentElement;
const s = select(root);
assert.strictEqual(root.__transition, undefined);
assert.strictEqual(s.interrupt(), s);
assert.strictEqual(root.__transition, undefined);
});
it("selection.interrupt() dispatches an interrupt event to the started transition on the selected elements", async () => {
const root = document.documentElement;
let interrupts = 0;
const s = select(root);
const t = s.transition().on("interrupt", () => { ++interrupts; });
await new Promise(resolve => timeout(() => {
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.state, STARTED);
s.interrupt();
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
assert.strictEqual(interrupts, 1);
resolve();
}));
});
it("selection.interrupt() destroys the schedule after dispatching the interrupt event", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition().on("interrupt", interrupted);
await new Promise(resolve => timeout(() => {
s.interrupt();
resolve();
}));
function interrupted() {
assert.strictEqual(t.delay(), 0);
assert.strictEqual(t.duration(), 250);
assert.strictEqual(t.on("interrupt"), interrupted);
}
});
it("selection.interrupt() does not dispatch an interrupt event to a starting transition", async () => {
const root = document.documentElement;
let interrupts = 0;
const s = select(root);
const t = s.transition().on("interrupt", () => { ++interrupts; });
await new Promise(resolve => t.on("start", () => {
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.state, STARTING);
s.interrupt();
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
assert.strictEqual(interrupts, 0);
resolve();
}));
});
it("selection.interrupt() prevents a created transition from starting", async () => {
const root = document.documentElement;
let starts = 0;
const s = select(root);
const t = s.transition().on("start", () => { ++starts; });
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.state, CREATED);
s.interrupt();
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
await new Promise(resolve => timeout(resolve, 10));
assert.strictEqual(starts, 0);
});
it("selection.interrupt() prevents a scheduled transition from starting", async () => {
const root = document.documentElement;
let starts = 0;
const s = select(root);
const t = s.transition().delay(50).on("start", () => { ++starts; });
const schedule = root.__transition[t._id];
await new Promise(resolve => timeout(resolve));
assert.strictEqual(schedule.state, SCHEDULED);
s.interrupt();
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
await new Promise(resolve => timeout(resolve, 60));
assert.strictEqual(starts, 0);
});
it("selection.interrupt() prevents a starting transition from initializing tweens", async () => {
const root = document.documentElement;
let tweens = 0;
const s = select(root);
const t = s.transition().tween("tween", () => { ++tweens; });
const schedule = root.__transition[t._id];
await new Promise(resolve => t.on("start", () => {
assert.strictEqual(schedule.state, STARTING);
s.interrupt();
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
resolve();
}));
await new Promise(resolve => timeout(resolve, 10));
assert.strictEqual(tweens, 0);
});
it("selection.interrupt() during tween initialization prevents an active transition from continuing", async () => {
const root = document.documentElement;
let tweens = 0;
const s = select(root);
const t = s.transition().tween("tween", () => { s.interrupt(); return () => { ++tweens; }; });
const schedule = root.__transition[t._id];
await new Promise(resolve => timeout(resolve, 10));
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
assert.strictEqual(tweens, 0);
});
it("selection.interrupt() prevents an active transition from continuing", async () => {
const root = document.documentElement;
let interrupted = false;
let tweens = 0;
const s = select(root);
const t = s.transition().tween("tween", () => () => { if (interrupted) ++tweens; });
const schedule = root.__transition[t._id];
await new Promise(resolve => timeout(() => {
interrupted = true;
assert.strictEqual(schedule.state, STARTED);
s.interrupt();
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
resolve();
}, 10));
await new Promise(resolve => timeout(resolve, 50));
assert.strictEqual(tweens, 0);
});
it("selection.interrupt() during the final tween invocation prevents the end event from being dispatched", async () => {
const root = document.documentElement;
let ends = 0;
const s = select(root);
const t = s.transition().duration(50).tween("tween", tween).on("end", () => { ++ends; });
const schedule = root.__transition[t._id];
function tween() {
return (t) => {
if (t >= 1) {
assert.strictEqual(schedule.state, ENDING);
s.interrupt();
}
};
}
await new Promise(resolve => timeout(() => {
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(root.__transition, undefined);
assert.strictEqual(ends, 0);
resolve();
}, 60));
});
it("selection.interrupt() has no effect on an ended transition", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition().duration(50);
const schedule = root.__transition[t._id];
await t.end();
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(schedule.timer._call, null);
s.interrupt();
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(root.__transition, undefined);
});
it("selection.interrupt() has no effect on an interrupting transition", async () => {
const root = document.documentElement;
let interrupts = 0;
const s = select(root);
const t = s.transition().duration(50).on("interrupt", interrupted);
const schedule = root.__transition[t._id];
function interrupted() {
++interrupts;
s.interrupt();
}
await new Promise(resolve => timeout(() => {
assert.strictEqual(schedule.state, STARTED);
s.interrupt();
assert.strictEqual(schedule.state, ENDED);
assert.strictEqual(schedule.timer._call, null);
assert.strictEqual(interrupts, 1);
resolve();
}));
});
================================================
FILE: test/selection/transition-test.js
================================================
import assert from "assert";
import {easeBounce, easeCubic} from "d3-ease";
import {select, selectAll} from "d3-selection";
import {now, timeout} from "d3-timer";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("selection.transition() returns an instanceof transition", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition();
assert.strictEqual(t instanceof transition, true);
});
it("selection.transition() uses the default timing parameters", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition();
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.time, now());
assert.strictEqual(schedule.delay, 0);
assert.strictEqual(schedule.duration, 250);
assert.strictEqual(schedule.ease, easeCubic);
});
it("selection.transition() assigns a monotonically-increasing id", () => {
const root = document.documentElement;
const s = select(root);
const t1 = s.transition();
const t2 = s.transition();
const t3 = s.transition();
assert(t2._id > t1._id);
assert(t3._id > t2._id);
});
it("selection.transition() uses a default name of null", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition();
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.name, null);
});
it("selection.transition(null) uses a name of null", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition(null);
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.name, null);
});
it("selection.transition(undefined) uses a name of null", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition(undefined);
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.name, null);
});
it("selection.transition(name) uses the specified name", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition("foo");
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.name, "foo");
});
it("selection.transition(name) coerces the name to a string", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition({toString() { return "foo"; }});
const schedule = root.__transition[t._id];
assert.strictEqual(schedule.name, "foo");
});
it("selection.transition(transition) inherits the id, name and timing from the corresponding parent in the specified transition", "<h1 id='one'><child></h1><h1 id='two'><child></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const s = selectAll([one, two]);
const t = s.transition().delay(function(d, i) { return i * 50; }).duration(100).ease(easeBounce);
const schedule1 = one.__transition[t._id];
const schedule2 = two.__transition[t._id];
const t1b = select(one.firstChild).transition(t);
const schedule1b = one.firstChild.__transition[t._id];
assert.strictEqual(t1b._id, t._id);
assert.strictEqual(schedule1b.name, schedule1.name);
assert.strictEqual(schedule1b.delay, schedule1.delay);
assert.strictEqual(schedule1b.duration, schedule1.duration);
assert.strictEqual(schedule1b.ease, schedule1.ease);
assert.strictEqual(schedule1b.time, schedule1.time);
await new Promise(resolve => timeout(resolve, 10));
const t2b = select(two.firstChild).transition(t);
const schedule2b = two.firstChild.__transition[t._id];
assert.strictEqual(t2b._id, t._id);
assert.strictEqual(schedule2b.name, schedule2.name);
assert.strictEqual(schedule2b.delay, schedule2.delay);
assert.strictEqual(schedule2b.duration, schedule2.duration);
assert.strictEqual(schedule2b.ease, schedule2.ease);
assert.strictEqual(schedule2b.time, schedule2.time);
});
it("selection.transition(transition) reselects the existing transition with the specified transition’s id, if any", () => {
const root = document.documentElement;
const foo = () => {};
const bar = () => {};
const s = select(root);
const t1 = s.transition().tween("tween", foo);
const schedule1 = root.__transition[t1._id];
const t2 = s.transition(t1).tween("tween", bar);
const schedule2 = root.__transition[t2._id];
assert.strictEqual(t1._id, t2._id);
assert.strictEqual(schedule1, schedule2);
assert.strictEqual(t1.tween("tween"), bar);
assert.strictEqual(t2.tween("tween"), bar);
});
it("selection.transition(transition) throws an error if the specified transition is not found", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = select(one).transition();
const t2 = select(two).transition().delay(50);
assert.throws(() => select(two).transition(t1), /transition .* not found/);
assert.throws(() => select(one).transition(t2), /transition .* not found/);
});
================================================
FILE: test/transition/attr-test.js
================================================
import assert from "assert";
import {easeCubic} from "d3-ease";
import {interpolateNumber, interpolateRgb, interpolateString} from "d3-interpolate";
import {select, selectAll} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.attr(name, value) creates an tween to the specified value", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root).attr("fill", "red");
s.transition().attr("fill", "blue");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("fill"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, value) creates a namespaced tween to the specified value", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root).attr("svg:fill", "red");
s.transition().attr("svg:fill", "blue");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "fill"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, value) creates an tween to the value returned by the specified function", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root).attr("fill", "red");
s.transition().attr("fill", function() { return "blue"; });
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("fill"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, value) creates a namespaced tween to the value returned by the specified function", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root).attr("svg:fill", "red");
s.transition().attr("svg:fill", function() { return "blue"; });
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "fill"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, constant) is a noop if the string-coerced value matches the current value on tween initialization", async () => {
const root = document.documentElement;
const s = select(root).attr("foo", 1);
s.transition().attr("foo", 1);
timeout(() => root.setAttribute("foo", 2), 125);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.getAttribute("foo"), "2");
resolve();
}, 250));
});
it("transition.attr(ns:name, constant) is a noop if the string-coerced value matches the current value on tween initialization", async () => {
const root = document.documentElement;
const s = select(root).attr("svg:foo", 1);
s.transition().attr("svg:foo", 1);
timeout(() => root.setAttributeNS("http://www.w3.org/2000/svg", "foo", 2), 125);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "foo"), "2");
resolve();
}, 250));
});
it("transition.attr(name, function) is a noop if the string-coerced value matches the current value on tween initialization", async () => {
const root = document.documentElement;
const s = select(root).attr("foo", 1);
s.transition().attr("foo", function() { return 1; });
timeout(() => root.setAttribute("foo", 2), 125);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.getAttribute("foo"), "2");
resolve();
}, 250));
});
it("transition.attr(ns:name, function) is a noop if the string-coerced value matches the current value on tween initialization", async () => {
const root = document.documentElement;
const s = select(root).attr("svg:foo", 1);
s.transition().attr("svg:foo", function() { return 1; });
timeout(() => root.setAttributeNS("http://www.w3.org/2000/svg", "foo", 2), 125);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "foo"), "2");
resolve();
}, 250));
});
it("transition.attr(name, constant) uses interpolateNumber if value is a number", async () => {
const root = document.documentElement;
const s = select(root).attr("foo", "15px");
s.transition().attr("foo", 10);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.getAttribute("foo"), "NaN");
resolve();
}, 125));
});
it("transition.attr(name, function) uses interpolateNumber if value is a number", async () => {
const root = document.documentElement;
const s = select(root).attr("foo", "15px");
s.transition().attr("foo", () => 10);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.getAttribute("foo"), "NaN");
resolve();
}, 125));
});
it("transition.attr(name, value) immediately evaluates the specified function with the expected context and arguments", "<h1 id='one' fill='cyan'></h1><h1 id='two' fill='magenta'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const ease = easeCubic;
const duration = 250;
const interpolate1 = interpolateRgb("cyan", "red");
const interpolate2 = interpolateRgb("magenta", "green");
const s = selectAll([one, two]).data(["red", "green"]);
const result = [];
s.transition().attr("fill", function(d, i, nodes) { result.push([d, i, nodes, this]); return d; });
assert.deepStrictEqual(result, [
["red", 0, [one, two], one],
["green", 1, [one, two], two]
]);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(one.getAttribute("fill"), interpolate1(ease(elapsed / duration)));
assert.strictEqual(two.getAttribute("fill"), interpolate2(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, value) constructs an interpolator using the current value on start", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root);
s.transition().on("start", function() { s.attr("fill", "red"); }).attr("fill", function() { return "blue"; });
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("fill"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, null) creates an tween which removes the specified attribute post-start", async () => {
const root = document.documentElement;
const s = select(root).attr("fill", "red");
const started = () => assert.strictEqual(root.getAttribute("fill"), "red");
s.transition().attr("fill", null).on("start", started);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.hasAttribute("fill"), false);
resolve();
}));
});
it("transition.attr(name, null) creates an tween which removes the specified namespaced attribute post-start", async () => {
const root = document.documentElement;
const s = select(root).attr("svg:fill", "red");
const started = () => assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "fill"), "red");
s.transition().attr("svg:fill", null).on("start", started);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.hasAttributeNS("http://www.w3.org/2000/svg", "fill"), false);
resolve();
}));
});
it("transition.attr(name, value) creates an tween which removes the specified attribute post-start if the specified function returns null", async () => {
const root = document.documentElement;
const s = select(root).attr("fill", "red");
const started = () => assert.strictEqual(root.getAttribute("fill"), "red");
s.transition().attr("fill", function() {}).on("start", started);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.hasAttribute("fill"), false);
resolve();
}));
});
it("transition.attr(name, value) creates an tween which removes the specified namespaced attribute post-start if the specified function returns null", async () => {
const root = document.documentElement;
const s = select(root).attr("svg:fill", "red");
const started = () => assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "fill"), "red");
s.transition().attr("svg:fill", function() {}).on("start", started);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.hasAttributeNS("http://www.w3.org/2000/svg", "fill"), false);
resolve();
}));
});
it("transition.attr(name, value) interpolates numbers", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateNumber(1, 2);
const s = select(root).attr("foo", 1);
s.transition().attr("foo", 2);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("foo"), interpolate(ease(elapsed / duration)) + "");
resolve();
}, 125));
});
it("transition.attr(name, value) interpolates strings", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateString("1px", "2px");
const s = select(root).attr("foo", "1px");
s.transition().attr("foo", "2px");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("foo"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, value) interpolates colors", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("#f00", "#00f");
const s = select(root).attr("foo", "#f00");
s.transition().attr("foo", "#00f");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("foo"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.attr(name, value) creates an attrTween with the specified name", async () => {
const root = document.documentElement;
const s = select(root).attr("fill", "red");
const t = s.transition().attr("fill", "blue");
assert.strictEqual(t.attrTween("fill").call(root).call(root, 0.5), "rgb(128, 0, 128)");
});
it("transition.attr(name, value) creates a tween with the name \"attr.name\"", async () => {
const root = document.documentElement;
const s = select(root).attr("fill", "red");
const t = s.transition().attr("fill", "blue");
t.tween("attr.fill").call(root).call(root, 0.5);
assert.strictEqual(root.getAttribute("fill"), "rgb(128, 0, 128)");
});
================================================
FILE: test/transition/attrTween-test.js
================================================
import assert from "assert";
import {easeCubic} from "d3-ease";
import {interpolateHcl} from "d3-interpolate";
import {select, selectAll} from "d3-selection";
import {timeout, now} from "d3-timer";
import "../../src/index.js";
import {ENDING} from "../../src/transition/schedule.js";
import it from "../jsdom.js";
it("transition.attrTween(name, value) defines an attribute tween using the interpolator returned by the specified function", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
select(root).transition().attrTween("foo", () => interpolate);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("foo"), interpolate(easeCubic(elapsed / 250)));
resolve();
}, 125));
});
it("transition.attrTween(name, value) invokes the value function with the expected context and arguments", "<h1 id='one'></h1><h1 id='two'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const result = [];
selectAll([one, two]).data(["one", "two"]).transition().attrTween("foo", function(d, i, nodes) { result.push([d, i, nodes, this]); });
await new Promise(resolve => timeout(resolve));
assert.deepStrictEqual(result, [
["one", 0, [one, two], one],
["two", 1, [one, two], two]
]);
});
it("transition.attrTween(name, value) passes the eased time to the interpolator", async () => {
const root = document.documentElement;
const then = now();
const duration = 250;
const ease = easeCubic;
const t = select(root).transition().attrTween("foo", () => interpolate);
const schedule = root.__transition[t._id];
function interpolate(t) {
assert.strictEqual(this, root);
assert.strictEqual(t, schedule.state === ENDING ? 1 : ease((now() - then) / duration));
}
await t.end();
});
it("transition.attrTween(name, value) allows the specified function to return null for a noop", async () => {
const root = document.documentElement;
const s = select(root).attr("foo", "42").attr("svg:bar", "43");
s.transition().attrTween("foo", () => {}).attrTween("svg:bar", () => {});
await new Promise(resolve => timeout(resolve, 125));
assert.strictEqual(root.getAttribute("foo"), "42");
assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "bar"), "43");
});
it("transition.attrTween(name, value) defines a namespaced attribute tween using the interpolator returned by the specified function", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
select(root).transition().attrTween("svg:foo", () => interpolate);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttributeNS("http://www.w3.org/2000/svg", "foo"), interpolate(easeCubic(elapsed / 250)));
resolve();
}, 125));
});
it("transition.attrTween(name, value) coerces the specified name to a string", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
select(root).transition().attrTween({toString() { return "foo"; }}, () => interpolate);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.getAttribute("foo"), interpolate(easeCubic(elapsed / 250)));
resolve();
}, 125));
});
it("transition.attrTween(name, value) throws an error if value is not null and not a function", async () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.attrTween("foo", 42); });
});
it("transition.attrTween(name, null) removes the specified attribute tween", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const t = select(root).transition().attrTween("foo", () => interpolate).attrTween("foo", null);
assert.strictEqual(t.attrTween("foo"), null);
assert.strictEqual(t.tween("attr.foo"), null);
await new Promise(resolve => timeout(resolve, 125));
assert.strictEqual(root.hasAttribute("foo"), false);
});
it("transition.attrTween(name) returns the attribute tween with the specified name", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const tween = () => interpolate;
const started = () => assert.strictEqual(t.attrTween("foo"), tween);
const ended = () => assert.strictEqual(t.attrTween("foo"), tween);
const t = select(root).transition().attrTween("foo", tween).on("start", started).on("end", ended);
assert.strictEqual(t.attrTween("foo"), tween);
assert.strictEqual(t.attrTween("bar"), null);
await t.end();
});
it("transition.attrTween(name) coerces the specified name to a string", async () => {
const root = document.documentElement;
const tween = () => {};
const t = select(root).transition().attrTween("color", tween);
assert.strictEqual(t.attrTween({toString() { return "color"; }}), tween);
});
================================================
FILE: test/transition/call-test.js
================================================
import assert from "assert";
import {selection} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.call is the same as selection.call", () => {
assert.strictEqual(transition.prototype.call, selection.prototype.call);
});
================================================
FILE: test/transition/delay-test.js
================================================
import assert from "assert";
import {select, selectAll} from "d3-selection";
import it from "../jsdom.js";
import "../../src/index.js";
it("transition.delay() returns the delay for the first non-null node", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = select(one).transition();
const t2 = select(two).transition().delay(50);
assert.strictEqual(one.__transition[t1._id].delay, 0);
assert.strictEqual(two.__transition[t2._id].delay, 50);
assert.strictEqual(t1.delay(), 0);
assert.strictEqual(t2.delay(), 50);
assert.strictEqual(select(one).transition(t1).delay(), 0);
assert.strictEqual(select(two).transition(t2).delay(), 50);
assert.strictEqual(selectAll([null, one]).transition(t1).delay(), 0);
assert.strictEqual(selectAll([null, two]).transition(t2).delay(), 50);
});
it("transition.delay(number) sets the delay for each selected element to the specified number", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().delay(50);
assert.strictEqual(one.__transition[t._id].delay, 50);
assert.strictEqual(two.__transition[t._id].delay, 50);
});
it("transition.delay(value) coerces the specified value to a number", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().delay("50");
assert.strictEqual(one.__transition[t._id].delay, 50);
assert.strictEqual(two.__transition[t._id].delay, 50);
});
it("transition.delay(function) passes the expected arguments and context to the function", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const result = [];
const s = selectAll([one, two]).data(["one", "two"]);
const t = s.transition().delay(function(d, i, nodes) { result.push([d, i, nodes, this]); });
assert.deepStrictEqual(result, [
["one", 0, t._groups[0], one],
["two", 1, t._groups[0], two]
]);
});
it("transition.delay(function) sets the delay for each selected element to the number returned by the specified function", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().delay(function(d, i) { return i * 20; });
assert.strictEqual(one.__transition[t._id].delay, 0);
assert.strictEqual(two.__transition[t._id].delay, 20);
});
it("transition.delay(function) coerces the value returned by the specified function to a number", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().delay(function(d, i) { return i * 20 + ""; });
assert.strictEqual(one.__transition[t._id].delay, 0);
assert.strictEqual(two.__transition[t._id].delay, 20);
});
================================================
FILE: test/transition/duration-test.js
================================================
import assert from "assert";
import {select, selectAll} from "d3-selection";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.duration() returns the duration for the first non-null node", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = select(one).transition();
const t2 = select(two).transition().duration(50);
assert.strictEqual(one.__transition[t1._id].duration, 250);
assert.strictEqual(two.__transition[t2._id].duration, 50);
assert.strictEqual(t1.duration(), 250);
assert.strictEqual(t2.duration(), 50);
assert.strictEqual(select(one).transition(t1).duration(), 250);
assert.strictEqual(select(two).transition(t2).duration(), 50);
assert.strictEqual(selectAll([null, one]).transition(t1).duration(), 250);
assert.strictEqual(selectAll([null, two]).transition(t2).duration(), 50);
});
it("transition.duration(number) sets the duration for each selected element to the specified number", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().duration(50);
assert.strictEqual(one.__transition[t._id].duration, 50);
assert.strictEqual(two.__transition[t._id].duration, 50);
});
it("transition.duration(value) coerces the specified value to a number", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().duration("50");
assert.strictEqual(one.__transition[t._id].duration, 50);
assert.strictEqual(two.__transition[t._id].duration, 50);
});
it("transition.duration(function) passes the expected arguments and context to the function", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const result = [];
const s = selectAll([one, two]).data(["one", "two"]);
const t = s.transition().duration(function(d, i, nodes) { result.push([d, i, nodes, this]); });
assert.deepStrictEqual(result, [
["one", 0, t._groups[0], one],
["two", 1, t._groups[0], two]
]);
});
it("transition.duration(function) sets the duration for each selected element to the number returned by the specified function", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().duration(function(d, i) { return i * 20; });
assert.strictEqual(one.__transition[t._id].duration, 0);
assert.strictEqual(two.__transition[t._id].duration, 20);
});
it("transition.duration(function) coerces the value returned by the specified function to a number", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().duration(function(d, i) { return i * 20 + ""; });
assert.strictEqual(one.__transition[t._id].duration, 0);
assert.strictEqual(two.__transition[t._id].duration, 20);
});
================================================
FILE: test/transition/each-test.js
================================================
import assert from "assert";
import {select, selectAll, selection} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.each is the same as selection.each", () => {
assert.strictEqual(transition.prototype.each, selection.prototype.each);
});
it("transition.each() runs as expected", () => {
const root = document.documentElement;
let a = 0;
select(root).transition().each(() => { ++a; });
assert.strictEqual(a, 1);
a = 0;
selectAll([null, root]).transition().each(() => { ++a; });
assert.strictEqual(a, 1);
});
================================================
FILE: test/transition/ease-test.js
================================================
import assert from "assert";
import {easeBounce, easeCubic} from "d3-ease";
import {select, selectAll} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import {ENDING, RUNNING} from "../../src/transition/schedule.js";
import it from "../jsdom.js";
it("transition.ease() returns the easing function for the first non-null node", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = select(one).transition();
const t2 = select(two).transition().ease(easeBounce);
assert.strictEqual(one.__transition[t1._id].ease, easeCubic);
assert.strictEqual(two.__transition[t2._id].ease, easeBounce);
assert.strictEqual(t1.ease(), easeCubic);
assert.strictEqual(t2.ease(), easeBounce);
assert.strictEqual(select(one).transition(t1).ease(), easeCubic);
assert.strictEqual(select(two).transition(t2).ease(), easeBounce);
assert.strictEqual(selectAll([null, one]).transition(t1).ease(), easeCubic);
assert.strictEqual(selectAll([null, two]).transition(t2).ease(), easeBounce);
});
it("transition.ease(ease) throws an error if ease is not a function", () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.ease(42); });
assert.throws(() => { t.ease(null); });
});
it("transition.ease(ease) sets the easing function for each selected element to the specified function", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().ease(easeBounce);
assert.strictEqual(one.__transition[t._id].ease, easeBounce);
assert.strictEqual(two.__transition[t._id].ease, easeBounce);
});
it("transition.ease(ease) passes the easing function the normalized time in [0, 1]", async () => {
let actual;
const root = document.documentElement;
const ease = t => { actual = t; return t; };
select(root).transition().ease(ease);
await new Promise(resolve => timeout((elapsed) => {
assert.strictEqual(actual, elapsed / 250);
resolve()
}, 100));
});
it("transition.ease(ease) does not invoke the easing function on the last frame", async () => {
const root = document.documentElement;
const ease = t => { assert.strictEqual(schedule.state, RUNNING); return t; };
const t = select(root).transition().ease(ease);
const schedule = root.__transition[t._id];
await t.end();
});
it("transition.ease(ease) observes the eased time returned by the easing function", async () => {
const root = document.documentElement;
let expected;
const ease = () => { return expected = Math.random() * 2 - 0.5; };
const tween = () => { return t => { assert.strictEqual(t, schedule.state === ENDING ? 1 : expected); }; };
const t = select(root).transition().ease(ease).tween("tween", tween);
const schedule = root.__transition[t._id];
await t.end();
});
================================================
FILE: test/transition/easeVarying-test.js
================================================
import assert from "assert";
import {easePolyIn} from "d3-ease";
import {select} from "d3-selection";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.easeVarying(factory) accepts an easing function factory", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const t = select(document).selectAll("h1").data([{exponent: 3}, {exponent: 4}]).transition();
t.easeVarying(d => easePolyIn.exponent(d.exponent));
assert.strictEqual(t.ease()(0.5), easePolyIn.exponent(3)(0.5));
});
it("transition.easeVarying(factory) passes factory datum, index, group with the node as this", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const t = select(document).selectAll("h1").data([{exponent: 3}, {exponent: 4}]).transition();
const results = [];
t.easeVarying(function(d, i, e) { results.push([d, i, e, this]); return t => t; });
assert.deepStrictEqual(results, [
[{exponent: 3}, 0, [...t], document.querySelector("#one")],
[{exponent: 4}, 1, [...t], document.querySelector("#two")],
]);
});
it("transition.easeVarying() throws an error if the argument is not a function", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const t = select(document).selectAll("h1").data([{exponent: 3}, {exponent: 4}]).transition();
assert.throws(() => { t.easeVarying(); });
assert.throws(() => { t.easeVarying("a"); });
});
================================================
FILE: test/transition/empty-test.js
================================================
import assert from "assert";
import {selection} from "d3-selection";
import {transition} from "../../src/index.js";
it("transition.empty is the same as selection.empty", () => {
assert.strictEqual(transition.prototype.empty, selection.prototype.empty);
});
================================================
FILE: test/transition/filter-test.js
================================================
import assert from "assert";
import {selectAll} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.filter(selector) retains the elements matching the specified selector", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.filter("#two");
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups, [[two]]);
assert.strictEqual(t2._parents, t1._parents);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
});
it("transition.filter(function) retains the elements for which the specified function returns true", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.filter(function() { return this === two; });
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups, [[two]]);
assert.strictEqual(t2._parents, t1._parents);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
});
================================================
FILE: test/transition/index-test.js
================================================
import assert from "assert";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition() returns a transition on the document element with the null name", () => {
const root = document.documentElement;
const t = transition();
const schedule = root.__transition[t._id];
assert.strictEqual(t.node(), root);
assert.strictEqual(schedule.name, null);
});
it("transition(null) returns a transition on the document element with the null name", () => {
const root = document.documentElement;
const t = transition(null);
const schedule = root.__transition[t._id];
assert.strictEqual(t.node(), root);
assert.strictEqual(schedule.name, null);
});
it("transition(undefined) returns a transition on the document element with the null name", () => {
const root = document.documentElement;
const t = transition(undefined);
const schedule = root.__transition[t._id];
assert.strictEqual(t.node(), root);
assert.strictEqual(schedule.name, null);
});
it("transition(name) returns a transition on the document element with the specified name", () => {
const root = document.documentElement;
const t = transition("foo");
const schedule = root.__transition[t._id];
assert.strictEqual(t.node(), root);
assert.strictEqual(schedule.name, "foo");
});
it("transition.prototype can be extended", () => {
try {
let pass = 0;
transition.prototype.test = () => { return ++pass; };
assert.strictEqual(transition().test(), 1);
assert.strictEqual(pass, 1);
} finally {
delete transition.prototype.test;
}
});
it("transitions are instanceof transition", () => {
assert.strictEqual(transition() instanceof transition, true);
});
================================================
FILE: test/transition/merge-test.js
================================================
import assert from "assert";
import {select, selectAll} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.merge(other) merges elements from the specified other transition for null elements in this transition", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t0 = select(document.documentElement).transition();
const t1 = selectAll([null, two]).transition(t0);
const t2 = selectAll([one, null]).transition(t0);
const t3 = t1.merge(t2);
assert.strictEqual(t3 instanceof transition, true);
assert.deepStrictEqual(t3._groups, [[one, two]]);
assert.strictEqual(t3._parents, t1._parents);
assert.strictEqual(t3._name, t1._name);
assert.strictEqual(t3._id, t1._id);
});
it("transition.merge(other) throws an error if the other transition has a different id", "<h1 id='one'></h1><h1 id='two'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([null, two]).transition();
const t2 = selectAll([one, null]).transition();
assert.throws(() => { t1.merge(t2); });
});
================================================
FILE: test/transition/node-test.js
================================================
import assert from "assert";
import {selection} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.node is the same as selection.node", () => {
assert.strictEqual(transition.prototype.node, selection.prototype.node);
});
================================================
FILE: test/transition/nodes-test.js
================================================
import assert from "assert";
import {selection} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.nodes is the same as selection.nodes", () => {
assert.strictEqual(transition.prototype.nodes, selection.prototype.nodes);
});
================================================
FILE: test/transition/on-test.js
================================================
import assert from "assert";
import {select, selectAll} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import {ENDED, ENDING, STARTING} from "../../src/transition/schedule.js";
import it from "../jsdom.js";
it("transition.on(type, listener) throws an error if listener is not a function", async () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.on("start", 42); });
});
it("transition.on(typename) returns the listener with the specified typename, if any", async () => {
const root = document.documentElement;
const foo = () => {};
const bar = () => {};
const t = select(root).transition().on("start", foo).on("start.bar", bar);
assert.strictEqual(t.on("start"), foo);
assert.strictEqual(t.on("start.foo"), undefined);
assert.strictEqual(t.on("start.bar"), bar);
assert.strictEqual(t.on("end"), undefined);
});
it("transition.on(typename) throws an error if the specified type is not supported", async () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.on("foo"); });
});
it("transition.on(typename, listener) throws an error if the specified type is not supported", async () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.on("foo", () => {}); });
});
it("transition.on(typename, listener) throws an error if the specified listener is not a function", async () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.on("foo", 42); });
});
it("transition.on(typename, null) removes the listener with the specified typename, if any", async () => {
const root = document.documentElement;
let starts = 0;
const t = select(root).transition().on("start.foo", () => { ++starts; });
const schedule = root.__transition[t._id];
assert.strictEqual(t.on("start.foo", null), t);
assert.strictEqual(t.on("start.foo"), undefined);
assert.strictEqual(schedule.on.on("start.foo"), undefined);
await new Promise(resolve => timeout(resolve));
assert.strictEqual(starts, 0);
});
it("transition.on(\"start\", listener) registers a listener for the start event", async () => {
const root = document.documentElement;
const t = select(root).transition();
const schedule = root.__transition[t._id];
await new Promise(resolve => t.on("start", () => {
assert.strictEqual(schedule.state, STARTING)
resolve();
}));
});
it("transition.on(\"interrupt\", listener) registers a listener for the interrupt event (during start)", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition();
const schedule = root.__transition[t._id];
timeout(() => s.interrupt());
await new Promise(resolve => t.on("interrupt", () => {
assert.strictEqual(schedule.state, ENDED);
resolve();
}));
});
it("transition.on(\"interrupt\", listener) registers a listener for the interrupt event (during run)", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition();
const schedule = root.__transition[t._id];
timeout(() => s.interrupt(), 50);
await new Promise(resolve => t.on("interrupt", () => {
assert.strictEqual(schedule.state, ENDED);
resolve();
}));
});
it("transition.on(\"end\", listener) registers a listener for the end event", async () => {
const root = document.documentElement;
const t = select(root).transition().duration(50);
const schedule = root.__transition[t._id];
await new Promise(resolve => t.on("end", () => {
assert.strictEqual(schedule.state, ENDING);
resolve();
}));
});
it("transition.on(typename, listener) uses copy-on-write to apply changes", "<h1 id='one'></h1><h1 id='two'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const foo = () => {};
const bar = () => {};
const t = selectAll([one, two]).transition();
const schedule1 = one.__transition[t._id];
const schedule2 = two.__transition[t._id];
t.on("start", foo);
assert.strictEqual(schedule1.on.on("start"), foo);
assert.strictEqual(schedule2.on, schedule1.on);
t.on("start", bar);
assert.strictEqual(schedule1.on.on("start"), bar);
assert.strictEqual(schedule2.on, schedule1.on);
select(two).transition(t).on("start", foo);
assert.strictEqual(schedule1.on.on("start"), bar);
assert.strictEqual(schedule2.on.on("start"), foo);
});
================================================
FILE: test/transition/remove-test.js
================================================
import assert from "assert";
import {select} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.remove() creates an end listener to remove the element", async () => {
const root = document.documentElement;
const body = document.body;
const s = select(body);
const t = s.transition().remove().on("start", started).on("end", ended);
const end = t.end();
function started() {
assert.strictEqual(body.parentNode, root);
}
function ended() {
assert.strictEqual(body.parentNode, null);
}
await new Promise(resolve => timeout(resolve));
assert.strictEqual(body.parentNode, root);
await end;
});
it("transition.remove() creates an end listener named end.remove", async () => {
const root = document.documentElement;
const body = document.body;
const s = select(body);
const t = s.transition().remove().on("start", started).on("end", ended);
const end = t.end();
function started() {
assert.strictEqual(body.parentNode, root);
}
function ended() {
assert.strictEqual(body.parentNode, root);
}
t.on("end.remove").call(body);
assert.strictEqual(body.parentNode, null);
t.on("end.remove", null);
root.appendChild(body);
await end;
});
================================================
FILE: test/transition/select-test.js
================================================
import assert from "assert";
import {selectAll} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.select(selector) selects the descendants matching the specified selector, then derives a transition", "<h1 id='one'><child/></h1><h1 id='two'><child/></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.select("child");
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups, [[one.firstChild, two.firstChild]]);
assert.strictEqual(t2._parents, t1._parents);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
assert.strictEqual(one.firstChild.__data__, 1);
assert.strictEqual(two.firstChild.__data__, 2);
assert.strictEqual(one.firstChild.__transition[t1._id].delay, 10);
assert.strictEqual(two.firstChild.__transition[t1._id].delay, 20);
});
it("transition.select(function) selects the descendants returned by the specified function, then derives a transition", "<h1 id='one'><child/></h1><h1 id='two'><child/></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.select(function() { return this.firstChild; });
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups, [[one.firstChild, two.firstChild]]);
assert.strictEqual(t2._parents, t1._parents);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
assert.strictEqual(one.firstChild.__data__, 1);
assert.strictEqual(two.firstChild.__data__, 2);
assert.strictEqual(one.firstChild.__transition[t1._id].delay, 10);
assert.strictEqual(two.firstChild.__transition[t1._id].delay, 20);
});
================================================
FILE: test/transition/selectAll-test.js
================================================
import assert from "assert";
import {selectAll} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.selectAll(selector) selects the descendants matching the specified selector, then derives a transition", "<h1 id='one'><child/></h1><h1 id='two'><child/></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.selectAll("child");
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups.map(group => Array.from(group)), [[one.firstChild], [two.firstChild]]);
assert.deepStrictEqual(t2._parents, [one, two]);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
assert.strictEqual("__data__" in one.firstChild, false);
assert.strictEqual("__data__" in two.firstChild, false);
assert.strictEqual(one.firstChild.__transition[t1._id].delay, 10);
assert.strictEqual(two.firstChild.__transition[t1._id].delay, 20);
});
it("transition.selectAll(function) selects the descendants returned by the specified function, then derives a transition", "<h1 id='one'><child/></h1><h1 id='two'><child/></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.selectAll(function() { return [this.firstChild]; });
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups, [[one.firstChild], [two.firstChild]]);
assert.deepStrictEqual(t2._parents, [one, two]);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
assert.strictEqual("__data__" in one.firstChild, false);
assert.strictEqual("__data__" in two.firstChild, false);
assert.strictEqual(one.firstChild.__transition[t1._id].delay, 10);
assert.strictEqual(two.firstChild.__transition[t1._id].delay, 20);
});
================================================
FILE: test/transition/selectChild-test.js
================================================
import assert from "assert";
import {selectAll} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.selectChild(selector) selects the child matching the specified selector, then derives a transition", "<h1 id='one'><child/></h1><h1 id='two'><child/></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.selectChild("child");
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups, [[one.firstChild, two.firstChild]]);
assert.strictEqual(t2._parents, t1._parents);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
assert.strictEqual(one.firstChild.__data__, 1);
assert.strictEqual(two.firstChild.__data__, 2);
assert.strictEqual(one.firstChild.__transition[t1._id].delay, 10);
assert.strictEqual(two.firstChild.__transition[t1._id].delay, 20);
});
================================================
FILE: test/transition/selectChildren-test.js
================================================
import assert from "assert";
import {selectAll} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.selectChildren(selector) selects the children matching the specified selector, then derives a transition", "<h1 id='one'><child/></h1><h1 id='two'><child/></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t1 = selectAll([one, two]).data([1, 2]).transition().delay(function(d) { return d * 10; });
const t2 = t1.selectChildren("child");
assert.strictEqual(t2 instanceof transition, true);
assert.deepStrictEqual(t2._groups.map(group => Array.from(group)), [[one.firstChild], [two.firstChild]]);
assert.deepStrictEqual(t2._parents, [one, two]);
assert.strictEqual(t2._name, t1._name);
assert.strictEqual(t2._id, t1._id);
assert.strictEqual("__data__" in one.firstChild, false);
assert.strictEqual("__data__" in two.firstChild, false);
assert.strictEqual(one.firstChild.__transition[t1._id].delay, 10);
assert.strictEqual(two.firstChild.__transition[t1._id].delay, 20);
});
================================================
FILE: test/transition/selection-test.js
================================================
import assert from "assert";
import {select, selection} from "d3-selection";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.selection() returns the transition’s selection", "<h1 id='one'>one</h1><h1 id='two'>two</h1>", () => {
const s0 = select(document.body).selectAll("h1");
const t = s0.transition();
const s1 = t.selection();
assert(s1 instanceof selection);
assert.strictEqual(s1._groups, s0._groups);
assert.strictEqual(s1._parents, s0._parents);
});
================================================
FILE: test/transition/size-test.js
================================================
import assert from "assert";
import {select, selectAll, selection} from "d3-selection";
import {transition} from "../../src/index.js";
import it from "../jsdom.js";
it("transition.size is the same as selection.size", () => {
assert.strictEqual(transition.prototype.size, selection.prototype.size);
});
it("transition.size() returns the expected value", () => {
const root = document.documentElement;
assert.strictEqual(select(root).transition().size(), 1);
assert.strictEqual(selectAll([null, root]).transition().size(), 1);
});
================================================
FILE: test/transition/style-test.js
================================================
import assert from "assert";
import {easeCubic} from "d3-ease";
import {interpolateNumber, interpolateRgb, interpolateString} from "d3-interpolate";
import {select, selectAll} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.style(name, value) creates an tween to the specified value", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root).style("color", "red");
s.transition().style("color", "blue");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.style.getPropertyValue("color"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.style(name, value) creates an tween to the value returned by the specified function", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root).style("color", "red");
s.transition().style("color", () => "blue");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.style.getPropertyValue("color"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.style(name, value) immediately evaluates the specified function with the expected context and arguments", "<h1 id='one' style='color:#0ff;'></h1><h1 id='two' style='color:#f0f;'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const ease = easeCubic;
const duration = 250;
const interpolate1 = interpolateRgb("cyan", "red");
const interpolate2 = interpolateRgb("magenta", "green");
const t = selectAll([one, two]).data(["red", "green"]);
const result = [];
t.transition().style("color", function(d, i, nodes) { result.push([d, i, nodes, this]); return d; });
assert.deepStrictEqual(result, [
["red", 0, [one, two], one],
["green", 1, [one, two], two]
]);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(one.style.getPropertyValue("color"), interpolate1(ease(elapsed / duration)));
assert.strictEqual(two.style.getPropertyValue("color"), interpolate2(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.style(name, value) recycles tweens ", "<h1 id='one' style='color:#f0f;'></h1><h1 id='two' style='color:#f0f;'></h1>", () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const t = selectAll([one, two]).transition().style("color", "red");
assert.strictEqual(one.__transition[t._id].tween, two.__transition[t._id].tween);
});
it("transition.style(name, value) constructs an interpolator using the current value on start", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("red", "blue");
const s = select(root);
s.transition().on("start", () => { s.style("color", "red"); }).style("color", () => "blue");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.style.getPropertyValue("color"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.style(name, null) creates an tween which removes the specified style post-start", async () => {
const root = document.documentElement;
const started = () => assert.strictEqual(root.style.getPropertyValue("color"), "red");
const s = select(root).style("color", "red");
s.transition().style("color", null).on("start", started);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("color"), "");
resolve();
}));
});
it("transition.style(name, null) creates an tween which removes the specified style post-start", async () => {
const root = document.documentElement;
const started = () => assert.strictEqual(root.style.getPropertyValue("color"), "red");
const selection = select(root).style("color", "red");
selection.transition().style("color", () => null).on("start", started);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("color"), "");
resolve();
}));
});
it("transition.style(name, value) creates an tween which removes the specified style post-start if the specified function returns null", async () => {
const root = document.documentElement;
const started = () => assert.strictEqual(root.style.getPropertyValue("color"), "red");
const selection = select(root).style("color", "red");
selection.transition().style("color", function() {}).on("start", started);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("color"), "");
resolve();
}));
});
it("transition.style(name, constant) is a noop if the string-coerced value matches the current value on tween initialization", async () => {
const root = document.documentElement;
const selection = select(root).style("opacity", 1);
selection.transition().style("opacity", 1);
timeout(() => { root.style.opacity = 0.5; }, 125);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("opacity"), "0.5");
resolve();
}, 250));
});
it("transition.style(name, function) is a noop if the string-coerced value matches the current value on tween initialization", async () => {
const root = document.documentElement;
const selection = select(root).style("opacity", 1);
selection.transition().style("opacity", function() { return 1; });
timeout(() => { root.style.opacity = 0.5; }, 125);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("opacity"), "0.5");
resolve();
}, 250));
});
it("transition.style(name, value) interpolates numbers", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateNumber(0, 1);
const s = select(root).style("opacity", 0);
s.transition().style("opacity", 1);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.style.getPropertyValue("opacity"), interpolate(ease(elapsed / duration)) + "");
resolve();
}, 125));
});
it("transition.style(name, constant) uses interpolateNumber if value is a number", async () => {
const root = document.documentElement;
const s = select(root).style("font-size", "15px");
s.transition().style("font-size", 10);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("font-size"), "15px"); // ignores NaN
resolve();
}, 125));
});
it("transition.style(name, function) uses interpolateNumber if value is a number", async () => {
const root = document.documentElement;
const s = select(root).style("font-size", "15px");
s.transition().style("font-size", () => 10);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("font-size"), "15px"); // ignores NaN
resolve();
}, 125));
});
it("transition.style(name, value) interpolates strings", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateString("1px", "2px");
const s = select(root).style("font-size", "1px");
s.transition().style("font-size", "2px");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.style.getPropertyValue("font-size"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.style(name, value) interpolates colors", async () => {
const root = document.documentElement;
const ease = easeCubic;
const duration = 250;
const interpolate = interpolateRgb("#f00", "#00f");
const s = select(root).style("color", "#f00");
s.transition().style("color", "#00f");
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(root.style.getPropertyValue("color"), interpolate(ease(elapsed / duration)));
resolve();
}, 125));
});
it("transition.style(name, value) creates an styleTween with the specified name", async () => {
const root = document.documentElement;
const s = select(root).style("color", "red");
const t = s.transition().style("color", "blue");
assert.strictEqual(t.styleTween("color").call(root).call(root, 0.5), "rgb(128, 0, 128)");
});
it("transition.style(name, value) creates a tween with the name \"style.name\"", async () => {
const root = document.documentElement;
const s = select(root).style("color", "red");
const t = s.transition().style("color", "blue");
t.tween("style.color").call(root).call(root, 0.5);
assert.strictEqual(root.style.getPropertyValue("color"), "rgb(128, 0, 128)");
});
================================================
FILE: test/transition/styleTween-test.js
================================================
import assert from "assert";
import {easeCubic} from "d3-ease";
import {interpolateHcl} from "d3-interpolate";
import {select, selectAll} from "d3-selection";
import {now, timeout} from "d3-timer";
import "../../src/index.js";
import {ENDING} from "../../src/transition/schedule.js";
import it from "../jsdom.js";
it("transition.styleTween(name, value) defines a style tween using the interpolator returned by the specified function", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const ease = easeCubic;
select(root).transition().styleTween("color", () => interpolate);
await new Promise(resolve => timeout(elapsed => {
assert.deepStrictEqual(root.style.getPropertyValue("color"), interpolate(ease(elapsed / 250)));
assert.deepStrictEqual(root.style.getPropertyPriority("color"), "");
resolve();
}, 125));
});
it("transition.styleTween(name, value, priority) defines a style tween using the interpolator returned by the specified function", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const ease = easeCubic;
select(root).transition().styleTween("color", () => interpolate, "important");
await new Promise(resolve => timeout(elapsed => {
assert.deepStrictEqual(root.style.getPropertyValue("color"), interpolate(ease(elapsed / 250)));
assert.deepStrictEqual(root.style.getPropertyPriority("color"), "important");
resolve();
}, 125));
});
it("transition.styleTween(name, value) invokes the value function with the expected context and arguments", "<h1 id='one'></h1><h1 id='two'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const result = [];
selectAll([one, two]).data(["one", "two"]).transition().styleTween("color", function(d, i, nodes) { result.push([d, i, nodes, this]); });
await new Promise(resolve => timeout(resolve));
assert.deepStrictEqual(result, [
["one", 0, [one, two], one],
["two", 1, [one, two], two]
]);
});
it("transition.styleTween(name, value) passes the eased time to the interpolator", async () => {
const root = document.documentElement;
const then = now();
const duration = 250;
const ease = easeCubic;
const t = select(root).transition().styleTween("color", () => interpolate);
const schedule = root.__transition[t._id];
function interpolate(t) {
assert.strictEqual(this, root);
assert.strictEqual(t, schedule.state === ENDING ? 1 : ease((now() - then) / duration));
}
await t.end();
});
it("transition.styleTween(name, value) allows the specified function to return null for a noop", async () => {
const root = document.documentElement;
const s = select(root).style("color", "red");
s.transition().styleTween("color", () => {});
await new Promise(resolve => timeout(() => {
assert.deepStrictEqual(root.style.getPropertyValue("color"), "red");
resolve();
}, 125));
});
it("transition.styleTween(name, value) coerces the specified name to a string", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const ease = easeCubic;
select(root).transition().styleTween({toString() { return "color"; }}, () => interpolate);
await new Promise(resolve => timeout(elapsed => {
assert.deepStrictEqual(root.style.getPropertyValue("color"), interpolate(ease(elapsed / 250)));
resolve();
}, 125));
});
it("transition.styleTween(name, value) throws an error if value is not null and not a function", async () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.styleTween("color", 42); });
});
it("transition.styleTween(name, null) removes the specified style tween", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const t = select(root).transition().styleTween("color", () => interpolate).styleTween("color", null);
assert.strictEqual(t.styleTween("color"), null);
assert.strictEqual(t.tween("style.color"), null);
await new Promise(resolve => timeout(() => {
assert.strictEqual(root.style.getPropertyValue("color"), "");
resolve();
}, 125));
});
it("transition.styleTween(name) returns the style tween with the specified name", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const tween = () => interpolate;
const started = () => { assert.strictEqual(t.styleTween("color"), tween); };
const ended = () => { assert.strictEqual(t.styleTween("color"), tween); };
const t = select(root).transition().styleTween("color", tween).on("start", started).on("end", ended);
assert.strictEqual(t.styleTween("color"), tween);
assert.strictEqual(t.styleTween("bar"), null);
await t.end();
});
it("transition.styleTween(name) coerces the specified name to a string", async () => {
const root = document.documentElement;
const tween = () => {};
const t = select(root).transition().styleTween("color", tween);
assert.strictEqual(t.styleTween({toString() { return "color"; }}), tween);
});
================================================
FILE: test/transition/text-test.js
================================================
import assert from "assert";
import {select, selectAll} from "d3-selection";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.text(value) creates a tween to set the text content to the specified value post-start", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition().text("hello");
await new Promise(resolve => t.on("start", () => {
assert.strictEqual(root.textContent, "");
resolve();
}));
assert.strictEqual(root.textContent, "hello");
});
it("transition.text(value) creates a tween to set the text content to the value returned by the specified function post-start", async () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition().text(() => "hello");
await new Promise(resolve => t.on("start", () => {
assert.strictEqual(root.textContent, "");
resolve();
}));
assert.strictEqual(root.textContent, "hello");
});
it("transition.text(value) immediately evaluates the specified function with the expected context and arguments", "<h1 id='one'></h1><h1 id='two'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const s = selectAll([one, two]).data(["red", "green"]);
const result = [];
const t = s.transition().text(function(d, i, nodes) { result.push([d, i, nodes, this]); return d; });
assert.deepStrictEqual(result, [
["red", 0, [one, two], one],
["green", 1, [one, two], two]
]);
await new Promise(resolve => t.on("start", resolve));
assert.strictEqual(one.textContent, "red");
assert.strictEqual(two.textContent, "green");
});
it("transition.text(value) creates a tween with the name \"text\"", () => {
const root = document.documentElement;
const s = select(root);
const t = s.transition().text("hello");
assert.strictEqual(t.tween("text").call(root), undefined);
assert.strictEqual(root.textContent, "hello");
});
================================================
FILE: test/transition/textTween-test.js
================================================
import assert from "assert";
import {easeCubic} from "d3-ease";
import {interpolateHcl} from "d3-interpolate";
import {select} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.textTween(value) defines a text tween using the interpolator returned by the specified function", async () => {
const root = document.documentElement;
const interpolate = interpolateHcl("red", "blue");
const ease = easeCubic;
select(root).transition().textTween(() => interpolate);
await new Promise(resolve => timeout(elapsed => {
assert.deepStrictEqual(root.textContent, interpolate(ease(elapsed / 250)));
resolve();
}, 125));
});
it("transition.textTween() returns the existing text tween", () => {
const root = document.documentElement;
const factory = () => {};
const t = select(root).transition().textTween(factory);
assert.strictEqual(t.textTween(), factory);
});
it("transition.textTween(null) removes an existing text tween", () => {
const root = document.documentElement;
const factory = () => {};
const t = select(root).transition().textTween(factory);
t.textTween(undefined);
assert.strictEqual(t.textTween(), null);
});
================================================
FILE: test/transition/transition-test.js
================================================
import assert from "assert";
import {select} from "d3-selection";
import {timeout} from "d3-timer";
import "../../src/index.js";
import it from "../jsdom.js";
it("transition.transition() allows preceeding transitions with zero duration to end naturally", async () => {
let end0 = false;
let end1 = false;
let end2 = false;
const s = select(document.documentElement);
const t = s.transition().duration(0).on("end", () => { end0 = true; });
s.transition().duration(0).on("end", () => { end1 = true; });
t.transition().duration(0).on("end", () => { end2 = true; });
await new Promise(resolve => timeout(resolve, 50));
assert.strictEqual(end0, true);
assert.strictEqual(end1, true);
assert.strictEqual(end2, true);
});
================================================
FILE: test/transition/tween-test.js
================================================
import assert from "assert";
import {easeCubic} from "d3-ease";
import {select, selectAll} from "d3-selection";
import {now, timeout} from "d3-timer";
import "../../src/index.js";
import {ENDING} from "../../src/transition/schedule.js";
import it from "../jsdom.js";
it("transition.tween(name, value) defines an tween using the interpolator returned by the specified function", async () => {
const root = document.documentElement;
let value;
const interpolate = t => { value = t; };
select(root).transition().tween("foo", () => interpolate);
await new Promise(resolve => timeout(elapsed => {
assert.strictEqual(value, easeCubic(elapsed / 250));
resolve();
}, 125));
});
it("transition.tween(name, value) invokes the value function with the expected context and arguments", "<h1 id='one'></h1><h1 id='two'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const result = [];
selectAll([one, two]).data(["one", "two"]).transition().tween("foo", function(d, i, nodes) { result.push([d, i, nodes, this]); });
await new Promise(resolve => timeout(resolve));
assert.deepStrictEqual(result, [
["one", 0, [one, two], one],
["two", 1, [one, two], two]
]);
});
it("transition.tween(name, value) passes the eased time to the interpolator", async () => {
const root = document.documentElement;
const then = now();
const duration = 250;
const ease = easeCubic;
const t = select(root).transition().tween("foo", () => interpolate);
const schedule = root.__transition[t._id];
function interpolate(t) {
assert.strictEqual(this, root);
assert.strictEqual(t, schedule.state === ENDING ? 1 : ease((now() - then) / duration));
}
await t.end();
});
it("transition.tween(name, value) allows the specified function to return null for a noop", async () => {
const root = document.documentElement;
const s = select(root);
s.transition().tween("foo", () => {});
});
it("transition.tween(name, value) uses copy-on-write to apply changes", "<h1 id='one'></h1><h1 id='two'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const foo = () => {};
const bar = () => {};
const t = selectAll([one, two]).transition();
const schedule1 = one.__transition[t._id];
const schedule2 = two.__transition[t._id];
t.tween("foo", foo);
assert.deepStrictEqual(schedule1.tween, [{name: "foo", value: foo}]);
assert.strictEqual(schedule2.tween, schedule1.tween);
t.tween("foo", bar);
assert.deepStrictEqual(schedule1.tween, [{name: "foo", value: bar}]);
assert.strictEqual(schedule2.tween, schedule1.tween);
select(two).transition(t).tween("foo", foo);
assert.deepStrictEqual(schedule1.tween, [{name: "foo", value: bar}]);
assert.deepStrictEqual(schedule2.tween, [{name: "foo", value: foo}]);
});
it("transition.tween(name, value) uses copy-on-write to apply removals", "<h1 id='one'></h1><h1 id='two'></h1>", async () => {
const one = document.querySelector("#one");
const two = document.querySelector("#two");
const foo = () => {};
const t = selectAll([one, two]).transition();
const schedule1 = one.__transition[t._id];
const schedule2 = two.__transition[t._id];
t.tween("foo", foo);
assert.deepStrictEqual(schedule1.tween, [{name: "foo", value: foo}]);
assert.strictEqual(schedule2.tween, schedule1.tween);
t.tween("bar", null);
assert.deepStrictEqual(schedule1.tween, [{name: "foo", value: foo}]);
assert.strictEqual(schedule2.tween, schedule1.tween);
t.tween("foo", null);
assert.deepStrictEqual(schedule1.tween, []);
assert.strictEqual(schedule2.tween, schedule1.tween);
select(two).transition(t).tween("foo", foo);
assert.deepStrictEqual(schedule1.tween, []);
assert.deepStrictEqual(schedule2.tween, [{name: "foo", value: foo}]);
});
it("transition.tween(name, value) coerces the specified name to a string", async () => {
const root = document.documentElement;
const tween = () => {};
const t = select(root).transition().tween({toString() { return "foo"; }}, tween);
assert.strictEqual(t.tween("foo"), tween);
});
it("transition.tween(name) coerces the specified name to a string", async () => {
const root = document.documentElement;
const tween = () => {};
const t = select(root).transition().tween("foo", tween);
assert.strictEqual(t.tween({toString() { return "foo"; }}), tween);
});
it("transition.tween(name, value) throws an error if value is not null and not a function", async () => {
const root = document.documentElement;
const t = select(root).transition();
assert.throws(() => { t.tween("foo", 42); });
});
it("transition.tween(name, null) removes the specified tween", async () => {
const root = document.documentElement;
let frames = 0;
const interpolate = () => { ++frames; };
const t = select(root).transition().tween("foo", () => interpolate).tween("foo", null);
assert.strictEqual(t.tween("foo"), null);
await new Promise(resolve => timeout(() => {
assert.strictEqual(frames, 0);
resolve();
}, 125));
});
it("transition.tween(name) returns the tween with the specified name", async () => {
const root = document.documentElement;
const tween = () => {};
const started = () => { assert.strictEqual(t.tween("foo"), tween); }
const ended = () => { assert.strictEqual(t.tween("foo"), tween); }
const t = select(root).transition().tween("foo", tween).on("start", started).on("end", ended);
assert.strictEqual(t.tween("foo"), tween);
assert.strictEqual(t.tween("bar"), null);
await t.end();
});
gitextract_uicvwejc/
├── .eslintrc.json
├── .github/
│ ├── eslint.json
│ └── workflows/
│ └── node.js.yml
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── rollup.config.js
├── src/
│ ├── active.js
│ ├── index.js
│ ├── interrupt.js
│ ├── selection/
│ │ ├── index.js
│ │ ├── interrupt.js
│ │ └── transition.js
│ └── transition/
│ ├── attr.js
│ ├── attrTween.js
│ ├── delay.js
│ ├── duration.js
│ ├── ease.js
│ ├── easeVarying.js
│ ├── end.js
│ ├── filter.js
│ ├── index.js
│ ├── interpolate.js
│ ├── merge.js
│ ├── on.js
│ ├── remove.js
│ ├── schedule.js
│ ├── select.js
│ ├── selectAll.js
│ ├── selection.js
│ ├── style.js
│ ├── styleTween.js
│ ├── text.js
│ ├── textTween.js
│ ├── transition.js
│ └── tween.js
└── test/
├── .eslintrc.json
├── active-test.js
├── error-test.js
├── interrupt-test.js
├── jsdom.js
├── selection/
│ ├── interrupt-test.js
│ └── transition-test.js
└── transition/
├── attr-test.js
├── attrTween-test.js
├── call-test.js
├── delay-test.js
├── duration-test.js
├── each-test.js
├── ease-test.js
├── easeVarying-test.js
├── empty-test.js
├── filter-test.js
├── index-test.js
├── merge-test.js
├── node-test.js
├── nodes-test.js
├── on-test.js
├── remove-test.js
├── select-test.js
├── selectAll-test.js
├── selectChild-test.js
├── selectChildren-test.js
├── selection-test.js
├── size-test.js
├── style-test.js
├── styleTween-test.js
├── text-test.js
├── textTween-test.js
├── transition-test.js
└── tween-test.js
SYMBOL INDEX (65 symbols across 25 files)
FILE: rollup.config.js
method onwarn (line 25) | onwarn(message, warn) {
FILE: src/selection/transition.js
function inherit (line 13) | function inherit(node, id) {
FILE: src/transition/attr.js
function attrRemove (line 6) | function attrRemove(name) {
function attrRemoveNS (line 12) | function attrRemoveNS(fullname) {
function attrConstant (line 18) | function attrConstant(name, interpolate, value1) {
function attrConstantNS (line 30) | function attrConstantNS(fullname, interpolate, value1) {
function attrFunction (line 42) | function attrFunction(name, interpolate, value) {
function attrFunctionNS (line 57) | function attrFunctionNS(fullname, interpolate, value) {
FILE: src/transition/attrTween.js
function attrInterpolate (line 3) | function attrInterpolate(name, i) {
function attrInterpolateNS (line 9) | function attrInterpolateNS(fullname, i) {
function attrTweenNS (line 15) | function attrTweenNS(fullname, value) {
function attrTween (line 26) | function attrTween(name, value) {
FILE: src/transition/delay.js
function delayFunction (line 3) | function delayFunction(id, value) {
function delayConstant (line 9) | function delayConstant(id, value) {
FILE: src/transition/duration.js
function durationFunction (line 3) | function durationFunction(id, value) {
function durationConstant (line 9) | function durationConstant(id, value) {
FILE: src/transition/ease.js
function easeConstant (line 3) | function easeConstant(id, value) {
FILE: src/transition/easeVarying.js
function easeVarying (line 3) | function easeVarying(id, value) {
FILE: src/transition/index.js
function Transition (line 25) | function Transition(groups, parents, name, id) {
function transition (line 32) | function transition(name) {
function newId (line 36) | function newId() {
FILE: src/transition/on.js
function start (line 3) | function start(name) {
function onFunction (line 11) | function onFunction(id, name, listener) {
FILE: src/transition/remove.js
function removeFunction (line 1) | function removeFunction(id) {
FILE: src/transition/schedule.js
function init (line 34) | function init(node, id) {
function set (line 40) | function set(node, id) {
function get (line 46) | function get(node, id) {
function create (line 52) | function create(node, id, self) {
FILE: src/transition/style.js
function styleNull (line 7) | function styleNull(name, interpolate) {
function styleRemove (line 20) | function styleRemove(name) {
function styleConstant (line 26) | function styleConstant(name, interpolate, value1) {
function styleFunction (line 38) | function styleFunction(name, interpolate, value) {
function styleMaybeRemove (line 53) | function styleMaybeRemove(id, name) {
FILE: src/transition/styleTween.js
function styleInterpolate (line 1) | function styleInterpolate(name, i, priority) {
function styleTween (line 7) | function styleTween(name, value, priority) {
FILE: src/transition/text.js
function textConstant (line 3) | function textConstant(value) {
function textFunction (line 9) | function textFunction(value) {
FILE: src/transition/textTween.js
function textInterpolate (line 1) | function textInterpolate(i) {
function textTween (line 7) | function textTween(value) {
FILE: src/transition/tween.js
function tweenRemove (line 3) | function tweenRemove(id, name) {
function tweenFunction (line 27) | function tweenFunction(id, name, value) {
function tweenValue (line 70) | function tweenValue(transition, name, value) {
FILE: test/active-test.js
function check (line 115) | function check() {
function tweened (line 123) | function tweened() {
function check (line 138) | function check() {
function tweened (line 146) | function tweened() {
FILE: test/jsdom.js
function jsdomit (line 3) | function jsdomit(message, html, run) {
FILE: test/selection/interrupt-test.js
method toString (line 79) | toString() { return "foo"; }
function interrupted (line 117) | function interrupted() {
function tween (line 229) | function tween() {
function interrupted (line 267) | function interrupted() {
FILE: test/selection/transition-test.js
method toString (line 71) | toString() { return "foo"; }
FILE: test/transition/attrTween-test.js
function interpolate (line 39) | function interpolate(t) {
method toString (line 68) | toString() { return "foo"; }
method toString (line 107) | toString() { return "color"; }
FILE: test/transition/remove-test.js
function started (line 14) | function started() {
function ended (line 18) | function ended() {
function started (line 34) | function started() {
function ended (line 38) | function ended() {
FILE: test/transition/styleTween-test.js
function interpolate (line 53) | function interpolate(t) {
method toString (line 74) | toString() { return "color"; }
method toString (line 115) | toString() { return "color"; }
FILE: test/transition/tween-test.js
function interpolate (line 39) | function interpolate(t) {
method toString (line 95) | toString() { return "foo"; }
method toString (line 103) | toString() { return "foo"; }
Condensed preview — 72 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (138K chars).
[
{
"path": ".eslintrc.json",
"chars": 187,
"preview": "{\n \"extends\": \"eslint:recommended\",\n \"parserOptions\": {\n \"sourceType\": \"module\",\n \"ecmaVersion\": 8\n },\n \"env\":"
},
{
"path": ".github/eslint.json",
"chars": 367,
"preview": "{\n \"problemMatcher\": [\n {\n \"owner\": \"eslint-compact\",\n \"pattern\": [\n {\n \"regexp\": \"^(.+):\\"
},
{
"path": ".github/workflows/node.js.yml",
"chars": 648,
"preview": "# https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions\n\nname: Node.js CI\n\non:\n"
},
{
"path": ".gitignore",
"chars": 63,
"preview": "*.sublime-workspace\n.DS_Store\ndist/\nnode_modules\nnpm-debug.log\n"
},
{
"path": "LICENSE",
"chars": 731,
"preview": "Copyright 2010-2021 Mike Bostock\n\nPermission to use, copy, modify, and/or distribute this software for any purpose\nwith "
},
{
"path": "README.md",
"chars": 653,
"preview": "# d3-transition\n\n<a href=\"https://d3js.org\"><img src=\"https://github.com/d3/d3/raw/main/docs/public/logo.svg\" width=\"256"
},
{
"path": "package.json",
"chars": 1909,
"preview": "{\n \"name\": \"d3-transition\",\n \"version\": \"3.0.1\",\n \"description\": \"Animated transitions for D3 selections.\",\n \"homepa"
},
{
"path": "rollup.config.js",
"chars": 1244,
"preview": "import {readFileSync} from \"fs\";\nimport {terser} from \"rollup-plugin-terser\";\nimport * as meta from \"./package.json\";\n\n/"
},
{
"path": "src/active.js",
"chars": 490,
"preview": "import {Transition} from \"./transition/index.js\";\nimport {SCHEDULED} from \"./transition/schedule.js\";\n\nvar root = [null]"
},
{
"path": "src/index.js",
"chars": 192,
"preview": "import \"./selection/index.js\";\nexport {default as transition} from \"./transition/index.js\";\nexport {default as active} f"
},
{
"path": "src/interrupt.js",
"chars": 676,
"preview": "import {STARTING, ENDING, ENDED} from \"./transition/schedule.js\";\n\nexport default function(node, name) {\n var schedules"
},
{
"path": "src/selection/index.js",
"chars": 251,
"preview": "import {selection} from \"d3-selection\";\nimport selection_interrupt from \"./interrupt.js\";\nimport selection_transition fr"
},
{
"path": "src/selection/interrupt.js",
"chars": 141,
"preview": "import interrupt from \"../interrupt.js\";\n\nexport default function(name) {\n return this.each(function() {\n interrupt("
},
{
"path": "src/selection/transition.js",
"chars": 1093,
"preview": "import {Transition, newId} from \"../transition/index.js\";\nimport schedule from \"../transition/schedule.js\";\nimport {ease"
},
{
"path": "src/transition/attr.js",
"chars": 2673,
"preview": "import {interpolateTransformSvg as interpolateTransform} from \"d3-interpolate\";\nimport {namespace} from \"d3-selection\";\n"
},
{
"path": "src/transition/attrTween.js",
"chars": 1148,
"preview": "import {namespace} from \"d3-selection\";\n\nfunction attrInterpolate(name, i) {\n return function(t) {\n this.setAttribut"
},
{
"path": "src/transition/delay.js",
"chars": 513,
"preview": "import {get, init} from \"./schedule.js\";\n\nfunction delayFunction(id, value) {\n return function() {\n init(this, id).d"
},
{
"path": "src/transition/duration.js",
"chars": 531,
"preview": "import {get, set} from \"./schedule.js\";\n\nfunction durationFunction(id, value) {\n return function() {\n set(this, id)."
},
{
"path": "src/transition/ease.js",
"chars": 351,
"preview": "import {get, set} from \"./schedule.js\";\n\nfunction easeConstant(id, value) {\n if (typeof value !== \"function\") throw new"
},
{
"path": "src/transition/easeVarying.js",
"chars": 357,
"preview": "import {set} from \"./schedule.js\";\n\nfunction easeVarying(id, value) {\n return function() {\n var v = value.apply(this"
},
{
"path": "src/transition/end.js",
"chars": 833,
"preview": "import {set} from \"./schedule.js\";\n\nexport default function() {\n var on0, on1, that = this, id = that._id, size = that."
},
{
"path": "src/transition/filter.js",
"chars": 577,
"preview": "import {matcher} from \"d3-selection\";\nimport {Transition} from \"./index.js\";\n\nexport default function(match) {\n if (typ"
},
{
"path": "src/transition/index.js",
"chars": 2346,
"preview": "import {selection} from \"d3-selection\";\nimport transition_attr from \"./attr.js\";\nimport transition_attrTween from \"./att"
},
{
"path": "src/transition/interpolate.js",
"chars": 340,
"preview": "import {color} from \"d3-color\";\nimport {interpolateNumber, interpolateRgb, interpolateString} from \"d3-interpolate\";\n\nex"
},
{
"path": "src/transition/merge.js",
"chars": 656,
"preview": "import {Transition} from \"./index.js\";\n\nexport default function(transition) {\n if (transition._id !== this._id) throw n"
},
{
"path": "src/transition/on.js",
"chars": 854,
"preview": "import {get, set, init} from \"./schedule.js\";\n\nfunction start(name) {\n return (name + \"\").trim().split(/^|\\s+/).every(f"
},
{
"path": "src/transition/remove.js",
"chars": 284,
"preview": "function removeFunction(id) {\n return function() {\n var parent = this.parentNode;\n for (var i in this.__transitio"
},
{
"path": "src/transition/schedule.js",
"chars": 4617,
"preview": "import {dispatch} from \"d3-dispatch\";\nimport {timer, timeout} from \"d3-timer\";\n\nvar emptyOn = dispatch(\"start\", \"end\", \""
},
{
"path": "src/transition/select.js",
"chars": 832,
"preview": "import {selector} from \"d3-selection\";\nimport {Transition} from \"./index.js\";\nimport schedule, {get} from \"./schedule.js"
},
{
"path": "src/transition/selectAll.js",
"chars": 889,
"preview": "import {selectorAll} from \"d3-selection\";\nimport {Transition} from \"./index.js\";\nimport schedule, {get} from \"./schedule"
},
{
"path": "src/transition/selection.js",
"chars": 174,
"preview": "import {selection} from \"d3-selection\";\n\nvar Selection = selection.prototype.constructor;\n\nexport default function() {\n "
},
{
"path": "src/transition/style.js",
"chars": 2746,
"preview": "import {interpolateTransformCss as interpolateTransform} from \"d3-interpolate\";\nimport {style} from \"d3-selection\";\nimpo"
},
{
"path": "src/transition/styleTween.js",
"chars": 742,
"preview": "function styleInterpolate(name, i, priority) {\n return function(t) {\n this.style.setProperty(name, i.call(this, t), "
},
{
"path": "src/transition/text.js",
"chars": 476,
"preview": "import {tweenValue} from \"./tween.js\";\n\nfunction textConstant(value) {\n return function() {\n this.textContent = valu"
},
{
"path": "src/transition/textTween.js",
"chars": 599,
"preview": "function textInterpolate(i) {\n return function(t) {\n this.textContent = i.call(this, t);\n };\n}\n\nfunction textTween("
},
{
"path": "src/transition/transition.js",
"chars": 697,
"preview": "import {Transition, newId} from \"./index.js\";\nimport schedule, {get} from \"./schedule.js\";\n\nexport default function() {\n"
},
{
"path": "src/transition/tween.js",
"chars": 2025,
"preview": "import {get, set} from \"./schedule.js\";\n\nfunction tweenRemove(id, name) {\n var tween0, tween1;\n return function() {\n "
},
{
"path": "test/.eslintrc.json",
"chars": 203,
"preview": "{\n \"extends\": \"eslint:recommended\",\n \"parserOptions\": {\n \"sourceType\": \"module\",\n \"ecmaVersion\": 8\n },\n \"env\":"
},
{
"path": "test/active-test.js",
"chars": 4647,
"preview": "import assert from \"assert\";\nimport {select} from \"d3-selection\";\nimport {timeout} from \"d3-timer\";\nimport {active} from"
},
{
"path": "test/error-test.js",
"chars": 2614,
"preview": "import assert from \"assert\";\nimport {select} from \"d3-selection\";\nimport {timeout} from \"d3-timer\";\nimport \"../src/index"
},
{
"path": "test/interrupt-test.js",
"chars": 1025,
"preview": "import assert from \"assert\";\nimport {select} from \"d3-selection\";\nimport {interrupt} from \"../src/index.js\";\nimport it f"
},
{
"path": "test/jsdom.js",
"chars": 401,
"preview": "import {JSDOM} from \"jsdom\";\n\nexport default function jsdomit(message, html, run) {\n if (arguments.length < 3) run = ht"
},
{
"path": "test/selection/interrupt-test.js",
"chars": 10593,
"preview": "import assert from \"assert\";\nimport {select} from \"d3-selection\";\nimport {timeout} from \"d3-timer\";\nimport \"../../src/in"
},
{
"path": "test/selection/transition-test.js",
"chars": 4999,
"preview": "import assert from \"assert\";\nimport {easeBounce, easeCubic} from \"d3-ease\";\nimport {select, selectAll} from \"d3-selectio"
},
{
"path": "test/transition/attr-test.js",
"chars": 10918,
"preview": "import assert from \"assert\";\nimport {easeCubic} from \"d3-ease\";\nimport {interpolateNumber, interpolateRgb, interpolateSt"
},
{
"path": "test/transition/attrTween-test.js",
"chars": 4957,
"preview": "import assert from \"assert\";\nimport {easeCubic} from \"d3-ease\";\nimport {interpolateHcl} from \"d3-interpolate\";\nimport {s"
},
{
"path": "test/transition/call-test.js",
"chars": 286,
"preview": "import assert from \"assert\";\nimport {selection} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/delay-test.js",
"chars": 3138,
"preview": "import assert from \"assert\";\nimport {select, selectAll} from \"d3-selection\";\nimport it from \"../jsdom.js\";\nimport \"../.."
},
{
"path": "test/transition/duration-test.js",
"chars": 3239,
"preview": "import assert from \"assert\";\nimport {select, selectAll} from \"d3-selection\";\nimport \"../../src/index.js\";\nimport it from"
},
{
"path": "test/transition/each-test.js",
"chars": 589,
"preview": "import assert from \"assert\";\nimport {select, selectAll, selection} from \"d3-selection\";\nimport {transition} from \"../../"
},
{
"path": "test/transition/ease-test.js",
"chars": 2986,
"preview": "import assert from \"assert\";\nimport {easeBounce, easeCubic} from \"d3-ease\";\nimport {select, selectAll} from \"d3-selectio"
},
{
"path": "test/transition/easeVarying-test.js",
"chars": 1349,
"preview": "import assert from \"assert\";\nimport {easePolyIn} from \"d3-ease\";\nimport {select} from \"d3-selection\";\nimport \"../../src/"
},
{
"path": "test/transition/empty-test.js",
"chars": 260,
"preview": "import assert from \"assert\";\nimport {selection} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\n\nit("
},
{
"path": "test/transition/filter-test.js",
"chars": 1375,
"preview": "import assert from \"assert\";\nimport {selectAll} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/index-test.js",
"chars": 1701,
"preview": "import assert from \"assert\";\nimport {transition} from \"../../src/index.js\";\nimport it from \"../jsdom.js\";\n\nit(\"transitio"
},
{
"path": "test/transition/merge-test.js",
"chars": 1219,
"preview": "import assert from \"assert\";\nimport {select, selectAll} from \"d3-selection\";\nimport {transition} from \"../../src/index.j"
},
{
"path": "test/transition/node-test.js",
"chars": 286,
"preview": "import assert from \"assert\";\nimport {selection} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/nodes-test.js",
"chars": 290,
"preview": "import assert from \"assert\";\nimport {selection} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/on-test.js",
"chars": 4550,
"preview": "import assert from \"assert\";\nimport {select, selectAll} from \"d3-selection\";\nimport {timeout} from \"d3-timer\";\nimport \"."
},
{
"path": "test/transition/remove-test.js",
"chars": 1277,
"preview": "import assert from \"assert\";\nimport {select} from \"d3-selection\";\nimport {timeout} from \"d3-timer\";\nimport \"../../src/in"
},
{
"path": "test/transition/select-test.js",
"chars": 1990,
"preview": "import assert from \"assert\";\nimport {selectAll} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/selectAll-test.js",
"chars": 2082,
"preview": "import assert from \"assert\";\nimport {selectAll} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/selectChild-test.js",
"chars": 1055,
"preview": "import assert from \"assert\";\nimport {selectAll} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/selectChildren-test.js",
"chars": 1119,
"preview": "import assert from \"assert\";\nimport {selectAll} from \"d3-selection\";\nimport {transition} from \"../../src/index.js\";\nimpo"
},
{
"path": "test/transition/selection-test.js",
"chars": 497,
"preview": "import assert from \"assert\";\nimport {select, selection} from \"d3-selection\";\nimport \"../../src/index.js\";\nimport it from"
},
{
"path": "test/transition/size-test.js",
"chars": 539,
"preview": "import assert from \"assert\";\nimport {select, selectAll, selection} from \"d3-selection\";\nimport {transition} from \"../../"
},
{
"path": "test/transition/style-test.js",
"chars": 8872,
"preview": "import assert from \"assert\";\nimport {easeCubic} from \"d3-ease\";\nimport {interpolateNumber, interpolateRgb, interpolateSt"
},
{
"path": "test/transition/styleTween-test.js",
"chars": 5183,
"preview": "import assert from \"assert\";\nimport {easeCubic} from \"d3-ease\";\nimport {interpolateHcl} from \"d3-interpolate\";\nimport {s"
},
{
"path": "test/transition/text-test.js",
"chars": 1981,
"preview": "import assert from \"assert\";\nimport {select, selectAll} from \"d3-selection\";\nimport \"../../src/index.js\";\nimport it from"
},
{
"path": "test/transition/textTween-test.js",
"chars": 1230,
"preview": "import assert from \"assert\";\nimport {easeCubic} from \"d3-ease\";\nimport {interpolateHcl} from \"d3-interpolate\";\nimport {s"
},
{
"path": "test/transition/transition-test.js",
"chars": 738,
"preview": "import assert from \"assert\";\nimport {select} from \"d3-selection\";\nimport {timeout} from \"d3-timer\";\nimport \"../../src/in"
},
{
"path": "test/transition/tween-test.js",
"chars": 5600,
"preview": "import assert from \"assert\";\nimport {easeCubic} from \"d3-ease\";\nimport {select, selectAll} from \"d3-selection\";\nimport {"
}
]
About this extraction
This page contains the full source code of the d3/d3-transition GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 72 files (124.7 KB), approximately 34.0k tokens, and a symbol index with 65 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.