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 diff is collapsed.
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 diff is collapsed.
<!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);