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