1 /**
  2  * Constructs a new mark with default properties. Marks, with the exception of
  3  * the root panel, are not typically constructed directly; instead, they are
  4  * added to a panel or an existing mark via {@link pv.Mark#add}.
  5  *
  6  * @class Represents a data-driven graphical mark. The <tt>Mark</tt> class is
  7  * the base class for all graphical marks in Protovis; it does not provide any
  8  * specific rendering functionality, but together with {@link Panel} establishes
  9  * the core framework.
 10  *
 11  * <p>Concrete mark types include familiar visual elements such as bars, lines
 12  * and labels. Although a bar mark may be used to construct a bar chart, marks
 13  * know nothing about charts; it is only through their specification and
 14  * composition that charts are produced. These building blocks permit many
 15  * combinatorial possibilities.
 16  *
 17  * <p>Marks are associated with <b>data</b>: a mark is generated once per
 18  * associated datum, mapping the datum to visual <b>properties</b> such as
 19  * position and color. Thus, a single mark specification represents a set of
 20  * visual elements that share the same data and visual encoding. The type of
 21  * mark defines the names of properties and their meaning. A property may be
 22  * static, ignoring the associated datum and returning a constant; or, it may be
 23  * dynamic, derived from the associated datum or index. Such dynamic encodings
 24  * can be specified succinctly using anonymous functions. Special properties
 25  * called event handlers can be registered to add interactivity.
 26  *
 27  * <p>Protovis uses <b>inheritance</b> to simplify the specification of related
 28  * marks: a new mark can be derived from an existing mark, inheriting its
 29  * properties. The new mark can then override properties to specify new
 30  * behavior, potentially in terms of the old behavior. In this way, the old mark
 31  * serves as the <b>prototype</b> for the new mark. Most mark types share the
 32  * same basic properties for consistency and to facilitate inheritance.
 33  *
 34  * <p>The prioritization of redundant properties is as follows:<ol>
 35  *
 36  * <li>If the <tt>width</tt> property is not specified (i.e., null), its value
 37  * is the width of the parent panel, minus this mark's left and right margins;
 38  * the left and right margins are zero if not specified.
 39  *
 40  * <li>Otherwise, if the <tt>right</tt> margin is not specified, its value is
 41  * the width of the parent panel, minus this mark's width and left margin; the
 42  * left margin is zero if not specified.
 43  *
 44  * <li>Otherwise, if the <tt>left</tt> property is not specified, its value is
 45  * the width of the parent panel, minus this mark's width and the right margin.
 46  *
 47  * </ol>This prioritization is then duplicated for the <tt>height</tt>,
 48  * <tt>bottom</tt> and <tt>top</tt> properties, respectively.
 49  *
 50  * <p>While most properties are <i>variable</i>, some mark types, such as lines
 51  * and areas, generate a single visual element rather than a distinct visual
 52  * element per datum. With these marks, some properties may be <b>fixed</b>.
 53  * Fixed properties can vary per mark, but not <i>per datum</i>! These
 54  * properties are evaluated solely for the first (0-index) datum, and typically
 55  * are specified as a constant. However, it is valid to use a function if the
 56  * property varies between panels or is dynamically generated.
 57  *
 58  * <p>See also the <a href="../../api/">Protovis guide</a>.
 59  */
 60 pv.Mark = function() {
 61   /*
 62    * TYPE 0 constant defs
 63    * TYPE 1 function defs
 64    * TYPE 2 constant properties
 65    * TYPE 3 function properties
 66    * in order of evaluation!
 67    */
 68   this.$properties = [];
 69   this.$handlers = {};
 70 };
 71 
 72 /** @private Records which properties are defined on this mark type. */
 73 pv.Mark.prototype.properties = {};
 74 
 75 /** @private Records the cast function for each property. */
 76 pv.Mark.cast = {};
 77 
 78 /**
 79  * @private Defines and registers a property method for the property with the
 80  * given name.  This method should be called on a mark class prototype to define
 81  * each exposed property. (Note this refers to the JavaScript
 82  * <tt>prototype</tt>, not the Protovis mark prototype, which is the {@link
 83  * #proto} field.)
 84  *
 85  * <p>The created property method supports several modes of invocation: <ol>
 86  *
 87  * <li>If invoked with a <tt>Function</tt> argument, this function is evaluated
 88  * for each associated datum. The return value of the function is used as the
 89  * computed property value. The context of the function (<tt>this</tt>) is this
 90  * mark. The arguments to the function are the associated data of this mark and
 91  * any enclosing panels. For example, a linear encoding of numerical data to
 92  * height is specified as
 93  *
 94  * <pre>m.height(function(d) d * 100);</pre>
 95  *
 96  * The expression <tt>d * 100</tt> will be evaluated for the height property of
 97  * each mark instance. The return value of the property method (e.g.,
 98  * <tt>m.height</tt>) is this mark (<tt>m</tt>)).<p>
 99  *
100  * <li>If invoked with a non-function argument, the property is treated as a
101  * constant. The return value of the property method (e.g., <tt>m.height</tt>)
102  * is this mark.<p>
103  *
104  * <li>If invoked with no arguments, the computed property value for the current
105  * mark instance in the scene graph is returned. This facilitates <i>property
106  * chaining</i>, where one mark's properties are defined in terms of another's.
107  * For example, to offset a mark's location from its prototype, you might say
108  *
109  * <pre>m.top(function() this.proto.top() + 10);</pre>
110  *
111  * Note that the index of the mark being evaluated (in the above example,
112  * <tt>this.proto</tt>) is inherited from the <tt>Mark</tt> class and set by
113  * this mark. So, if the fifth element's top property is being evaluated, the
114  * fifth instance of <tt>this.proto</tt> will similarly be queried for the value
115  * of its top property. If the mark being evaluated has a different number of
116  * instances, or its data is unrelated, the behavior of this method is
117  * undefined. In these cases it may be better to index the <tt>scene</tt>
118  * explicitly to specify the exact instance.
119  *
120  * </ol><p>Property names should follow standard JavaScript method naming
121  * conventions, using lowerCamel-style capitalization.
122  *
123  * <p>In addition to creating the property method, every property is registered
124  * in the {@link #properties} map on the <tt>prototype</tt>. Although this is an
125  * instance field, it is considered immutable and shared by all instances of a
126  * given mark type. The <tt>properties</tt> map can be queried to see if a mark
127  * type defines a particular property, such as width or height.
128  *
129  * @param {string} name the property name.
130  * @param {function} [cast] the cast function for this property.
131  */
132 pv.Mark.prototype.property = function(name, cast) {
133   if (!this.hasOwnProperty("properties")) {
134     this.properties = pv.extend(this.properties);
135   }
136   this.properties[name] = true;
137 
138   /*
139    * Define the setter-getter globally, since the default behavior should be the
140    * same for all properties, and since the Protovis inheritance chain is
141    * independent of the JavaScript inheritance chain. For example, anchors
142    * define a "name" property that is evaluated on derived marks, even though
143    * those marks don't normally have a name.
144    */
145   pv.Mark.prototype.propertyMethod(name, false, pv.Mark.cast[name] = cast);
146   return this;
147 };
148 
149 /**
150  * @private Defines a setter-getter for the specified property.
151  *
152  * <p>If a cast function has been assigned to the specified property name, the
153  * property function is wrapped by the cast function, or, if a constant is
154  * specified, the constant is immediately cast. Note, however, that if the
155  * property value is null, the cast function is not invoked.
156  *
157  * @param {string} name the property name.
158  * @param {boolean} [def] whether is a property or a def.
159  * @param {function} [cast] the cast function for this property.
160  */
161 pv.Mark.prototype.propertyMethod = function(name, def, cast) {
162   if (!cast) cast = pv.Mark.cast[name];
163   this[name] = function(v) {
164 
165       /* If this is a def, use it rather than property. */
166       if (def && this.scene) {
167         var defs = this.scene.defs;
168         if (arguments.length) {
169           defs[name] = {
170             id: (v == null) ? 0 : pv.id(),
171             value: ((v != null) && cast) ? cast(v) : v
172           };
173           return this;
174         }
175         return defs[name] ? defs[name].value : null;
176       }
177 
178       /* If arguments are specified, set the property value. */
179       if (arguments.length) {
180         var type = !def << 1 | (typeof v == "function");
181         this.propertyValue(name, (type & 1 && cast) ? function() {
182             var x = v.apply(this, arguments);
183             return (x != null) ? cast(x) : null;
184           } : (((v != null) && cast) ? cast(v) : v)).type = type;
185         return this;
186       }
187 
188       return this.instance()[name];
189     };
190 };
191 
192 /** @private Sets the value of the property <i>name</i> to <i>v</i>. */
193 pv.Mark.prototype.propertyValue = function(name, v) {
194   var properties = this.$properties, p = {name: name, id: pv.id(), value: v};
195   for (var i = 0; i < properties.length; i++) {
196     if (properties[i].name == name) {
197       properties.splice(i, 1);
198       break;
199     }
200   }
201   properties.push(p);
202   return p;
203 };
204 
205 /* Define all global properties. */
206 pv.Mark.prototype
207     .property("data")
208     .property("visible", Boolean)
209     .property("left", Number)
210     .property("right", Number)
211     .property("top", Number)
212     .property("bottom", Number)
213     .property("cursor", String)
214     .property("title", String)
215     .property("reverse", Boolean)
216     .property("antialias", Boolean)
217     .property("events", String);
218 
219 /**
220  * The mark type; a lower camelCase name. The type name controls rendering
221  * behavior, and unless the rendering engine is extended, must be one of the
222  * built-in concrete mark types: area, bar, dot, image, label, line, panel,
223  * rule, or wedge.
224  *
225  * @type string
226  * @name pv.Mark.prototype.type
227  */
228 
229 /**
230  * The mark prototype, possibly undefined, from which to inherit property
231  * functions. The mark prototype is not necessarily of the same type as this
232  * mark. Any properties defined on this mark will override properties inherited
233  * either from the prototype or from the type-specific defaults.
234  *
235  * @type pv.Mark
236  * @name pv.Mark.prototype.proto
237  */
238 
239 /**
240  * The enclosing parent panel. The parent panel is generally undefined only for
241  * the root panel; however, it is possible to create "offscreen" marks that are
242  * used only for inheritance purposes.
243  *
244  * @type pv.Panel
245  * @name pv.Mark.prototype.parent
246  */
247 
248 /**
249  * The child index. -1 if the enclosing parent panel is null; otherwise, the
250  * zero-based index of this mark into the parent panel's <tt>children</tt> array.
251  *
252  * @type number
253  */
254 pv.Mark.prototype.childIndex = -1;
255 
256 /**
257  * The mark index. The value of this field depends on which instance (i.e.,
258  * which element of the data array) is currently being evaluated. During the
259  * build phase, the index is incremented over each datum; when handling events,
260  * the index is set to the instance that triggered the event.
261  *
262  * @type number
263  */
264 pv.Mark.prototype.index = -1;
265 
266 /**
267  * The current scale factor, based on any enclosing transforms. The current
268  * scale can be used to create scale-independent graphics. For example, to
269  * define a dot that has a radius of 10 irrespective of any zooming, say:
270  *
271  * <pre>dot.radius(function() 10 / this.scale)</pre>
272  *
273  * Note that the stroke width and font size are defined irrespective of scale
274  * (i.e., in screen space) already. Also note that when a transform is applied
275  * to a panel, the scale affects only the child marks, not the panel itself.
276  *
277  * @type number
278  * @see pv.Panel#transform
279  */
280 pv.Mark.prototype.scale = 1;
281 
282 /**
283  * @private The scene graph. The scene graph is an array of objects; each object
284  * (or "node") corresponds to an instance of this mark and an element in the
285  * data array. The scene graph can be traversed to lookup previously-evaluated
286  * properties.
287  *
288  * @name pv.Mark.prototype.scene
289  */
290 
291 /**
292  * The root parent panel. This may be undefined for "offscreen" marks that are
293  * created for inheritance purposes only.
294  *
295  * @type pv.Panel
296  * @name pv.Mark.prototype.root
297  */
298 
299 /**
300  * The data property; an array of objects. The size of the array determines the
301  * number of marks that will be instantiated; each element in the array will be
302  * passed to property functions to compute the property values. Typically, the
303  * data property is specified as a constant array, such as
304  *
305  * <pre>m.data([1, 2, 3, 4, 5]);</pre>
306  *
307  * However, it is perfectly acceptable to define the data property as a
308  * function. This function might compute the data dynamically, allowing
309  * different data to be used per enclosing panel. For instance, in the stacked
310  * area graph example (see {@link #scene}), the data function on the area mark
311  * dereferences each series.
312  *
313  * @type array
314  * @name pv.Mark.prototype.data
315  */
316 
317 /**
318  * The visible property; a boolean determining whether or not the mark instance
319  * is visible. If a mark instance is not visible, its other properties will not
320  * be evaluated. Similarly, for panels no child marks will be rendered.
321  *
322  * @type boolean
323  * @name pv.Mark.prototype.visible
324  */
325 
326 /**
327  * The left margin; the distance, in pixels, between the left edge of the
328  * enclosing panel and the left edge of this mark. Note that in some cases this
329  * property may be redundant with the right property, or with the conjunction of
330  * right and width.
331  *
332  * @type number
333  * @name pv.Mark.prototype.left
334  */
335 
336 /**
337  * The right margin; the distance, in pixels, between the right edge of the
338  * enclosing panel and the right edge of this mark. Note that in some cases this
339  * property may be redundant with the left property, or with the conjunction of
340  * left and width.
341  *
342  * @type number
343  * @name pv.Mark.prototype.right
344  */
345 
346 /**
347  * The top margin; the distance, in pixels, between the top edge of the
348  * enclosing panel and the top edge of this mark. Note that in some cases this
349  * property may be redundant with the bottom property, or with the conjunction
350  * of bottom and height.
351  *
352  * @type number
353  * @name pv.Mark.prototype.top
354  */
355 
356 /**
357  * The bottom margin; the distance, in pixels, between the bottom edge of the
358  * enclosing panel and the bottom edge of this mark. Note that in some cases
359  * this property may be redundant with the top property, or with the conjunction
360  * of top and height.
361  *
362  * @type number
363  * @name pv.Mark.prototype.bottom
364  */
365 
366 /**
367  * The cursor property; corresponds to the CSS cursor property. This is
368  * typically used in conjunction with event handlers to indicate interactivity.
369  *
370  * @type string
371  * @name pv.Mark.prototype.cursor
372  * @see <a href="http://www.w3.org/TR/CSS2/ui.html#propdef-cursor">CSS2 cursor</a>
373  */
374 
375 /**
376  * The title property; corresponds to the HTML/SVG title property, allowing the
377  * general of simple plain text tooltips.
378  *
379  * @type string
380  * @name pv.Mark.prototype.title
381  */
382 
383 /**
384  * The events property; corresponds to the SVG pointer-events property,
385  * specifying how the mark should participate in mouse events. The default value
386  * is "painted". Supported values are:
387  *
388  * <p>"painted": The given mark may receive events when the mouse is over a
389  * "painted" area. The painted areas are the interior (i.e., fill) of the mark
390  * if a 'fillStyle' is specified, and the perimeter (i.e., stroke) of the mark
391  * if a 'strokeStyle' is specified.
392  *
393  * <p>"all": The given mark may receive events when the mouse is over either the
394  * interior (i.e., fill) or the perimeter (i.e., stroke) of the mark, regardless
395  * of the specified fillStyle and strokeStyle.
396  *
397  * <p>"none": The given mark may not receive events.
398  *
399  * @type string
400  * @name pv.Mark.prototype.events
401  */
402 
403 /**
404  * The reverse property; a boolean determining whether marks are ordered from
405  * front-to-back or back-to-front. SVG does not support explicit z-ordering;
406  * shapes are rendered in the order they appear. Thus, by default, marks are
407  * rendered in data order. Setting the reverse property to false reverses the
408  * order in which they are rendered; however, the properties are still evaluated
409  * (i.e., built) in forward order.
410  *
411  * @type boolean
412  * @name pv.Mark.prototype.reverse
413  */
414 
415 /**
416  * Default properties for all mark types. By default, the data array is the
417  * parent data as a single-element array; if the data property is not specified,
418  * this causes each mark to be instantiated as a singleton with the parents
419  * datum. The visible property is true by default, and the reverse property is
420  * false.
421  *
422  * @type pv.Mark
423  */
424 pv.Mark.prototype.defaults = new pv.Mark()
425     .data(function(d) { return [d]; })
426     .visible(true)
427     .antialias(true)
428     .events("painted");
429 
430 /**
431  * Sets the prototype of this mark to the specified mark. Any properties not
432  * defined on this mark may be inherited from the specified prototype mark, or
433  * its prototype, and so on. The prototype mark need not be the same type of
434  * mark as this mark. (Note that for inheritance to be useful, properties with
435  * the same name on different mark types should have equivalent meaning.)
436  *
437  * @param {pv.Mark} proto the new prototype.
438  * @returns {pv.Mark} this mark.
439  * @see #add
440  */
441 pv.Mark.prototype.extend = function(proto) {
442   this.proto = proto;
443   return this;
444 };
445 
446 /**
447  * Adds a new mark of the specified type to the enclosing parent panel, whilst
448  * simultaneously setting the prototype of the new mark to be this mark.
449  *
450  * @param {function} type the type of mark to add; a constructor, such as
451  * <tt>pv.Bar</tt>.
452  * @returns {pv.Mark} the new mark.
453  * @see #extend
454  */
455 pv.Mark.prototype.add = function(type) {
456   return this.parent.add(type).extend(this);
457 };
458 
459 /**
460  * Defines a custom property on this mark. Custom properties are currently
461  * fixed, in that they are initialized once per mark set (i.e., per parent panel
462  * instance). Custom properties can be used to store local state for the mark,
463  * such as data needed by other properties (e.g., a custom scale) or interaction
464  * state.
465  *
466  * <p>WARNING We plan on changing this feature in a future release to define
467  * standard properties, as opposed to <i>fixed</i> properties that behave
468  * idiosyncratically within event handlers. Furthermore, we recommend storing
469  * state in an external data structure, rather than tying it to the
470  * visualization specification as with defs.
471  *
472  * @param {string} name the name of the local variable.
473  * @param {function} [v] an optional initializer; may be a constant or a
474  * function.
475  */
476 pv.Mark.prototype.def = function(name, v) {
477   this.propertyMethod(name, true);
478   return this[name](arguments.length > 1 ? v : null);
479 };
480 
481 /**
482  * Returns an anchor with the specified name. All marks support the five
483  * standard anchor names:<ul>
484  *
485  * <li>top
486  * <li>left
487  * <li>center
488  * <li>bottom
489  * <li>right
490  *
491  * </ul>In addition to positioning properties (left, right, top bottom), the
492  * anchors support text rendering properties (text-align, text-baseline). Text is
493  * rendered to appear inside the mark by default.
494  *
495  * <p>To facilitate stacking, anchors are defined in terms of their opposite
496  * edge. For example, the top anchor defines the bottom property, such that the
497  * mark extends upwards; the bottom anchor instead defines the top property,
498  * such that the mark extends downwards. See also {@link pv.Layout.Stack}.
499  *
500  * <p>While anchor names are typically constants, the anchor name is a true
501  * property, which means you can specify a function to compute the anchor name
502  * dynamically. See the {@link pv.Anchor#name} property for details.
503  *
504  * @param {string} name the anchor name; either a string or a property function.
505  * @returns {pv.Anchor} the new anchor.
506  */
507 pv.Mark.prototype.anchor = function(name) {
508   var target = this, scene;
509 
510   /* Default anchor name. */
511   if (!name) name = "center";
512 
513   /** @private Find the instances of target that match source. */
514   function instances(source) {
515     var mark = target, index = [];
516 
517     /* Mirrored descent. */
518     while (!(scene = mark.scene)) {
519       source = source.parent;
520       index.push({index: source.index, childIndex: mark.childIndex});
521       mark = mark.parent;
522     }
523     while (index.length) {
524       var i = index.pop();
525       scene = scene[i.index].children[i.childIndex];
526     }
527 
528     /*
529      * When the anchor target is also an ancestor, as in the case of adding
530      * to a panel anchor, only generate one instance per panel. Also, set
531      * the margins to zero, since they are offset by the enclosing panel.
532      */
533     if (target.hasOwnProperty("index")) {
534       var s = pv.extend(scene[target.index]);
535       s.right = s.top = s.left = s.bottom = 0;
536       return [s];
537     }
538     return scene;
539   }
540 
541   return new pv.Anchor(this)
542     .name(name)
543     .def("$mark.anchor", function() {
544         scene = this.scene.target = instances(this);
545       })
546     .data(function() {
547         return scene.map(function(s) { return s.data; });
548       })
549     .visible(function() {
550         return scene[this.index].visible;
551       })
552     .left(function() {
553         var s = scene[this.index], w = s.width || 0;
554         switch (this.name()) {
555           case "bottom":
556           case "top":
557           case "center": return s.left + w / 2;
558           case "left": return null;
559         }
560         return s.left + w;
561       })
562     .top(function() {
563         var s = scene[this.index], h = s.height || 0;
564         switch (this.name()) {
565           case "left":
566           case "right":
567           case "center": return s.top + h / 2;
568           case "top": return null;
569         }
570         return s.top + h;
571       })
572     .right(function() {
573         var s = scene[this.index];
574         return this.name() == "left" ? s.right + (s.width || 0) : null;
575       })
576     .bottom(function() {
577         var s = scene[this.index];
578         return this.name() == "top" ? s.bottom + (s.height || 0) : null;
579       })
580     .textAlign(function() {
581         switch (this.name()) {
582           case "bottom":
583           case "top":
584           case "center": return "center";
585           case "right": return "right";
586         }
587         return "left";
588       })
589     .textBaseline(function() {
590         switch (this.name()) {
591           case "right":
592           case "left":
593           case "center": return "middle";
594           case "top": return "top";
595         }
596         return "bottom";
597       });
598 };
599 
600 /**
601  * Returns the anchor target of this mark, if it is derived from an anchor;
602  * otherwise returns null. For example, if a label is derived from a bar anchor,
603  *
604  * <pre>bar.anchor("top").add(pv.Label);</pre>
605  *
606  * then property functions on the label can refer to the bar via the
607  * <tt>anchorTarget</tt> method. This method is also useful for mark types
608  * defining properties on custom anchors.
609  *
610  * @returns {pv.Mark} the anchor target of this mark; possibly null.
611  */
612 pv.Mark.prototype.anchorTarget = function() {
613   return this.proto.anchorTarget();
614 };
615 
616 /**
617  * Alias for setting the left, right, top and bottom properties simultaneously.
618  *
619  * @see #left
620  * @see #right
621  * @see #top
622  * @see #bottom
623  * @returns {pv.Mark} this.
624  */
625 pv.Mark.prototype.margin = function(n) {
626   return this.left(n).right(n).top(n).bottom(n);
627 };
628 
629 /**
630  * @private Returns the current instance of this mark in the scene graph. This
631  * is typically equivalent to <tt>this.scene[this.index]</tt>, however if the
632  * scene or index is unset, the default instance of the mark is returned. If no
633  * default is set, the default is the last instance. Similarly, if the scene or
634  * index of the parent panel is unset, the default instance of this mark in the
635  * last instance of the enclosing panel is returned, and so on.
636  *
637  * @returns a node in the scene graph.
638  */
639 pv.Mark.prototype.instance = function(defaultIndex) {
640   var scene = this.scene || this.parent.instance(-1).children[this.childIndex],
641       index = !arguments.length || this.hasOwnProperty("index") ? this.index : defaultIndex;
642   return scene[index < 0 ? scene.length - 1 : index];
643 };
644 
645 /**
646  * @private Returns the first instance of this mark in the scene graph. This
647  * method can only be called when the mark is bound to the scene graph (for
648  * example, from an event handler, or within a property function).
649  *
650  * @returns a node in the scene graph.
651  */
652 pv.Mark.prototype.first = function() {
653   return this.scene[0];
654 };
655 
656 /**
657  * @private Returns the last instance of this mark in the scene graph. This
658  * method can only be called when the mark is bound to the scene graph (for
659  * example, from an event handler, or within a property function). In addition,
660  * note that mark instances are built sequentially, so the last instance of this
661  * mark may not yet be constructed.
662  *
663  * @returns a node in the scene graph.
664  */
665 pv.Mark.prototype.last = function() {
666   return this.scene[this.scene.length - 1];
667 };
668 
669 /**
670  * @private Returns the previous instance of this mark in the scene graph, or
671  * null if this is the first instance.
672  *
673  * @returns a node in the scene graph, or null.
674  */
675 pv.Mark.prototype.sibling = function() {
676   return (this.index == 0) ? null : this.scene[this.index - 1];
677 };
678 
679 /**
680  * @private Returns the current instance in the scene graph of this mark, in the
681  * previous instance of the enclosing parent panel. May return null if this
682  * instance could not be found.
683  *
684  * @returns a node in the scene graph, or null.
685  */
686 pv.Mark.prototype.cousin = function() {
687   var p = this.parent, s = p && p.sibling();
688   return (s && s.children) ? s.children[this.childIndex][this.index] : null;
689 };
690 
691 /**
692  * Renders this mark, including recursively rendering all child marks if this is
693  * a panel. This method finds all instances of this mark and renders them. This
694  * method descends recursively to the level of the mark to be rendered, finding
695  * all visible instances of the mark. After the marks are rendered, the scene
696  * and index attributes are removed from the mark to restore them to a clean
697  * state.
698  *
699  * <p>If an enclosing panel has an index property set (as is the case inside in
700  * an event handler), then only instances of this mark inside the given instance
701  * of the panel will be rendered; otherwise, all visible instances of the mark
702  * will be rendered.
703  */
704 pv.Mark.prototype.render = function() {
705   var parent = this.parent,
706       stack = pv.Mark.stack;
707 
708   /* For the first render, take it from the top. */
709   if (parent && !this.root.scene) {
710     this.root.render();
711     return;
712   }
713 
714   /* Record the path to this mark. */
715   var indexes = [];
716   for (var mark = this; mark.parent; mark = mark.parent) {
717     indexes.unshift(mark.childIndex);
718   }
719 
720   /** @private */
721   function render(mark, depth, scale) {
722     mark.scale = scale;
723     if (depth < indexes.length) {
724       stack.unshift(null);
725       if (mark.hasOwnProperty("index")) {
726         renderInstance(mark, depth, scale);
727       } else {
728         for (var i = 0, n = mark.scene.length; i < n; i++) {
729           mark.index = i;
730           renderInstance(mark, depth, scale);
731         }
732         delete mark.index;
733       }
734       stack.shift();
735     } else {
736       mark.build();
737 
738       /*
739        * In the update phase, the scene is rendered by creating and updating
740        * elements and attributes in the SVG image. No properties are evaluated
741        * during the update phase; instead the values computed previously in the
742        * build phase are simply translated into SVG. The update phase is
743        * decoupled (see pv.Scene) to allow different rendering engines.
744        */
745       pv.Scene.scale = scale;
746       pv.Scene.updateAll(mark.scene);
747     }
748     delete mark.scale;
749   }
750 
751   /**
752    * @private Recursively renders the current instance of the specified mark.
753    * This is slightly tricky because `index` and `scene` properties may or may
754    * not already be set; if they are set, it means we are rendering only a
755    * specific instance; if they are unset, we are rendering all instances.
756    * Furthermore, we must preserve the original context of these properties when
757    * rendering completes.
758    *
759    * <p>Another tricky aspect is that the `scene` attribute should be set for
760    * any preceding children, so as to allow property chaining. This is
761    * consistent with first-pass rendering.
762    */
763   function renderInstance(mark, depth, scale) {
764     var s = mark.scene[mark.index], i;
765     if (s.visible) {
766       var childIndex = indexes[depth],
767           child = mark.children[childIndex];
768 
769       /* Set preceding child scenes. */
770       for (i = 0; i < childIndex; i++) {
771         mark.children[i].scene = s.children[i];
772       }
773 
774       /* Set current child scene, if necessary. */
775       stack[0] = s.data;
776       if (child.scene) {
777         render(child, depth + 1, scale * s.transform.k);
778       } else {
779         child.scene = s.children[childIndex];
780         render(child, depth + 1, scale * s.transform.k);
781         delete child.scene;
782       }
783 
784       /* Clear preceding child scenes. */
785       for (i = 0; i < childIndex; i++) {
786         delete mark.children[i].scene;
787       }
788     }
789   }
790 
791   /* Bind this mark's property definitions. */
792   this.bind();
793 
794   /* The render context is the first ancestor with an explicit index. */
795   while (parent && !parent.hasOwnProperty("index")) parent = parent.parent;
796 
797   /* Recursively render all instances of this mark. */
798   this.context(
799       parent ? parent.scene : undefined,
800       parent ? parent.index : -1,
801       function() { render(this.root, 0, 1); });
802 };
803 
804 /** @private Stores the current data stack. */
805 pv.Mark.stack = [];
806 
807 /**
808  * @private In the bind phase, inherited property definitions are cached so they
809  * do not need to be queried during build.
810  */
811 pv.Mark.prototype.bind = function() {
812   var seen = {}, types = [[], [], [], []], data, visible;
813 
814   /** Scans the proto chain for the specified mark. */
815   function bind(mark) {
816     do {
817       var properties = mark.$properties;
818       for (var i = properties.length - 1; i >= 0 ; i--) {
819         var p = properties[i];
820         if (!(p.name in seen)) {
821           seen[p.name] = p;
822           switch (p.name) {
823             case "data": data = p; break;
824             case "visible": visible = p; break;
825             default: types[p.type].push(p); break;
826           }
827         }
828       }
829     } while (mark = mark.proto);
830   }
831 
832   /* Scan the proto chain for all defined properties. */
833   bind(this);
834   bind(this.defaults);
835   types[1].reverse();
836   types[3].reverse();
837 
838   /* Any undefined properties are null. */
839   var mark = this;
840   do for (var name in mark.properties) {
841     if (!(name in seen)) {
842       types[2].push(seen[name] = {name: name, type: 2, value: null});
843     }
844   } while (mark = mark.proto);
845 
846   /* Define setter-getter for inherited defs. */
847   var defs = types[0].concat(types[1]);
848   for (var i = 0; i < defs.length; i++) {
849     this.propertyMethod(defs[i].name, true);
850   }
851 
852   /* Setup binds to evaluate constants before functions. */
853   this.binds = {
854     properties: seen,
855     data: data,
856     defs: defs,
857     required: [visible],
858     optional: pv.blend(types)
859   };
860 };
861 
862 /**
863  * @private Evaluates properties and computes implied properties. Properties are
864  * stored in the {@link #scene} array for each instance of this mark.
865  *
866  * <p>As marks are built recursively, the {@link #index} property is updated to
867  * match the current index into the data array for each mark. Note that the
868  * index property is only set for the mark currently being built and its
869  * enclosing parent panels. The index property for other marks is unset, but is
870  * inherited from the global <tt>Mark</tt> class prototype. This allows mark
871  * properties to refer to properties on other marks <i>in the same panel</i>
872  * conveniently; however, in general it is better to reference mark instances
873  * specifically through the scene graph rather than depending on the magical
874  * behavior of {@link #index}.
875  *
876  * <p>The root scene array has a special property, <tt>data</tt>, which stores
877  * the current data stack. The first element in this stack is the current datum,
878  * followed by the datum of the enclosing parent panel, and so on. The data
879  * stack should not be accessed directly; instead, property functions are passed
880  * the current data stack as arguments.
881  *
882  * <p>The evaluation of the <tt>data</tt> and <tt>visible</tt> properties is
883  * special. The <tt>data</tt> property is evaluated first; unlike the other
884  * properties, the data stack is from the parent panel, rather than the current
885  * mark, since the data is not defined until the data property is evaluated.
886  * The <tt>visisble</tt> property is subsequently evaluated for each instance;
887  * only if true will the {@link #buildInstance} method be called, evaluating
888  * other properties and recursively building the scene graph.
889  *
890  * <p>If this mark is being re-built, any old instances of this mark that no
891  * longer exist (because the new data array contains fewer elements) will be
892  * cleared using {@link #clearInstance}.
893  *
894  * @param parent the instance of the parent panel from the scene graph.
895  */
896 pv.Mark.prototype.build = function() {
897   var scene = this.scene, stack = pv.Mark.stack;
898   if (!scene) {
899     scene = this.scene = [];
900     scene.mark = this;
901     scene.type = this.type;
902     scene.childIndex = this.childIndex;
903     if (this.parent) {
904       scene.parent = this.parent.scene;
905       scene.parentIndex = this.parent.index;
906     }
907   }
908 
909   /* Evaluate defs. */
910   if (this.binds.defs.length) {
911     var defs = scene.defs;
912     if (!defs) scene.defs = defs = {};
913     for (var i = 0; i < this.binds.defs.length; i++) {
914       var p = this.binds.defs[i], d = defs[p.name];
915       if (!d || (p.id > d.id)) {
916         defs[p.name] = {
917           id: 0, // this def will be re-evaluated on next build
918           value: (p.type & 1) ? p.value.apply(this, stack) : p.value
919         };
920       }
921     }
922   }
923 
924   /* Evaluate special data property. */
925   var data = this.binds.data;
926   data = data.type & 1 ? data.value.apply(this, stack) : data.value;
927 
928   /* Create, update and delete scene nodes. */
929   stack.unshift(null);
930   scene.length = data.length;
931   for (var i = 0; i < data.length; i++) {
932     pv.Mark.prototype.index = this.index = i;
933     var s = scene[i];
934     if (!s) scene[i] = s = {};
935     s.data = stack[0] = data[i];
936     this.buildInstance(s);
937   }
938   pv.Mark.prototype.index = -1;
939   delete this.index;
940   stack.shift();
941 
942   return this;
943 };
944 
945 /**
946  * @private Evaluates the specified array of properties for the specified
947  * instance <tt>s</tt> in the scene graph.
948  *
949  * @param s a node in the scene graph; the instance of the mark to build.
950  * @param properties an array of properties.
951  */
952 pv.Mark.prototype.buildProperties = function(s, properties) {
953   for (var i = 0, n = properties.length; i < n; i++) {
954     var p = properties[i], v = p.value; // assume case 2 (constant)
955     switch (p.type) {
956       case 0:
957       case 1: v = this.scene.defs[p.name].value; break;
958       case 3: v = v.apply(this, pv.Mark.stack); break;
959     }
960     s[p.name] = v;
961   }
962 };
963 
964 /**
965  * @private Evaluates all of the properties for this mark for the specified
966  * instance <tt>s</tt> in the scene graph. The set of properties to evaluate is
967  * retrieved from the {@link #properties} array for this mark type (see {@link
968  * #type}).  After these properties are evaluated, any <b>implied</b> properties
969  * may be computed by the mark and set on the scene graph; see
970  * {@link #buildImplied}.
971  *
972  * <p>For panels, this method recursively builds the scene graph for all child
973  * marks as well. In general, this method should not need to be overridden by
974  * concrete mark types.
975  *
976  * @param s a node in the scene graph; the instance of the mark to build.
977  */
978 pv.Mark.prototype.buildInstance = function(s) {
979   this.buildProperties(s, this.binds.required);
980   if (s.visible) {
981     this.buildProperties(s, this.binds.optional);
982     this.buildImplied(s);
983   }
984 };
985 
986 /**
987  * @private Computes the implied properties for this mark for the specified
988  * instance <tt>s</tt> in the scene graph. Implied properties are those with
989  * dependencies on multiple other properties; for example, the width property
990  * may be implied if the left and right properties are set. This method can be
991  * overridden by concrete mark types to define new implied properties, if
992  * necessary.
993  *
994  * @param s a node in the scene graph; the instance of the mark to build.
995  */
996 pv.Mark.prototype.buildImplied = function(s) {
997   var l = s.left;
998   var r = s.right;
999   var t = s.top;
1000   var b = s.bottom;
1001 
1002   /* Assume width and height are zero if not supported by this mark type. */
1003   var p = this.properties;
1004   var w = p.width ? s.width : 0;
1005   var h = p.height ? s.height : 0;
1006 
1007   /* Compute implied width, right and left. */
1008   var width = this.parent ? this.parent.width() : (w + l + r);
1009   if (w == null) {
1010     w = width - (r = r || 0) - (l = l || 0);
1011   } else if (r == null) {
1012     r = width - w - (l = l || 0);
1013   } else if (l == null) {
1014     l = width - w - (r = r || 0);
1015   }
1016 
1017   /* Compute implied height, bottom and top. */
1018   var height = this.parent ? this.parent.height() : (h + t + b);
1019   if (h == null) {
1020     h = height - (t = t || 0) - (b = b || 0);
1021   } else if (b == null) {
1022     b = height - h - (t = t || 0);
1023   } else if (t == null) {
1024     t = height - h - (b = b || 0);
1025   }
1026 
1027   s.left = l;
1028   s.right = r;
1029   s.top = t;
1030   s.bottom = b;
1031 
1032   /* Only set width and height if they are supported by this mark type. */
1033   if (p.width) s.width = w;
1034   if (p.height) s.height = h;
1035 
1036   /* Set any null colors to pv.Color.transparent. */
1037   if (p.textStyle && !s.textStyle) s.textStyle = pv.Color.transparent;
1038   if (p.fillStyle && !s.fillStyle) s.fillStyle = pv.Color.transparent;
1039   if (p.strokeStyle && !s.strokeStyle) s.strokeStyle = pv.Color.transparent;
1040 };
1041 
1042 /**
1043  * Returns the current location of the mouse (cursor) relative to this mark's
1044  * parent. The <i>x</i> coordinate corresponds to the left margin, while the
1045  * <i>y</i> coordinate corresponds to the top margin.
1046  *
1047  * @returns {pv.Vector} the mouse location.
1048  */
1049 pv.Mark.prototype.mouse = function() {
1050 
1051   /* Compute xy-coordinates relative to the panel. */
1052   var x = pv.event.pageX || 0,
1053       y = pv.event.pageY || 0,
1054       n = this.root.canvas();
1055   do {
1056     x -= n.offsetLeft;
1057     y -= n.offsetTop;
1058   } while (n = n.offsetParent);
1059 
1060   /* Compute the inverse transform of all enclosing panels. */
1061   var t = pv.Transform.identity,
1062       p = this.properties.transform ? this : this.parent,
1063       pz = [];
1064   do { pz.push(p); } while (p = p.parent);
1065   while (p = pz.pop()) t = t.translate(p.left(), p.top()).times(p.transform());
1066   t = t.invert();
1067 
1068   return pv.vector(x * t.k + t.x, y * t.k + t.y);
1069 };
1070 
1071 /**
1072  * Registers an event handler for the specified event type with this mark. When
1073  * an event of the specified type is triggered, the specified handler will be
1074  * invoked. The handler is invoked in a similar method to property functions:
1075  * the context is <tt>this</tt> mark instance, and the arguments are the full
1076  * data stack. Event handlers can use property methods to manipulate the display
1077  * properties of the mark:
1078  *
1079  * <pre>m.event("click", function() this.fillStyle("red"));</pre>
1080  *
1081  * Alternatively, the external data can be manipulated and the visualization
1082  * redrawn:
1083  *
1084  * <pre>m.event("click", function(d) {
1085  *     data = all.filter(function(k) k.name == d);
1086  *     vis.render();
1087  *   });</pre>
1088  *
1089  * The return value of the event handler determines which mark gets re-rendered.
1090  * Use defs ({@link #def}) to set temporary state from event handlers.
1091  *
1092  * <p>The complete set of event types is defined by SVG; see the reference
1093  * below. The set of supported event types is:<ul>
1094  *
1095  * <li>click
1096  * <li>mousedown
1097  * <li>mouseup
1098  * <li>mouseover
1099  * <li>mousemove
1100  * <li>mouseout
1101  *
1102  * </ul>Since Protovis does not specify any concept of focus, it does not
1103  * support key events; these should be handled outside the visualization using
1104  * standard JavaScript. In the future, support for interaction may be extended
1105  * to support additional event types, particularly those most relevant to
1106  * interactive visualization, such as selection.
1107  *
1108  * <p>TODO In the current implementation, event handlers are not inherited from
1109  * prototype marks. They must be defined explicitly on each interactive mark. In
1110  * addition, only one event handler for a given event type can be defined; when
1111  * specifying multiple event handlers for the same type, only the last one will
1112  * be used.
1113  *
1114  * @see <a href="http://www.w3.org/TR/SVGTiny12/interact.html#SVGEvents">SVG events</a>
1115  * @param {string} type the event type.
1116  * @param {function} handler the event handler.
1117  * @returns {pv.Mark} this.
1118  */
1119 pv.Mark.prototype.event = function(type, handler) {
1120   this.$handlers[type] = pv.functor(handler);
1121   return this;
1122 };
1123 
1124 /** @private Evaluates the function <i>f</i> with the specified context. */
1125 pv.Mark.prototype.context = function(scene, index, f) {
1126   var proto = pv.Mark.prototype,
1127       stack = pv.Mark.stack,
1128       oscene = pv.Mark.scene,
1129       oindex = proto.index;
1130 
1131   /** @private Sets the context. */
1132   function apply(scene, index) {
1133     pv.Mark.scene = scene;
1134     proto.index = index;
1135     if (!scene) return;
1136 
1137     var that = scene.mark,
1138         mark = that,
1139         ancestors = [];
1140 
1141     /* Set ancestors' scene and index; populate data stack. */
1142     do {
1143       ancestors.push(mark);
1144       stack.push(scene[index].data);
1145       mark.index = index;
1146       mark.scene = scene;
1147       index = scene.parentIndex;
1148       scene = scene.parent;
1149     } while (mark = mark.parent);
1150 
1151     /* Set ancestors' scale; requires top-down. */
1152     for (var i = ancestors.length - 1, k = 1; i > 0; i--) {
1153       mark = ancestors[i];
1154       mark.scale = k;
1155       k *= mark.scene[mark.index].transform.k;
1156     }
1157 
1158     /* Set children's scene and scale. */
1159     if (that.children) for (var i = 0, n = that.children.length; i < n; i++) {
1160       mark = that.children[i];
1161       mark.scene = that.scene[that.index].children[i];
1162       mark.scale = k;
1163     }
1164   }
1165 
1166   /** @private Clears the context. */
1167   function clear(scene, index) {
1168     if (!scene) return;
1169     var that = scene.mark,
1170         mark;
1171 
1172     /* Reset children. */
1173     if (that.children) for (var i = 0, n = that.children.length; i < n; i++) {
1174       mark = that.children[i];
1175       delete mark.scene;
1176       delete mark.scale;
1177     }
1178 
1179     /* Reset ancestors. */
1180     mark = that;
1181     do {
1182       stack.pop();
1183       if (mark.parent) {
1184         delete mark.scene;
1185         delete mark.scale;
1186       }
1187       delete mark.index;
1188     } while (mark = mark.parent);
1189   }
1190 
1191   /* Context switch, invoke the function, then switch back. */
1192   clear(oscene, oindex);
1193   apply(scene, index);
1194   try {
1195     f.apply(this, stack);
1196   } finally {
1197     clear(scene, index);
1198     apply(oscene, oindex);
1199   }
1200 };
1201 
1202 /** @private Execute the event listener, then re-render. */
1203 pv.Mark.dispatch = function(type, scene, index) {
1204   var m = scene.mark, p = scene.parent, l = m.$handlers[type];
1205   if (!l) return p && pv.Mark.dispatch(type, p, scene.parentIndex);
1206   m.context(scene, index, function() {
1207       m = l.apply(m, pv.Mark.stack);
1208       if (m && m.render) m.render();
1209     });
1210   return true;
1211 };
1212