forked from mbostock/protovis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pv-internals.js
168 lines (157 loc) · 5.02 KB
/
pv-internals.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
* @private Returns a prototype object suitable for extending the given class
* <tt>f</tt>. Rather than constructing a new instance of <tt>f</tt> to serve as
* the prototype (which unnecessarily runs the constructor on the created
* prototype object, potentially polluting it), an anonymous function is
* generated internally that shares the same prototype:
*
* <pre>function g() {}
* g.prototype = f.prototype;
* return new g();</pre>
*
* For more details, see Douglas Crockford's essay on prototypal inheritance.
*
* @param {function} f a constructor.
* @returns a suitable prototype object.
* @see Douglas Crockford's essay on <a
* href="http://javascript.crockford.com/prototypal.html">prototypal
* inheritance</a>.
*/
pv.extend = Object.create
? function ( f ) {
return Object.create( f.prototype || f );
}
: function ( f ) {
function g() {}
g.prototype = f.prototype || f;
return new g();
};
try {
eval("pv.parse = function(x) x;"); // native support
} catch (e) {
/**
* @private Parses a Protovis specification, which may use JavaScript 1.8
* function expresses, replacing those function expressions with proper
* functions such that the code can be run by a JavaScript 1.6 interpreter. This
* hack only supports function expressions (using clumsy regular expressions, no
* less), and not other JavaScript 1.8 features such as let expressions.
*
* @param {string} s a Protovis specification (i.e., a string of JavaScript 1.8
* source code).
* @returns {string} a conformant JavaScript 1.6 source code.
*/
pv.parse = function(js) { // hacky regex support
var re = new RegExp("function\\s*(\\b\\w+)?\\s*\\([^)]*\\)\\s*", "mg"), m, d, i = 0, s = "";
while ( (m = re.exec(js)) ) {
var j = m.index + m[0].length;
if (js.charAt(j) != '{') {
s += js.substring(i, j) + "{return ";
i = j;
for (var p = 0; p >= 0 && j < js.length; j++) {
var c = js.charAt(j);
switch (c) {
case '"': case '\'':
while (++j < js.length && (d = js.charAt(j)) != c) {
if (d == '\\') j++;
}
break;
case '[': case '(': p++; break;
case ']': case ')': p--; break;
case ';':
case ',': if (p === 0) p--; break;
}
}
s += pv.parse(js.substring(i, --j)) + ";}";
i = j;
}
re.lastIndex = j;
}
s += js.substring(i);
return s;
};
}
/**
* @private Computes the value of the specified CSS property <tt>p</tt> on the
* specified element <tt>e</tt>.
*
* @param {string} p the name of the CSS property.
* @param e the element on which to compute the CSS property.
*/
pv.css = function(e, p) {
return window.getComputedStyle
? window.getComputedStyle(e, null).getPropertyValue(p)
: e.currentStyle[p];
};
/**
* @private Reports the specified error to the JavaScript console. Mozilla only
* allows logging to the console for privileged code; if the console is
* unavailable, the alert dialog box is used instead.
*
* @param e the exception that triggered the error.
*/
pv.error = function(e) {
if (typeof console == "undefined") {
alert(e);
}
else {
console.error(e);
}
};
/**
* @private Registers the specified listener for events of the specified type on
* the specified target. For standards-compliant browsers, this method uses
* <tt>addEventListener</tt>; for Internet Explorer, <tt>attachEvent</tt>.
*
* @param target a DOM element.
* @param {string} type the type of event, such as "click".
* @param {function} the event handler callback.
*/
pv.listen = function(target, type, listener) {
listener = pv.listener(listener);
return target.addEventListener
? target.addEventListener(type, listener, false)
: target.attachEvent("on" + type, listener);
};
/**
* @private Returns a wrapper for the specified listener function such that the
* {@link pv.event} is set for the duration of the listener's invocation. The
* wrapper is cached on the returned function, such that duplicate registrations
* of the wrapped event handler are ignored.
*
* @param {function} f an event handler.
* @returns {function} the wrapped event handler.
*/
pv.listener = function(f) {
return f.$listener || (f.$listener = function(e) {
try {
pv.event = e;
return f.call(this, e);
}
catch (e) {
pv.error(e);
}
finally {
delete pv.event;
}
});
};
/**
* @private Returns true iff <i>a</i> is an ancestor of <i>e</i>. This is useful
* for ignoring mouseout and mouseover events that are contained within the
* target element.
*/
pv.ancestor = function(a, e) {
while (e) {
if (e == a) return true;
e = e.parentNode;
}
return false;
};
/** @private Returns a locally-unique positive id. */
pv.id = function() {
var id = 1; return function() { return id++; };
}();
/** @private Returns a function wrapping the specified constant. */
pv.functor = function(v) {
return typeof v == "function" ? v : function() { return v; };
};