Permalink
Browse files

Use d3.map for d3_svg_symbols.

This fixes a crash with the symbol type "hasOwnProperty", rather than defaulting
to "circle". This commit also adds new map methods to retrieve the keys, values
and entries. The map class now uses non-enumerable properties (if supported).
  • Loading branch information...
1 parent 7069da0 commit f1197ed8b6b4893237cdd3e27e7454af3c97a612 @mbostock mbostock committed Feb 21, 2012
Showing with 127 additions and 61 deletions.
  1. +1 −0 Makefile
  2. +44 −15 d3.v2.js
  3. +4 −4 d3.v2.min.js
  4. +12 −0 src/core/class.js
  5. +18 −3 src/core/map.js
  6. +14 −12 src/svg/symbol.js
  7. +33 −27 test/core/map-test.js
  8. +1 −0 test/svg/symbol-test.js
View
@@ -28,6 +28,7 @@ d3.core.js: \
src/compat/date.js \
src/compat/style.js \
src/core/core.js \
+ src/core/class.js \
src/core/array.js \
src/core/map.js \
src/core/this.js \
View
@@ -11,6 +11,18 @@ try {
};
}
d3 = {version: "2.7.5"}; // semver
+function d3_class(ctor, properties) {
+ try {
+ for (var key in properties) {
+ Object.defineProperty(ctor.prototype, key, {
+ value: properties[key],
+ enumerable: false
+ });
+ }
+ } catch (e) {
+ ctor.prototype = properties;
+ }
+}
var d3_array = d3_arraySlice; // conversion for NodeLists
function d3_arrayCopy(pseudoarray) {
@@ -48,7 +60,7 @@ d3.map = function(object) {
function d3_Map() {}
-d3_Map.prototype = {
+d3_class(d3_Map, {
has: function(key) {
return d3_map_prefix + key in this;
},
@@ -58,18 +70,33 @@ d3_Map.prototype = {
set: function(key, value) {
this[d3_map_prefix + key] = value;
},
- delete: function(key) {
+ "delete": function(key) {
key = d3_map_prefix + key;
return key in this && delete this[key];
},
+ keys: function() {
+ var keys = [];
+ this.forEach(function(key) { keys.push(key); });
+ return keys;
+ },
+ values: function() {
+ var values = [];
+ this.forEach(function(key, value) { values.push(value); });
+ return values;
+ },
+ entries: function() {
+ var entries = [];
+ this.forEach(function(key, value) { entries.push({key: key, value: value}); });
+ return entries;
+ },
forEach: function(f) {
for (var key in this) {
if (key.charCodeAt(0) === d3_map_prefixCode) {
f.call(this, key.substring(1), this[key]);
}
}
}
-};
+});
var d3_map_prefix = "\0", // prevent collision with built-ins
d3_map_prefixCode = d3_map_prefix.charCodeAt(0);
@@ -3831,8 +3858,8 @@ d3.svg.symbol = function() {
size = d3_svg_symbolSize;
function symbol(d, i) {
- return (d3_svg_symbols[type.call(this, d, i)]
- || d3_svg_symbols.circle)
+ return (d3_svg_symbols.get(type.call(this, d, i))
+ || d3_svg_symbolCircle)
(size.call(this, d, i));
}
@@ -3860,15 +3887,17 @@ function d3_svg_symbolType() {
return "circle";
}
+function d3_svg_symbolCircle(size) {
+ var r = Math.sqrt(size / Math.PI);
+ return "M0," + r
+ + "A" + r + "," + r + " 0 1,1 0," + (-r)
+ + "A" + r + "," + r + " 0 1,1 0," + r
+ + "Z";
+}
+
// TODO cross-diagonal?
-var d3_svg_symbols = {
- "circle": function(size) {
- var r = Math.sqrt(size / Math.PI);
- return "M0," + r
- + "A" + r + "," + r + " 0 1,1 0," + (-r)
- + "A" + r + "," + r + " 0 1,1 0," + r
- + "Z";
- },
+var d3_svg_symbols = d3.map({
+ "circle": d3_svg_symbolCircle,
"cross": function(size) {
var r = Math.sqrt(size / 5) / 2;
return "M" + -3 * r + "," + -r
@@ -3918,9 +3947,9 @@ var d3_svg_symbols = {
+ " " + -rx + "," + ry
+ "Z";
}
-};
+});
-d3.svg.symbolTypes = d3.keys(d3_svg_symbols);
+d3.svg.symbolTypes = d3_svg_symbols.keys();
var d3_svg_symbolSqrt3 = Math.sqrt(3),
d3_svg_symbolTan30 = Math.tan(30 * Math.PI / 180);
View
Oops, something went wrong.
View
@@ -0,0 +1,12 @@
+function d3_class(ctor, properties) {
+ try {
+ for (var key in properties) {
+ Object.defineProperty(ctor.prototype, key, {
+ value: properties[key],
+ enumerable: false
+ });
+ }
+ } catch (e) {
+ ctor.prototype = properties;
+ }
+}
View
@@ -6,7 +6,7 @@ d3.map = function(object) {
function d3_Map() {}
-d3_Map.prototype = {
+d3_class(d3_Map, {
has: function(key) {
return d3_map_prefix + key in this;
},
@@ -16,18 +16,33 @@ d3_Map.prototype = {
set: function(key, value) {
this[d3_map_prefix + key] = value;
},
- delete: function(key) {
+ "delete": function(key) {
key = d3_map_prefix + key;
return key in this && delete this[key];
},
+ keys: function() {
+ var keys = [];
+ this.forEach(function(key) { keys.push(key); });
+ return keys;
+ },
+ values: function() {
+ var values = [];
+ this.forEach(function(key, value) { values.push(value); });
+ return values;
+ },
+ entries: function() {
+ var entries = [];
+ this.forEach(function(key, value) { entries.push({key: key, value: value}); });
+ return entries;
+ },
forEach: function(f) {
for (var key in this) {
if (key.charCodeAt(0) === d3_map_prefixCode) {
f.call(this, key.substring(1), this[key]);
}
}
}
-};
+});
var d3_map_prefix = "\0", // prevent collision with built-ins
d3_map_prefixCode = d3_map_prefix.charCodeAt(0);
View
@@ -3,8 +3,8 @@ d3.svg.symbol = function() {
size = d3_svg_symbolSize;
function symbol(d, i) {
- return (d3_svg_symbols[type.call(this, d, i)]
- || d3_svg_symbols.circle)
+ return (d3_svg_symbols.get(type.call(this, d, i))
+ || d3_svg_symbolCircle)
(size.call(this, d, i));
}
@@ -32,15 +32,17 @@ function d3_svg_symbolType() {
return "circle";
}
+function d3_svg_symbolCircle(size) {
+ var r = Math.sqrt(size / Math.PI);
+ return "M0," + r
+ + "A" + r + "," + r + " 0 1,1 0," + (-r)
+ + "A" + r + "," + r + " 0 1,1 0," + r
+ + "Z";
+}
+
// TODO cross-diagonal?
-var d3_svg_symbols = {
- "circle": function(size) {
- var r = Math.sqrt(size / Math.PI);
- return "M0," + r
- + "A" + r + "," + r + " 0 1,1 0," + (-r)
- + "A" + r + "," + r + " 0 1,1 0," + r
- + "Z";
- },
+var d3_svg_symbols = d3.map({
+ "circle": d3_svg_symbolCircle,
"cross": function(size) {
var r = Math.sqrt(size / 5) / 2;
return "M" + -3 * r + "," + -r
@@ -90,9 +92,9 @@ var d3_svg_symbols = {
+ " " + -rx + "," + ry
+ "Z";
}
-};
+});
-d3.svg.symbolTypes = d3.keys(d3_svg_symbols);
+d3.svg.symbolTypes = d3_svg_symbols.keys();
var d3_svg_symbolSqrt3 = Math.sqrt(3),
d3_svg_symbolTan30 = Math.tan(30 * Math.PI / 180);
View
@@ -9,11 +9,11 @@ suite.addBatch({
"constructor": {
"map() returns an empty map": function() {
var map = d3.map();
- assert.deepEqual(keys(map), []);
+ assert.deepEqual(map.keys(), []);
},
"map(null) returns an empty map": function() {
var map = d3.map(null);
- assert.deepEqual(keys(map), []);
+ assert.deepEqual(map.keys(), []);
},
"map(object) copies enumerable keys": function() {
var map = d3.map({foo: 42});
@@ -45,33 +45,51 @@ suite.addBatch({
"forEach": {
"empty maps have an empty keys array": function() {
var map = d3.map();
- assert.deepEqual(entries(map), []);
+ assert.deepEqual(map.entries(), []);
map.set("foo", "bar");
- assert.deepEqual(entries(map), [{key: "foo", value: "bar"}]);
+ assert.deepEqual(map.entries(), [{key: "foo", value: "bar"}]);
map.delete("foo");
- assert.deepEqual(entries(map), []);
+ assert.deepEqual(map.entries(), []);
},
"keys are returned in arbitrary order": function() {
var map = d3.map({foo: 1, bar: "42"});
- assert.deepEqual(entries(map).sort(ascendingByKey), [{key: "bar", value: "42"}, {key: "foo", value: 1}]);
+ assert.deepEqual(map.entries().sort(ascendingByKey), [{key: "bar", value: "42"}, {key: "foo", value: 1}]);
var map = d3.map({bar: "42", foo: 1});
- assert.deepEqual(entries(map).sort(ascendingByKey), [{key: "bar", value: "42"}, {key: "foo", value: 1}]);
+ assert.deepEqual(map.entries().sort(ascendingByKey), [{key: "bar", value: "42"}, {key: "foo", value: 1}]);
},
"observes changes via set and delete": function() {
var map = d3.map({foo: 1, bar: "42"});
- assert.deepEqual(entries(map).sort(ascendingByKey), [{key: "bar", value: "42"}, {key: "foo", value: 1}]);
+ assert.deepEqual(map.entries().sort(ascendingByKey), [{key: "bar", value: "42"}, {key: "foo", value: 1}]);
map.delete("foo");
- assert.deepEqual(entries(map), [{key: "bar", value: "42"}]);
+ assert.deepEqual(map.entries(), [{key: "bar", value: "42"}]);
map.set("bar", "bar");
- assert.deepEqual(entries(map), [{key: "bar", value: "bar"}]);
+ assert.deepEqual(map.entries(), [{key: "bar", value: "bar"}]);
map.set("foo", "foo");
- assert.deepEqual(entries(map).sort(ascendingByKey), [{key: "bar", value: "bar"}, {key: "foo", value: "foo"}]);
+ assert.deepEqual(map.entries().sort(ascendingByKey), [{key: "bar", value: "bar"}, {key: "foo", value: "foo"}]);
map.delete("bar");
- assert.deepEqual(entries(map), [{key: "foo", value: "foo"}]);
+ assert.deepEqual(map.entries(), [{key: "foo", value: "foo"}]);
map.delete("foo");
- assert.deepEqual(entries(map), []);
+ assert.deepEqual(map.entries(), []);
map.delete("foo");
- assert.deepEqual(entries(map), []);
+ assert.deepEqual(map.entries(), []);
+ }
+ },
+ "keys": {
+ "returns an array of string keys": function() {
+ var map = d3.map({foo: 1, bar: "42"});
+ assert.deepEqual(map.keys().sort(), ["bar", "foo"]);
+ }
+ },
+ "values": {
+ "returns an array of arbitrary values": function() {
+ var map = d3.map({foo: 1, bar: "42"});
+ assert.deepEqual(map.values().sort(), [1, "42"]);
+ }
+ },
+ "entries": {
+ "returns an array of key-value objects": function() {
+ var map = d3.map({foo: 1, bar: "42"});
+ assert.deepEqual(map.entries().sort(ascendingByKey), [{key: "bar", value: "42"}, {key: "foo", value: 1}]);
}
},
"has": {
@@ -165,7 +183,7 @@ suite.addBatch({
assert.equal(map.get(null), 2);
map.set(undefined, 3);
assert.equal(map.get(undefined), 3);
- assert.deepEqual(keys(map).sort(), ["42", "null", "undefined"]);
+ assert.deepEqual(map.keys().sort(), ["42", "null", "undefined"]);
},
"can replace values": function() {
var map = d3.map({foo: 42});
@@ -187,18 +205,6 @@ suite.addBatch({
}
});
-function keys(map) {
- var keys = [];
- map.forEach(function(key) { keys.push(key); });
- return keys;
-}
-
-function entries(map) {
- var entries = [];
- map.forEach(function(key, value) { entries.push({key: key, value: value}); });
- return entries;
-}
-
function ascendingByKey(a, b) {
return d3.ascending(a.key, b.key);
}
@@ -56,6 +56,7 @@ suite.addBatch({
var a = symbol().type(String);
assert.pathEqual(a(), "M0,4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,-4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,4.51351666838205Z");
assert.pathEqual(a("invalid"), "M0,4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,-4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,4.51351666838205Z");
+ assert.pathEqual(a("hasOwnProperty"), "M0,4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,-4.51351666838205A4.51351666838205,4.51351666838205 0 1,1 0,4.51351666838205Z");
},
"can specify type accessor as a function": function(symbol) {
var a = symbol().type(String);

0 comments on commit f1197ed

Please sign in to comment.