Commit 108d65da authored by Mike Bostock's avatar Mike Bostock

Merge branch '2.9.7'

parents fb86373a 540d3ace
......@@ -2,6 +2,7 @@
NODE_PATH ?= ./node_modules
JS_COMPILER = $(NODE_PATH)/uglify-js/bin/uglifyjs
JS_BEAUTIFIER = $(NODE_PATH)/uglify-js/bin/uglifyjs -b -i 2 -nm -ns
JS_TESTER = $(NODE_PATH)/vows/bin/vows
all: \
......@@ -74,6 +75,7 @@ d3.core.js: \
src/core/formatPrefix.js \
src/core/ease.js \
src/core/event.js \
src/core/transform.js \
src/core/interpolate.js \
src/core/uninterpolate.js \
src/core/rgb.js \
......@@ -107,6 +109,7 @@ d3.core.js: \
src/core/transition.js \
src/core/transition-select.js \
src/core/transition-selectAll.js \
src/core/transition-filter.js \
src/core/transition-attr.js \
src/core/transition-style.js \
src/core/transition-text.js \
......@@ -116,7 +119,6 @@ d3.core.js: \
src/core/transition-each.js \
src/core/transition-transition.js \
src/core/timer.js \
src/core/transform.js \
src/core/mouse.js \
src/core/touches.js \
src/core/noop.js
......@@ -225,7 +227,7 @@ test: all
d3%.js: Makefile
@rm -f $@
cat $(filter %.js,$^) > $@
cat $(filter %.js,$^) | $(JS_BEAUTIFIER) > $@
@chmod a-w $@
package.json: src/package.js
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -33,7 +33,7 @@ var angle = d3.scale.linear()
.range([0, 2 * Math.PI]);
var line = d3.svg.line.radial()
.interpolate("basis-closed")
.interpolate("linear-closed")
.radius(radius)
.angle(function(d, i) { return angle(i); });
......
......@@ -14,7 +14,7 @@ var width = 960,
var fill = d3.scale.linear()
.range(["hsl(-180, 50%, 50%)", "hsl(180, 50%, 50%)"])
.interpolate(d3.interpolateHsl);
.interpolate(d3.interpolateString);
var arc = d3.svg.arc()
.startAngle(0)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<body>
<script src="../../d3.v2.js"></script>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500);
var g = svg.append("g")
.attr("transform", "translate(100,100)")
.append("g");
var rect = g.append("rect")
.attr("x", -25)
.attr("y", -50)
.attr("width", 50)
.attr("height", 100);
g.transition()
.duration(3000)
.attr("transform", "matrix(1 0 0 1 100 100)rotate(360)");
</script>
<!DOCTYPE html>
<body>
<script src="../../d3.v2.js"></script>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500);
var g = svg.append("g")
.attr("transform", "translate(100,100)")
.append("g");
var rect = g.append("rect")
.attr("x", -25)
.attr("y", -50)
.attr("width", 50)
.attr("height", 100);
g.transition()
.duration(3000)
.attr("transform", "translate(100,100)rotate(360)");
</script>
......@@ -51,13 +51,14 @@ var tests = [
{start: 225, end: 170, expected: [-135.00, -148.75, -162.50, -176.25, 170.00]},
{start: -170, end: -225, expected: [-170.00, 176.25, 162.50, 148.75, 135.00]},
{start: -225, end: -170, expected: [ 135.00, 148.75, 162.50, 176.25, -170.00]},
{start: -170, end: 170, expected: [-170.00, -175.00, 180.00, 175.00, 170.00]},
{start: -170, end: 170, expected: [-170.00, -85.00, 0.00, 85.00, 170.00]},
{start: -170, end: 0, expected: [-170.00, -127.50, -85.00, -42.50, 0.00]},
{start: 170, end: 0, expected: [ 170.00, 127.50, 85.00, 42.50, 0.00]},
{start: -180, end: 90, expected: [ 180.00, 157.50, 135.00, 112.50, 90.00]},
{start: -180, end: 90, expected: [-180.00, -112.50, -45.00, 22.50, 90.00]},
{start: 180, end: 90, expected: [ 180.00, 157.50, 135.00, 112.50, 90.00]},
{start: -180, end: -90, expected: [-180.00, -157.50, -135.00, -112.50, -90.00]},
{start: 180, end: -90, expected: [ 180.00, -157.50, -135.00, -112.50, -90.00]}
{start: 180, end: -90, expected: [ 180.00, 112.50, 45.00, -22.50, -90.00]},
{start: 780, end: -90, expected: [ 60.00, -157.50, -15.00, 127.50, -90.00]}
];
var tr = d3.select("tbody").selectAll("tr")
......@@ -84,35 +85,41 @@ tr.selectAll(".actual")
.text(function(d, i) { return format(d.actual); })
.attr("class", function(d) { return Math.abs(d.actual - d.expected) < .01 ? "success" : "fail"; });
tr.append("td").attr("width", 40).append("svg")
var ga = tr.append("td").attr("width", 40).append("svg")
.attr("width", 40)
.attr("height", 20)
.append("g")
.attr("transform", "translate(20,10)")
.append("path")
.attr("d", d3.svg.symbol().type("cross").size(120))
.append("g")
.each(animateExpected);
tr.append("td").attr("width", 40).append("svg")
ga.append("path")
.attr("d", d3.svg.symbol().type("cross").size(120));
ga.append("circle")
.attr("cx", 8)
.attr("r", 4);
var gb = tr.append("td").attr("width", 40).append("svg")
.attr("width", 40)
.attr("height", 20)
.append("g")
.attr("transform", "translate(20,10)")
.append("path")
.attr("d", d3.svg.symbol().type("cross").size(120))
.append("g")
.each(animateActual);
gb.append("path")
.attr("d", d3.svg.symbol().type("cross").size(120));
gb.append("circle")
.attr("cx", 8)
.attr("r", 4);
function animateExpected(d) {
d3.select(this).transition()
.duration(2500)
.attrTween("transform", rotateTween)
.attrTween("transform", function(d) { return d3.interpolateString("rotate(" + d.start + ")", "rotate(" + d.end + ")"); })
.each("end", animateExpected);
function rotateTween(d) {
if (d.start - d.end > 180) d.end += 360;
else if (d.end - d.start > 180) d.start += 360;
return d3.interpolateString("rotate(" + d.start + ")", "rotate(" + d.end + ")");
}
}
function animateActual(d) {
......
{
"name": "d3",
"version": "2.9.6",
"version": "2.9.7",
"description": "A small, free JavaScript library for manipulating documents based on data.",
"keywords": [
"dom",
......
......@@ -136,13 +136,15 @@ d3.behavior.zoom = function() {
touches.forEach(function(t) { translate0[t.identifier] = location(t); });
d3_eventCancel();
if ((touches.length === 1) && (now - touchtime < 500)) { // dbltap
var p = touches[0], l = location(touches[0]);
scaleTo(scale * 2);
translateTo(p, l);
dispatch(event.of(this, arguments));
if (touches.length === 1) {
if (now - touchtime < 500) { // dbltap
var p = touches[0], l = location(touches[0]);
scaleTo(scale * 2);
translateTo(p, l);
dispatch(event.of(this, arguments));
}
touchtime = now;
}
touchtime = now;
}
function touchmove() {
......@@ -156,6 +158,7 @@ d3.behavior.zoom = function() {
scaleTo(d3.event.scale * scale0);
}
translateTo(p0, l0);
touchtime = null;
dispatch(event.of(this, arguments));
}
......
......@@ -4,7 +4,7 @@ d3.bisector = function(f) {
if (arguments.length < 3) lo = 0;
if (arguments.length < 4) hi = a.length;
while (lo < hi) {
var mid = lo + hi >> 1;
var mid = lo + hi >>> 1;
if (f.call(a, a[mid], mid) < x) lo = mid + 1;
else hi = mid;
}
......@@ -14,7 +14,7 @@ d3.bisector = function(f) {
if (arguments.length < 3) lo = 0;
if (arguments.length < 4) hi = a.length;
while (lo < hi) {
var mid = lo + hi >> 1;
var mid = lo + hi >>> 1;
if (x < f.call(a, a[mid], mid)) hi = mid;
else lo = mid + 1;
}
......
d3 = {version: "2.9.6"}; // semver
d3 = {version: "2.9.7"}; // semver
......@@ -17,7 +17,7 @@ d3.interpolateRound = function(a, b) {
d3.interpolateString = function(a, b) {
var m, // current match
i, // current index
j, // current index (for coallescing)
j, // current index (for coalescing)
s0 = 0, // start index of current string prefix
s1 = 0, // end index of current string prefix
s = [], // string constants and placeholders
......@@ -40,13 +40,13 @@ d3.interpolateString = function(a, b) {
// Find all numbers in a.
for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) {
o = q[i];
if (o.x == m[0]) { // The numbers match, so coallesce.
if (o.x == m[0]) { // The numbers match, so coalesce.
if (o.i) {
if (s[o.i + 1] == null) { // This match is followed by another number.
s[o.i - 1] += o.x;
s.splice(o.i, 1);
for (j = i + 1; j < n; ++j) q[j].i--;
} else { // This match is followed by a string, so coallesce twice.
} else { // This match is followed by a string, so coalesce twice.
s[o.i - 1] += o.x + s[o.i + 1];
s.splice(o.i, 2);
for (j = i + 1; j < n; ++j) q[j].i -= 2;
......@@ -54,7 +54,7 @@ d3.interpolateString = function(a, b) {
} else {
if (s[o.i + 1] == null) { // This match is followed by another number.
s[o.i] = o.x;
} else { // This match is followed by a string, so coallesce twice.
} else { // This match is followed by a string, so coalesce twice.
s[o.i] = o.x + s[o.i + 1];
s.splice(o.i + 1, 1);
for (j = i + 1; j < n; ++j) q[j].i--;
......@@ -73,7 +73,7 @@ d3.interpolateString = function(a, b) {
o = q.pop();
if (s[o.i + 1] == null) { // This match is followed by another number.
s[o.i] = o.x;
} else { // This match is followed by a string, so coallesce twice.
} else { // This match is followed by a string, so coalesce twice.
s[o.i] = o.x + s[o.i + 1];
s.splice(o.i + 1, 1);
}
......@@ -93,6 +93,8 @@ d3.interpolateString = function(a, b) {
};
d3.interpolateTransform = function(a, b) {
if ((n = d3_interpolateTransformSimilar(a, b))) return n;
var s = [], // string constants and placeholders
q = [], // number interpolators
n,
......@@ -144,6 +146,112 @@ d3.interpolateTransform = function(a, b) {
};
};
var d3_interpolateTransformTypes = [
"",
"",
"translate",
"scale",
"rotate",
"skewX",
"skewY"
];
// If both the ‘from’ and ‘to’ transforms have the same number of transform
// functions and corresponding functions in each transform list are of the same
// type, each transform function is animated with its corresponding destination
// function in isolation using the rules described above. The individual values
// are then applied as a list to produce resulting transform value.
var d3_interpolateTransformSimilar = function(a, b) {
var ga = document.createElementNS(d3.ns.prefix.svg, "g"),
gb = document.createElementNS(d3.ns.prefix.svg, "g");
return (d3_interpolateTransformSimilar = function(a, b) {
ga.setAttribute("transform", a);
gb.setAttribute("transform", b);
a = ga.transform.baseVal;
b = gb.transform.baseVal;
var sa = [],
sb = [],
i = -1,
n = a.numberOfItems,
m = b.numberOfItems,
ta,
tb,
type;
// If one of the ‘from’ or ‘to’ transforms is "none", the ‘none’ is replaced
// by an equivalent identity function list for the corresponding transform
// function list. Otherwise, if the transform function lists do not have the
// same number of items, the transforms are each converted into the
// equivalent matrix value and animation proceeds using the rule for a
// single function above.
if (m !== n) {
if (!m) b = d3_interpolateTransformIdentity(a);
else if (!n) a = d3_interpolateTransformIdentity(b), n = m;
else return;
}
// If both the ‘from’ and ‘to’ transforms are "none", there is no
// interpolation necessary.
else if (!m) return;
while (++i < n) {
ta = a.getItem(i);
tb = b.getItem(i);
type = ta.type;
// If the transform functions are not the same type, or the type is
// unknown, fallback to the decomposed transform transition.
if (type !== tb.type || !type) return; // unknown
switch (type) {
// For matrix, the matrix is decomposed using the method described by
// unmatrix into separate translation, scale, rotation and skew
// matrices, then each decomposed matrix is interpolated numerically,
// and finally combined in order to produce a resulting 3x2 matrix.
case 1: { // matrix
sa.push(new d3_transform(ta.matrix));
sb.push(new d3_transform(tb.matrix));
continue;
}
// For translate, scale, rotate and skew functions the individual
// components of the function are interpolated numerically.
case 2: { // translate
ra = ta.matrix.e + "," + ta.matrix.f;
rb = tb.matrix.e + "," + tb.matrix.f;
break;
}
case 3: { // scale
ra = ta.matrix.a + "," + ta.matrix.d;
rb = tb.matrix.a + "," + tb.matrix.d;
break;
}
default: { // rotate, skew
ra = ta.angle;
rb = tb.angle;
}
}
sa.push(type = d3_interpolateTransformTypes[type], "(", ra, ")");
sb.push(type, "(", rb, ")");
}
return d3.interpolateString(sa.join(""), sb.join(""));
})(a, b);
};
function d3_interpolateTransformIdentity(a) {
return {
getItem: function(i) {
return {
type: a.getItem(i).type,
angle: 0,
matrix: d3_transformIdentity
};
}
};
}
d3.interpolateRgb = function(a, b) {
a = d3.rgb(a);
b = d3.rgb(b);
......@@ -213,7 +321,7 @@ d3.interpolateObject = function(a, b) {
for (k in i) c[k] = i[k](t);
return c;
};
}
};
var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;
......
......@@ -41,9 +41,11 @@ function d3_selection_classed(name, value) {
var c = this.className,
cb = c.baseVal != null,
cv = cb ? c.baseVal : c;
cv = d3_collapse(cv.replace(re, " "));
if (cb) c.baseVal = cv;
else this.className = cv;
if (cv) {
cv = d3_collapse(cv.replace(re, " "));
if (cb) c.baseVal = cv;
else this.className = cv;
}
}
function classedFunction() {
......
......@@ -11,8 +11,9 @@ d3_selectionPrototype.on = function(type, listener, capture) {
if (arguments.length < 2) return (i = this.node()[name]) && i._;
// remove the old event listener, and add the new event listener
return this.each(function(d, i) {
return this.each(function() {
var node = this,
args = arguments,
o = node[name];
// remove the old listener, if any (using the previously-set capture)
......@@ -27,12 +28,13 @@ d3_selectionPrototype.on = function(type, listener, capture) {
l._ = listener; // stash the unwrapped listener for get
}
// wrapped event listener that preserves i
// wrapped event listener that propagates data changes
function l(e) {
var o = d3.event; // Events can be reentrant (e.g., focus).
d3.event = e;
args[0] = node.__data__;
try {
listener.call(node, node.__data__, i);
listener.apply(node, args);
} finally {
d3.event = o;
}
......
d3.transform = function(string) {
var g = document.createElementNS(d3.ns.prefix.svg, "g"),
identity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
var g = document.createElementNS(d3.ns.prefix.svg, "g");
return (d3.transform = function(string) {
g.setAttribute("transform", string);
var t = g.transform.baseVal.consolidate();
return new d3_transform(t ? t.matrix : identity);
return new d3_transform(t ? t.matrix : d3_transformIdentity);
})(string);
};
......@@ -57,4 +56,5 @@ function d3_transformCombine(a, b, k) {
return a;
}
var d3_transformDegrees = 180 / Math.PI;
var d3_transformDegrees = 180 / Math.PI,
d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
d3_transitionPrototype.filter = function(filter) {
var subgroups = [],
subgroup,
group,
node;
if (typeof filter !== "function") filter = d3_selection_filter(filter);
for (var j = 0, m = this.length; j < m; j++) {
subgroups.push(subgroup = []);
for (var group = this[j], i = 0, n = group.length; i < n; i++) {
if ((node = group[i]) && filter.call(node.node, node.node.__data__, i)) {
subgroup.push(node);
}
}
}
return d3_transition(subgroups, this.id, this.time).ease(this.ease());
};
......@@ -181,11 +181,11 @@ function d3_layout_packPlace(a, b, c) {
dy = b.y - a.y;
if (db && (dx || dy)) {
var da = b.r + c.r,
dc = Math.sqrt(dx * dx + dy * dy),
cos = Math.max(-1, Math.min(1, (db * db + dc * dc - da * da) / (2 * db * dc))),
theta = Math.acos(cos),
x = cos * (db /= dc),
y = Math.sin(theta) * db;
dc = dx * dx + dy * dy;
da *= da;
db *= db;
var x = .5 + (db - da) / (2 * dc),
y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
c.x = a.x + x * dx + y * dy;
c.y = a.y + x * dy - y * dx;
} else {
......
......@@ -86,6 +86,7 @@ var d3_svg_lineInterpolatorDefault = "linear";
// The various interpolators supported by the `line` class.
var d3_svg_lineInterpolators = d3.map({
"linear": d3_svg_lineLinear,
"linear-closed": d3_svg_lineLinearClosed,
"step-before": d3_svg_lineStepBefore,
"step-after": d3_svg_lineStepAfter,
"basis": d3_svg_lineBasis,
......@@ -100,12 +101,11 @@ var d3_svg_lineInterpolators = d3.map({
// Linear interpolation; generates "L" commands.
function d3_svg_lineLinear(points) {
var i = 0,
n = points.length,
p = points[0],
path = [p[0], ",", p[1]];
while (++i < n) path.push("L", (p = points[i])[0], ",", p[1]);
return path.join("");
return points.join("L");
}
function d3_svg_lineLinearClosed(points) {
return d3_svg_lineLinear(points) + "Z";
}
// Step interpolation; generates "H" and "V" commands.
......
d3.time.day = d3_time_interval(function(date) {
return new d3_time(date.getFullYear(), date.getMonth(), date.getDate());
var day = new d3_time(0, date.getMonth(), date.getDate());
day.setFullYear(date.getFullYear());
return day;
}, function(date, offset) {
date.setDate(date.getDate() + offset);
}, function(date) {
......@@ -11,5 +13,5 @@ d3.time.days.utc = d3.time.day.utc.range;
d3.time.dayOfYear = function(date) {
var year = d3.time.year(date);
return Math.floor((date - year) / 864e5 - (date.getTimezoneOffset() - year.getTimezoneOffset()) / 1440);
return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
};
......@@ -215,11 +215,11 @@ function d3_time_parseFullYear(date, string, i) {
function d3_time_parseYear(date, string, i) {
d3_time_numberRe.lastIndex = 0;
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
return n ? (date.y = d3_time_century() + +n[0], i += n[0].length) : -1;
return n ? (date.y = d3_time_expandYear(+n[0]), i += n[0].length) : -1;
}
function d3_time_century() {
return ~~(new Date().getFullYear() / 1000) * 1000;
function d3_time_expandYear(d) {
return d + (d > 68 ? 1900 : 2000);
}
function d3_time_parseMonthNumber(date, string, i) {
......
d3.time.month = d3_time_interval(function(date) {
return new d3_time(date.getFullYear(), date.getMonth(), 1);
date = d3.time.day(date);
date.setDate(1);
return date;
}, function(date, offset) {
date.setMonth(date.getMonth() + offset);
}, function(date) {
......
d3.time.year = d3_time_interval(function(date) {
return new d3_time(date.getFullYear(), 0, 1);
date = d3.time.day(date);
date.setMonth(0, 1);
return date;
}, function(date, offset) {
date.setFullYear(date.getFullYear() + offset);
}, function(date) {
......
......@@ -5,6 +5,8 @@ var vows = require("vows"),
var suite = vows.describe("d3.bisect");
var i30 = 1 << 30;
suite.addBatch({
"bisectLeft": {
topic: function() {
......@@ -48,6 +50,22 @@ suite.addBatch({
assert.equal(bisect(array, 4, 2, 3), 3);
assert.equal(bisect(array, 5, 2, 3), 3);
assert.equal(bisect(array, 6, 2, 3), 3);
},
"large arrays": function(bisect) {
var array = [],
i = i30;
array[i++] = 1;
array[i++] = 2;
array[i++] = 3;
array[i++] = 4;
array[i++] = 5;
assert.equal(bisect(array, 0, i - 5, i), i - 5);
assert.equal(bisect(array, 1, i - 5, i), i - 5);
assert.equal(bisect(array, 2, i - 5, i), i - 4);
assert.equal(bisect(array, 3, i - 5, i), i - 3);
assert.equal(bisect(array, 4, i - 5, i), i - 2);
assert.equal(bisect(array, 5, i - 5, i), i - 1);
assert.equal(bisect(array, 6, i - 5, i), i - 0);
}
}
});
......@@ -95,6 +113,22 @@ suite.addBatch({
assert.equal(bisect(array, 4, 2, 3), 3);
assert.equal(bisect(array, 5, 2, 3), 3);
assert.equal(bisect(array, 6, 2, 3), 3);
},
"large arrays": function(bisect) {
var array = [],
i = i30;
array[i++] = 1;
array[i++] = 2;
array[i++] = 3;
array[i++] = 4;
array[i++] = 5;
assert.equal(bisect(array, 0, i - 5, i), i - 5);
assert.equal(bisect(array, 1, i - 5, i), i - 4);
assert.equal(bisect(array, 2, i - 5, i), i - 3);
assert.equal(bisect(array, 3, i - 5, i), i - 2);
assert.equal(bisect(array, 4, i - 5, i), i - 1);
assert.equal(bisect(array, 5, i - 5,