1 // TODO don't populate default attributes?
  2 
  3 /**
  4  * @private
  5  * @namespace
  6  */
  7 pv.Scene = pv.SvgScene = {};
  8 
  9 /**
 10  * Updates the display for the specified array of scene nodes.
 11  *
 12  * @param scenes {array} an array of scene nodes.
 13  */
 14 pv.SvgScene.updateAll = function(scenes) {
 15   if (!scenes.length) return;
 16   if ((scenes[0].reverse)
 17       && (scenes.type != "line")
 18       && (scenes.type != "area")) {
 19     var reversed = pv.extend(scenes);
 20     for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) {
 21       reversed[i] = scenes[j];
 22     }
 23     scenes = reversed;
 24   }
 25   this[scenes.type](scenes);
 26 };
 27 
 28 /**
 29  * Creates a new SVG element of the specified type.
 30  *
 31  * @param type {string} an SVG element type, such as "rect".
 32  * @return a new SVG element.
 33  */
 34 pv.SvgScene.create = function(type) {
 35   return document.createElementNS(pv.ns.svg, type);
 36 };
 37 
 38 /**
 39  * Applies a title tooltip to the specified element <tt>e</tt>, using the
 40  * <tt>title</tt> property of the specified scene node <tt>s</tt>. Note that
 41  * this implementation does not create an SVG <tt>title</tt> element as a child
 42  * of <tt>e</tt>; although this is the recommended standard, it is only
 43  * supported in Opera. Instead, an anchor element is created around the element
 44  * <tt>e</tt>, and the <tt>xlink:title</tt> attribute is set accordingly.
 45  *
 46  * @param e an SVG element.
 47  * @param s a scene node.
 48  */
 49 pv.SvgScene.title = function(e, s) {
 50   var a = e.parentNode;
 51   if (a && (a.tagName != "a")) a = null;
 52   if (s.title) {
 53     if (!a) {
 54       a = this.create("a");
 55       if (e.parentNode) e.parentNode.replaceChild(a, e);
 56       a.appendChild(e);
 57     }
 58     a.setAttributeNS(pv.ns.xlink, "title", s.title);
 59   } else if (a) {
 60     a.removeAttributeNS(pv.ns.xlink, "title");
 61   } else {
 62     a = e;
 63   }
 64   return a;
 65 };
 66 
 67 /** TODO */
 68 pv.SvgScene.parentNode = function(scenes) {
 69   return scenes.parent[scenes.parentIndex].scene.g;
 70 };
 71 
 72 /** TODO */
 73 pv.SvgScene.cache = function(s, type, name) {
 74   if (!s.scene) return (s.scene = {})[name] = this.create(type);
 75   var e = s.scene[name];
 76   if (e) {
 77     while (e.lastChild) e.removeChild(e.lastChild);
 78     return e;
 79   }
 80   return s.scene[name] = this.create(type);
 81 };
 82 
 83 /** TODO */
 84 pv.SvgScene.group = function(scenes) {
 85   var g = this.cache(scenes, "g", "g");
 86   if (!g.parentNode) this.parentNode(scenes).appendChild(g);
 87   return g;
 88 };
 89 
 90 /** TODO */
 91 pv.SvgScene.listen = function(e, scenes, index) {
 92   e.$scene = {scenes:scenes, index:index};
 93 };
 94 
 95 var pv_SvgScene_mouseover;
 96 
 97 /** TODO */
 98 pv.SvgScene.dispatch = function(e) {
 99   var t;
100 
101   /*
102    * Firefox doesn't track the mouseout target very well, so here we do some
103    * bookkeeping to ensure that when a mouseover event triggers, the previous
104    * mouseover target gets a mouseout event.
105    */
106   if (pv_SvgScene_mouseover) {
107     t = pv_SvgScene_mouseover;
108     if (e.type == "mouseover") {
109       t.scenes.mark.dispatch("mouseout", t.scenes, t.index);
110       t = e.target.$scene;
111     } else if (e.type == "mouseout") {
112       pv_SvgScene_mouseover = null;
113     }
114   } else {
115     t = e.target.$scene;
116   }
117 
118   if (t) {
119     if (e.type == "mouseover") pv_SvgScene_mouseover = t;
120     t.scenes.mark.dispatch(e.type, t.scenes, t.index);
121     e.preventDefault();
122   }
123 };
124