/** * @private * @namespace */ pv.Scene = pv.SvgScene = { /* Various namespaces. */ svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns", xlink: "http://www.w3.org/1999/xlink", xhtml: "http://www.w3.org/1999/xhtml", /** The pre-multipled scale, based on any enclosing transforms. */ scale: 1, /** The set of supported events. */ events: [ "DOMMouseScroll", // for Firefox "mousewheel", "mousedown", "mouseup", "mouseover", "mouseout", "mousemove", "click", "dblclick" ], /** Implicit values for SVG and CSS properties. */ implicit: { svg: { "shape-rendering": "auto", "pointer-events": "painted", "x": 0, "y": 0, "dy": 0, "text-anchor": "start", "transform": "translate(0,0)", "fill": "none", "fill-opacity": 1, "stroke": "none", "stroke-opacity": 1, "stroke-width": 1.5, "stroke-linejoin": "miter" }, css: { "font": "10px sans-serif" } } }; /** * Updates the display for the specified array of scene nodes. * * @param scenes {array} an array of scene nodes. */ pv.SvgScene.updateAll = function(scenes) { if (scenes.length && scenes[0].reverse && (scenes.type != "line") && (scenes.type != "area")) { var reversed = pv.extend(scenes); for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) { reversed[i] = scenes[j]; } scenes = reversed; } this.removeSiblings(this[scenes.type](scenes)); }; /** * Creates a new SVG element of the specified type. * * @param type {string} an SVG element type, such as "rect". * @returns a new SVG element. */ pv.SvgScene.create = function(type) { return document.createElementNS(this.svg, type); }; /** * Expects the element e to be the specified type. If the element does * not exist, a new one is created. If the element does exist but is the wrong * type, it is replaced with the specified element. * * @param e the current SVG element. * @param type {string} an SVG element type, such as "rect". * @param attributes an optional attribute map. * @param style an optional style map. * @returns a new SVG element. */ pv.SvgScene.expect = function(e, type, attributes, style) { if (e) { if (e.tagName == "a") e = e.firstChild; if (e.tagName != type) { var n = this.create(type); e.parentNode.replaceChild(n, e); e = n; } } else { e = this.create(type); } for (var name in attributes) { var value = attributes[name]; if (value == this.implicit.svg[name]) value = null; if (value == null) e.removeAttribute(name); else e.setAttribute(name, value); } for (var name in style) { var value = style[name]; if (value == this.implicit.css[name]) value = null; if (value == null) e.style.removeProperty(name); else e.style[name] = value; } return e; }; /** TODO */ pv.SvgScene.append = function(e, scenes, index) { e.$scene = {scenes:scenes, index:index}; e = this.title(e, scenes[index]); if (!e.parentNode) scenes.$g.appendChild(e); return e.nextSibling; }; /** * Applies a title tooltip to the specified element e, using the * title property of the specified scene node s. Note that * this implementation does not create an SVG title element as a child * of e; although this is the recommended standard, it is only * supported in Opera. Instead, an anchor element is created around the element * e, and the xlink:title attribute is set accordingly. * * @param e an SVG element. * @param s a scene node. */ pv.SvgScene.title = function(e, s) { var a = e.parentNode; if (a && (a.tagName != "a")) a = null; if (s.title) { if (!a) { a = this.create("a"); if (e.parentNode) e.parentNode.replaceChild(a, e); a.appendChild(e); } a.setAttributeNS(this.xlink, "title", s.title); return a; } if (a) a.parentNode.replaceChild(e, a); return e; }; /** TODO */ pv.SvgScene.dispatch = pv.listener(function(e) { var t = e.target.$scene; if (t) { var type = e.type; /* Fixes for mousewheel support on Firefox & Opera. */ switch (type) { case "DOMMouseScroll": { type = "mousewheel"; e.wheel = -480 * e.detail; break; } case "mousewheel": { e.wheel = (window.opera ? 12 : 1) * e.wheelDelta; break; } } if (pv.Mark.dispatch(type, t.scenes, t.index)) e.preventDefault(); } }); /** @private Remove siblings following element e. */ pv.SvgScene.removeSiblings = function(e) { while (e) { var n = e.nextSibling; e.parentNode.removeChild(e); e = n; } }; /** @private Do nothing when rendering undefined mark types. */ pv.SvgScene.undefined = function() {};