|  | var svgCache; | 
|  | var svgDefs; | 
|  | var svgGradients; | 
|  | var svgNS = "http://www.w3.org/2000/svg"; | 
|  | var svgRoot; | 
|  |  | 
|  | function displaySvg(displayList) { | 
|  | for (var index = 0; index < displayList.length; ++index) { | 
|  | drawToSvg(displayList[index]); | 
|  | } | 
|  | } | 
|  |  | 
|  | function drawToSvg(display) { | 
|  | assert('string' == typeof(display.ref)); | 
|  | var cache; | 
|  | if (display.ref in svgCache) { | 
|  | cache = svgCache[display.ref]; | 
|  | if (display.drawDirty) { | 
|  | switch (cache.spec) { | 
|  | case "paths": | 
|  | svgSetPathData(cache.element, display.draw); | 
|  | break; | 
|  | case "pictures": | 
|  | svgSetPictureData(cache.element, display.draw); | 
|  | break; | 
|  | case "text": | 
|  | svgCreateText(cache.element, display.draw); | 
|  | break; | 
|  | default: | 
|  | assert(0); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | cache = {}; | 
|  | cache.action = display; | 
|  | cache.spec = display.drawSpec; | 
|  | var dot = cache.spec.indexOf("."); | 
|  | if (dot > 0) { | 
|  | cache.spec = cache.spec.substring(0, dot); | 
|  | } | 
|  | switch (cache.spec) { | 
|  | case "paths": | 
|  | cache.element = svgCreatePath(display.ref, display.draw); | 
|  | break; | 
|  | case "pictures": | 
|  | cache.element = svgCreatePicture(display.ref, display.draw); | 
|  | break; | 
|  | case "text": | 
|  | cache.element = svgCreateText(display.ref, display.draw); | 
|  | break; | 
|  | default: | 
|  | assert(0); | 
|  | } | 
|  | } | 
|  | display.drawDirty = false; | 
|  | if (display.paintDirty) { | 
|  | svgSetPaintData(cache.element, display.paint); | 
|  | var opacity = svg_opacity(display.paint.color); | 
|  | cache.element.setAttribute("fill-opacity", opacity); | 
|  | cache.element.setAttribute("stroke-opacity", opacity); | 
|  | display.paintDirty = false; | 
|  | } | 
|  | assert('object' == typeof(cache)); | 
|  | if (!(display.ref in svgCache)) { | 
|  | svgRoot.appendChild(cache.element); | 
|  | svgCache[display.ref] = cache; | 
|  | } | 
|  | } | 
|  |  | 
|  | function setupSvg() { | 
|  | svgCache = { "paths":{}, "pictures":{}, "text":{} }; | 
|  | svgDefs = document.createElementNS(svgNS, "defs"); | 
|  | svgGradients = {}; | 
|  | svgRoot = document.getElementById("svg"); | 
|  | while (svgRoot.lastChild) { | 
|  | svgRoot.removeChild(svgRoot.lastChild); | 
|  | } | 
|  | svgRoot.appendChild(svgDefs); | 
|  | } | 
|  |  | 
|  | function svg_rbg(color) { | 
|  | return "rgb(" + ((color >> 16) & 0xFF) | 
|  | + "," + ((color >>  8) & 0xFF) | 
|  | + "," + ((color >>  0) & 0xFF) + ")"; | 
|  | } | 
|  |  | 
|  | function svg_opacity(color) { | 
|  | return ((color >> 24) & 0xFF) / 255.0; | 
|  | } | 
|  |  | 
|  | function svgCreatePath(key, path) { | 
|  | var svgPath = document.createElementNS(svgNS, "path"); | 
|  | svgPath.setAttribute("id", key); | 
|  | svgSetPathData(svgPath, path); | 
|  | return svgPath; | 
|  | } | 
|  |  | 
|  | function svgCreatePicture(key, picture) { | 
|  | var svgPicture = document.createElementNS(svgNS, "g"); | 
|  | svgPicture.setAttribute("id", key); | 
|  | svgSetPictureData(svgPicture, picture); | 
|  | return svgPicture; | 
|  | } | 
|  |  | 
|  | function svgCreateRadialGradient(key) { | 
|  | var g = gradients[key]; | 
|  | var e = document.createElementNS(svgNS, "radialGradient"); | 
|  | e.setAttribute("id", key); | 
|  | e.setAttribute("cx", g.cx); | 
|  | e.setAttribute("cy", g.cy); | 
|  | e.setAttribute("r", g.r); | 
|  | e.setAttribute("gradientUnits", "userSpaceOnUse"); | 
|  | var stopLen = g.stops.length; | 
|  | for (var index = 0; index < stopLen; ++index) { | 
|  | var stop = g.stops[index]; | 
|  | var color = svg_rbg(stop.color); | 
|  | var s = document.createElementNS(svgNS, 'stop'); | 
|  | s.setAttribute("offset", stop.offset); | 
|  | var style = "stop-color:" + svg_rbg(stop.color) + "; stop-opacity:" | 
|  | + svg_opacity(stop.color); | 
|  | s.setAttribute("style", style); | 
|  | e.appendChild(s); | 
|  | } | 
|  | svgGradients[key] = e; | 
|  | svgDefs.appendChild(e); | 
|  | } | 
|  |  | 
|  | function svgCreateText(key, text) { | 
|  | var svgText = document.createElementNS(svgNS, "text"); | 
|  | svgText.setAttribute("id", key); | 
|  | var textNode = document.createTextNode(text.string); | 
|  | svgText.appendChild(textNode); | 
|  | svgSetTextData(svgText, text); | 
|  | return svgText; | 
|  | } | 
|  |  | 
|  | function svgSetPathData(svgPath, path) { | 
|  | var dString = ""; | 
|  | for (var cIndex = 0; cIndex < path.length; ++cIndex) { | 
|  | var curveKey = Object.keys(path[cIndex])[0]; | 
|  | var v = path[cIndex][curveKey]; | 
|  | switch (curveKey) { | 
|  | case 'arcTo': | 
|  | var clockwise = 1; // to do; work in general case | 
|  | dString += " A" + v[4] + "," + v[4] + " 0 0," + clockwise + " " | 
|  | + v[2] + "," + v[3]; | 
|  | break; | 
|  | case 'close': | 
|  | dString += " z"; | 
|  | break; | 
|  | case 'cubic': | 
|  | dString += " M" + v[0] + "," + v[1]; | 
|  | dString += " C" + v[2] + "," + v[3] | 
|  | + " " + v[4] + "," + v[5] | 
|  | + " " + v[6] + "," + v[7]; | 
|  | break; | 
|  | case 'line': | 
|  | dString += " M" + v[0] + "," + v[1]; | 
|  | dString += " L" + v[2] + "," + v[3]; | 
|  | break; | 
|  | case 'quad': | 
|  | dString += " M" + v[0] + "," + v[1]; | 
|  | dString += " Q" + v[2] + "," + v[3] | 
|  | + " " + v[4] + "," + v[5]; | 
|  | break; | 
|  | default: | 
|  | assert(0); | 
|  | } | 
|  | } | 
|  | svgPath.setAttribute("d", dString); | 
|  | } | 
|  |  | 
|  | function svgSetPaintData(svgElement, paint) { | 
|  | var color; | 
|  | var inPicture = 'string' == typeof(paint); | 
|  | if (inPicture) { | 
|  | paint = (new Function("return " + paint))(); | 
|  | assert('object' == typeof(paint) && !isArray(paint)); | 
|  | } | 
|  | if ('gradient' in paint) { | 
|  | var gradient = paint.gradient.split('.'); | 
|  | var gradName = gradient[1]; | 
|  | if (!svgGradients[gradName]) { | 
|  | svgCreateRadialGradient(gradName); | 
|  | } | 
|  | color = "url(#" + gradName + ")"; | 
|  | } else { | 
|  | color = svg_rbg(paint.color); | 
|  | } | 
|  | svgElement.setAttribute("fill", 'fill' == paint.style ? color : "none"); | 
|  | if ('stroke' == paint.style) { | 
|  | svgElement.setAttribute("stroke", color); | 
|  | } | 
|  | if ('strokeWidth' in paint) { | 
|  | svgElement.setAttribute("stroke-width", paint.strokeWidth); | 
|  | } | 
|  | if ('typeface' in paint) { | 
|  | var typeface = typefaces[paint.typeface]; | 
|  | var font = typeface.style; | 
|  | if ('textSize' in paint) { | 
|  | svgElement.setAttribute("font-size", paint.textSize); | 
|  | } | 
|  | if ('family' in typeface) { | 
|  | svgElement.setAttribute("font-family", typeface.family); | 
|  | } | 
|  | if ('textAlign' in paint) { | 
|  | svgElement.setAttribute("text-anchor", paint.textAlign == "right" ? "end" : assert(0)); | 
|  | } | 
|  | if ('textBaseline' in paint) { | 
|  | svgElement.setAttribute("alignment-baseline", paint.textBaseline); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | function svgSetPictureData(svgPicture, picture) { | 
|  | while (svgPicture.lastChild) { | 
|  | svgPicture.removeChild(svgPicture.lastChild); | 
|  | } | 
|  | for (var index = 0; index < picture.length; ++index) { | 
|  | var entry = picture[index]; | 
|  | var drawObj = (new Function("return " + entry.draw))(); | 
|  | var drawSpec = entry.draw.split('.'); | 
|  | var svgElement; | 
|  | switch (drawSpec[0]) { | 
|  | case 'paths': | 
|  | svgElement = svgCreatePath(drawSpec[1], drawObj); | 
|  | break; | 
|  | case 'pictures': | 
|  | svgElement = svgCreatePicture(drawSpec[1], drawObj); | 
|  | break; | 
|  | case 'text': | 
|  | svgElement = svgCreateText(drawSpec[1], drawObj); | 
|  | break; | 
|  | default: | 
|  | assert(0); | 
|  | } | 
|  | var paintObj = (new Function("return " + entry.paint))(); | 
|  | svgSetPaintData(svgElement, paintObj); | 
|  | svgPicture.appendChild(svgElement); | 
|  | } | 
|  | } | 
|  |  | 
|  | function svgSetTextData(svgElement, text) { | 
|  | svgElement.setAttribute('x', text.x); | 
|  | svgElement.setAttribute('y', text.y); | 
|  | } |