1 /** 2 * @private 3 * @namespace 4 */ 5 pv.Scene = pv.SvgScene = { 6 /* Various namespaces. */ 7 svg: "http://www.w3.org/2000/svg", 8 xmlns: "http://www.w3.org/2000/xmlns", 9 xlink: "http://www.w3.org/1999/xlink", 10 xhtml: "http://www.w3.org/1999/xhtml", 11 12 /** The pre-multipled scale, based on any enclosing transforms. */ 13 scale: 1, 14 15 /** The set of supported events. */ 16 events: [ 17 "DOMMouseScroll", // for Firefox 18 "mousewheel", 19 "mousedown", 20 "mouseup", 21 "mouseover", 22 "mouseout", 23 "mousemove", 24 "click", 25 "dblclick" 26 ], 27 28 /** Implicit values for SVG and CSS properties. */ 29 implicit: { 30 svg: { 31 "shape-rendering": "auto", 32 "pointer-events": "painted", 33 "x": 0, 34 "y": 0, 35 "dy": 0, 36 "text-anchor": "start", 37 "transform": "translate(0,0)", 38 "fill": "none", 39 "fill-opacity": 1, 40 "stroke": "none", 41 "stroke-opacity": 1, 42 "stroke-width": 1.5, 43 "stroke-linejoin": "miter" 44 }, 45 css: { 46 "font": "10px sans-serif" 47 } 48 } 49 }; 50 51 /** 52 * Updates the display for the specified array of scene nodes. 53 * 54 * @param scenes {array} an array of scene nodes. 55 */ 56 pv.SvgScene.updateAll = function(scenes) { 57 if (scenes.length 58 && scenes[0].reverse 59 && (scenes.type != "line") 60 && (scenes.type != "area")) { 61 var reversed = pv.extend(scenes); 62 for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) { 63 reversed[i] = scenes[j]; 64 } 65 scenes = reversed; 66 } 67 this.removeSiblings(this[scenes.type](scenes)); 68 }; 69 70 /** 71 * Creates a new SVG element of the specified type. 72 * 73 * @param type {string} an SVG element type, such as "rect". 74 * @returns a new SVG element. 75 */ 76 pv.SvgScene.create = function(type) { 77 return document.createElementNS(this.svg, type); 78 }; 79 80 /** 81 * Expects the element <i>e</i> to be the specified type. If the element does 82 * not exist, a new one is created. If the element does exist but is the wrong 83 * type, it is replaced with the specified element. 84 * 85 * @param e the current SVG element. 86 * @param type {string} an SVG element type, such as "rect". 87 * @param attributes an optional attribute map. 88 * @param style an optional style map. 89 * @returns a new SVG element. 90 */ 91 pv.SvgScene.expect = function(e, type, attributes, style) { 92 if (e) { 93 if (e.tagName == "a") e = e.firstChild; 94 if (e.tagName != type) { 95 var n = this.create(type); 96 e.parentNode.replaceChild(n, e); 97 e = n; 98 } 99 } else { 100 e = this.create(type); 101 } 102 for (var name in attributes) { 103 var value = attributes[name]; 104 if (value == this.implicit.svg[name]) value = null; 105 if (value == null) e.removeAttribute(name); 106 else e.setAttribute(name, value); 107 } 108 for (var name in style) { 109 var value = style[name]; 110 if (value == this.implicit.css[name]) value = null; 111 if (value == null) e.style.removeProperty(name); 112 else e.style[name] = value; 113 } 114 return e; 115 }; 116 117 /** TODO */ 118 pv.SvgScene.append = function(e, scenes, index) { 119 e.$scene = {scenes:scenes, index:index}; 120 e = this.title(e, scenes[index]); 121 if (!e.parentNode) scenes.$g.appendChild(e); 122 return e.nextSibling; 123 }; 124 125 /** 126 * Applies a title tooltip to the specified element <tt>e</tt>, using the 127 * <tt>title</tt> property of the specified scene node <tt>s</tt>. Note that 128 * this implementation does not create an SVG <tt>title</tt> element as a child 129 * of <tt>e</tt>; although this is the recommended standard, it is only 130 * supported in Opera. Instead, an anchor element is created around the element 131 * <tt>e</tt>, and the <tt>xlink:title</tt> attribute is set accordingly. 132 * 133 * @param e an SVG element. 134 * @param s a scene node. 135 */ 136 pv.SvgScene.title = function(e, s) { 137 var a = e.parentNode; 138 if (a && (a.tagName != "a")) a = null; 139 if (s.title) { 140 if (!a) { 141 a = this.create("a"); 142 if (e.parentNode) e.parentNode.replaceChild(a, e); 143 a.appendChild(e); 144 } 145 a.setAttributeNS(this.xlink, "title", s.title); 146 return a; 147 } 148 if (a) a.parentNode.replaceChild(e, a); 149 return e; 150 }; 151 152 /** TODO */ 153 pv.SvgScene.dispatch = pv.listener(function(e) { 154 var t = e.target.$scene; 155 if (t) { 156 var type = e.type; 157 158 /* Fixes for mousewheel support on Firefox & Opera. */ 159 switch (type) { 160 case "DOMMouseScroll": { 161 type = "mousewheel"; 162 e.wheel = -480 * e.detail; 163 break; 164 } 165 case "mousewheel": { 166 e.wheel = (window.opera ? 12 : 1) * e.wheelDelta; 167 break; 168 } 169 } 170 171 if (pv.Mark.dispatch(type, t.scenes, t.index)) e.preventDefault(); 172 } 173 }); 174 175 /** @private Remove siblings following element <i>e</i>. */ 176 pv.SvgScene.removeSiblings = function(e) { 177 while (e) { 178 var n = e.nextSibling; 179 e.parentNode.removeChild(e); 180 e = n; 181 } 182 }; 183 184 /** @private Do nothing when rendering undefined mark types. */ 185 pv.SvgScene.undefined = function() {}; 186