highstock-rails 1.3.10 → 2.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/images/highstock/meteogram-symbols-30px.png +0 -0
  4. data/app/assets/javascripts/highstock.js +418 -369
  5. data/app/assets/javascripts/highstock/adapters/standalone-framework.js +12 -12
  6. data/app/assets/javascripts/highstock/adapters/standalone-framework.src.js +635 -0
  7. data/app/assets/javascripts/highstock/highcharts-3d.js +48 -0
  8. data/app/assets/javascripts/highstock/highcharts-3d.src.js +1711 -0
  9. data/app/assets/javascripts/highstock/highcharts-more.js +49 -45
  10. data/app/assets/javascripts/highstock/highstock-all.js +637 -0
  11. data/app/assets/javascripts/highstock/modules/boost.js +12 -0
  12. data/app/assets/javascripts/highstock/modules/boost.src.js +591 -0
  13. data/app/assets/javascripts/highstock/modules/canvas-tools.js +9 -9
  14. data/app/assets/javascripts/highstock/modules/canvas-tools.src.js +3114 -0
  15. data/app/assets/javascripts/highstock/modules/data.js +20 -10
  16. data/app/assets/javascripts/highstock/modules/data.src.js +957 -0
  17. data/app/assets/javascripts/highstock/modules/drilldown.js +17 -14
  18. data/app/assets/javascripts/highstock/modules/drilldown.src.js +717 -0
  19. data/app/assets/javascripts/highstock/modules/exporting.js +17 -15
  20. data/app/assets/javascripts/highstock/modules/exporting.src.js +780 -0
  21. data/app/assets/javascripts/highstock/modules/funnel.js +5 -5
  22. data/app/assets/javascripts/highstock/modules/funnel.src.js +322 -0
  23. data/app/assets/javascripts/highstock/modules/heatmap.js +23 -2
  24. data/app/assets/javascripts/highstock/modules/heatmap.src.js +711 -0
  25. data/app/assets/javascripts/highstock/modules/no-data-to-display.js +4 -4
  26. data/app/assets/javascripts/highstock/modules/no-data-to-display.src.js +143 -0
  27. data/app/assets/javascripts/highstock/modules/offline-exporting.js +14 -0
  28. data/app/assets/javascripts/highstock/modules/offline-exporting.src.js +280 -0
  29. data/app/assets/javascripts/highstock/modules/solid-gauge.js +14 -0
  30. data/app/assets/javascripts/highstock/modules/solid-gauge.src.js +273 -0
  31. data/app/assets/javascripts/highstock/modules/treemap.js +30 -0
  32. data/app/assets/javascripts/highstock/modules/treemap.src.js +868 -0
  33. data/app/assets/javascripts/highstock/themes/dark-blue.js +1 -1
  34. data/app/assets/javascripts/highstock/themes/dark-green.js +1 -1
  35. data/app/assets/javascripts/highstock/themes/dark-unica.js +213 -0
  36. data/app/assets/javascripts/highstock/themes/gray.js +1 -1
  37. data/app/assets/javascripts/highstock/themes/grid-light.js +74 -0
  38. data/app/assets/javascripts/highstock/themes/sand-signika.js +104 -0
  39. data/lib/highstock/rails/version.rb +1 -1
  40. metadata +26 -7
  41. data/app/assets/javascripts/highstock/adapters/mootools-adapter.js +0 -13
  42. data/app/assets/javascripts/highstock/adapters/prototype-adapter.js +0 -15
  43. data/app/assets/javascripts/highstock/modules/annotations.js +0 -7
  44. data/app/assets/javascripts/highstock/modules/map.js +0 -41
@@ -11,7 +11,7 @@
11
11
 
12
12
  Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
13
13
 
14
- Highstock JS v1.3.10 (2014-03-10)
14
+ Highcharts JS v4.1.10 (2015-12-07)
15
15
  CanVGRenderer Extension module
16
16
 
17
17
  (c) 2011-2012 Torstein Honsi, Erik Olsson
@@ -29,7 +29,7 @@ this.g>255?255:this.g;this.b=this.b<0||isNaN(this.b)?0:this.b>255?255:this.b;thi
29
29
  c.setAttribute("id","rgbcolor-examples");for(k=0;k<b.length;k++)try{var l=document.createElement("li"),o=new RGBColor(b[k]),n=document.createElement("div");n.style.cssText="margin: 3px; border: 1px solid black; background:"+o.toHex()+"; color:"+o.toHex();n.appendChild(document.createTextNode("test"));var q=document.createTextNode(" "+b[k]+" -> "+o.toRGB()+" -> "+o.toHex());l.appendChild(n);l.appendChild(q);c.appendChild(l)}catch(p){}return c}}
30
30
  if(!window.console)window.console={},window.console.log=function(){},window.console.dir=function(){};if(!Array.prototype.indexOf)Array.prototype.indexOf=function(m){for(var a=0;a<this.length;a++)if(this[a]==m)return a;return-1};
31
31
  (function(){function m(){var a={FRAMERATE:30,MAX_VIRTUAL_PIXELS:3E4};a.init=function(c){a.Definitions={};a.Styles={};a.Animations=[];a.Images=[];a.ctx=c;a.ViewPort=new function(){this.viewPorts=[];this.Clear=function(){this.viewPorts=[]};this.SetCurrent=function(a,b){this.viewPorts.push({width:a,height:b})};this.RemoveCurrent=function(){this.viewPorts.pop()};this.Current=function(){return this.viewPorts[this.viewPorts.length-1]};this.width=function(){return this.Current().width};this.height=function(){return this.Current().height};
32
- this.ComputeSize=function(a){return a!=null&&typeof a=="number"?a:a=="x"?this.width():a=="y"?this.height():Math.sqrt(Math.pow(this.width(),2)+Math.pow(this.height(),2))/Math.sqrt(2)}}};a.init();a.ImagesLoaded=function(){for(var c=0;c<a.Images.length;c++)if(!a.Images[c].loaded)return!1;return!0};a.trim=function(a){return a.replace(/^\s+|\s+$/g,"")};a.compressSpaces=function(a){return a.replace(/[\s\r\t\n]+/gm," ")};a.ajax=function(a){var d;return(d=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"))?
32
+ this.ComputeSize=function(a){return a!=null&&typeof a=="number"?a:a=="x"?this.width():a=="y"?this.height():Math.sqrt(Math.pow(this.width(),2)+Math.pow(this.height(),2))/Math.sqrt(2)}}};a.init();a.ImagesLoaded=function(){for(var c=0;c<a.Images.length;c++)if(!a.Images[c].loaded)return!1;return!0};a.trim=function(a){return a.replace(/^\s+|\s+$/g,"")};a.compressSpaces=function(a){return a?a.replace(/[\s\r\t\n]+/gm," "):""};a.ajax=function(a){var d;return(d=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"))?
33
33
  (d.open("GET",a,!1),d.send(null),d.responseText):null};a.parseXml=function(a){if(window.DOMParser)return(new DOMParser).parseFromString(a,"text/xml");else{var a=a.replace(/<!DOCTYPE svg[^>]*>/,""),d=new ActiveXObject("Microsoft.XMLDOM");d.async="false";d.loadXML(a);return d}};a.Property=function(c,d){this.name=c;this.value=d;this.hasValue=function(){return this.value!=null&&this.value!==""};this.numValue=function(){if(!this.hasValue())return 0;var b=parseFloat(this.value);(this.value+"").match(/%$/)&&
34
34
  (b/=100);return b};this.valueOrDefault=function(b){return this.hasValue()?this.value:b};this.numValueOrDefault=function(b){return this.hasValue()?this.numValue():b};var b=this;this.Color={addOpacity:function(d){var c=b.value;if(d!=null&&d!=""){var f=new RGBColor(b.value);f.ok&&(c="rgba("+f.r+", "+f.g+", "+f.b+", "+d+")")}return new a.Property(b.name,c)}};this.Definition={getDefinition:function(){var d=b.value.replace(/^(url\()?#([^\)]+)\)?$/,"$2");return a.Definitions[d]},isUrl:function(){return b.value.indexOf("url(")==
35
35
  0},getFillStyle:function(b){var d=this.getDefinition();return d!=null&&d.createGradient?d.createGradient(a.ctx,b):d!=null&&d.createPattern?d.createPattern(a.ctx,b):null}};this.Length={DPI:function(){return 96},EM:function(b){var d=12,c=new a.Property("fontSize",a.Font.Parse(a.ctx.font).fontSize);c.hasValue()&&(d=c.Length.toPixels(b));return d},toPixels:function(d){if(!b.hasValue())return 0;var c=b.value+"";return c.match(/em$/)?b.numValue()*this.EM(d):c.match(/ex$/)?b.numValue()*this.EM(d)/2:c.match(/px$/)?
@@ -96,9 +96,9 @@ a.Element.animate;this.base(c)};a.Element.animateTransform.prototype=new a.Eleme
96
96
  d;else if(d.type=="glyph")d.arabicForm!=""?(this.isArabic=this.isRTL=!0,typeof this.glyphs[d.unicode]=="undefined"&&(this.glyphs[d.unicode]=[]),this.glyphs[d.unicode][d.arabicForm]=d):this.glyphs[d.unicode]=d}};a.Element.font.prototype=new a.Element.ElementBase;a.Element.fontface=function(c){this.base=a.Element.ElementBase;this.base(c);this.ascent=this.attribute("ascent").value;this.descent=this.attribute("descent").value;this.unitsPerEm=this.attribute("units-per-em").numValue()};a.Element.fontface.prototype=
97
97
  new a.Element.ElementBase;a.Element.missingglyph=function(c){this.base=a.Element.path;this.base(c);this.horizAdvX=0};a.Element.missingglyph.prototype=new a.Element.path;a.Element.glyph=function(c){this.base=a.Element.path;this.base(c);this.horizAdvX=this.attribute("horiz-adv-x").numValue();this.unicode=this.attribute("unicode").value;this.arabicForm=this.attribute("arabic-form").value};a.Element.glyph.prototype=new a.Element.path;a.Element.text=function(c){this.base=a.Element.RenderedElementBase;
98
98
  this.base(c);if(c!=null){this.children=[];for(var d=0;d<c.childNodes.length;d++){var b=c.childNodes[d];b.nodeType==1?this.addChild(b,!0):b.nodeType==3&&this.addChild(new a.Element.tspan(b),!1)}}this.baseSetContext=this.setContext;this.setContext=function(b){this.baseSetContext(b);if(this.style("dominant-baseline").hasValue())b.textBaseline=this.style("dominant-baseline").value;if(this.style("alignment-baseline").hasValue())b.textBaseline=this.style("alignment-baseline").value};this.renderChildren=
99
- function(b){for(var a=this.style("text-anchor").valueOrDefault("start"),c=this.attribute("x").Length.toPixels("x"),d=this.attribute("y").Length.toPixels("y"),j=0;j<this.children.length;j++){var h=this.children[j];h.attribute("x").hasValue()?h.x=h.attribute("x").Length.toPixels("x"):(h.attribute("dx").hasValue()&&(c+=h.attribute("dx").Length.toPixels("x")),h.x=c);c=h.measureText(b);if(a!="start"&&(j==0||h.attribute("x").hasValue())){for(var l=c,o=j+1;o<this.children.length;o++){var n=this.children[o];
100
- if(n.attribute("x").hasValue())break;l+=n.measureText(b)}h.x-=a=="end"?l:l/2}c=h.x+c;h.attribute("y").hasValue()?h.y=h.attribute("y").Length.toPixels("y"):(h.attribute("dy").hasValue()&&(d+=h.attribute("dy").Length.toPixels("y")),h.y=d);d=h.y;h.render(b)}}};a.Element.text.prototype=new a.Element.RenderedElementBase;a.Element.TextElementBase=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.getGlyph=function(a,b,c){var e=b[c],f=null;if(a.isArabic){var g="isolated";if((c==0||b[c-
101
- 1]==" ")&&c<b.length-2&&b[c+1]!=" ")g="terminal";c>0&&b[c-1]!=" "&&c<b.length-2&&b[c+1]!=" "&&(g="medial");if(c>0&&b[c-1]!=" "&&(c==b.length-1||b[c+1]==" "))g="initial";typeof a.glyphs[e]!="undefined"&&(f=a.glyphs[e][g],f==null&&a.glyphs[e].type=="glyph"&&(f=a.glyphs[e]))}else f=a.glyphs[e];if(f==null)f=a.missingGlyph;return f};this.renderChildren=function(c){var b=this.parent.style("font-family").Definition.getDefinition();if(b!=null){var k=this.parent.style("font-size").numValueOrDefault(a.Font.Parse(a.ctx.font).fontSize),
99
+ function(b){for(var a=this.style("text-anchor").valueOrDefault("start"),c=this.attribute("x").Length.toPixels("x"),d=this.attribute("y").Length.toPixels("y"),j=0;j<this.children.length;j++){var h=this.children[j];h.attribute("x").hasValue()?h.x=h.attribute("x").Length.toPixels("x"):(h.attribute("dx").hasValue()&&(c+=h.attribute("dx").Length.toPixels("x")),h.x=c);c=h.measureText?h.measureText(b):0;if(a!="start"&&(j==0||h.attribute("x").hasValue())){for(var l=c,o=j+1;o<this.children.length;o++){var n=
100
+ this.children[o];if(n.attribute("x").hasValue())break;l+=n.measureText?n.measureText(b):0}h.x-=a=="end"?l:l/2}c=h.x+c;h.attribute("y").hasValue()?h.y=h.attribute("y").Length.toPixels("y"):(h.attribute("dy").hasValue()&&(d+=h.attribute("dy").Length.toPixels("y")),h.y=d);d=h.y;h.render(b)}}};a.Element.text.prototype=new a.Element.RenderedElementBase;a.Element.TextElementBase=function(c){this.base=a.Element.RenderedElementBase;this.base(c);this.getGlyph=function(a,b,c){var e=b[c],f=null;if(a.isArabic){var g=
101
+ "isolated";if((c==0||b[c-1]==" ")&&c<b.length-2&&b[c+1]!=" ")g="terminal";c>0&&b[c-1]!=" "&&c<b.length-2&&b[c+1]!=" "&&(g="medial");if(c>0&&b[c-1]!=" "&&(c==b.length-1||b[c+1]==" "))g="initial";typeof a.glyphs[e]!="undefined"&&(f=a.glyphs[e][g],f==null&&a.glyphs[e].type=="glyph"&&(f=a.glyphs[e]))}else f=a.glyphs[e];if(f==null)f=a.missingGlyph;return f};this.renderChildren=function(c){var b=this.parent.style("font-family").Definition.getDefinition();if(b!=null){var k=this.parent.style("font-size").numValueOrDefault(a.Font.Parse(a.ctx.font).fontSize),
102
102
  e=this.parent.style("font-style").valueOrDefault(a.Font.Parse(a.ctx.font).fontStyle),f=this.getText();b.isRTL&&(f=f.split("").reverse().join(""));for(var g=a.ToNumberArray(this.parent.attribute("dx").value),j=0;j<f.length;j++){var h=this.getGlyph(b,f,j),l=k/b.fontFace.unitsPerEm;c.translate(this.x,this.y);c.scale(l,-l);var o=c.lineWidth;c.lineWidth=c.lineWidth*b.fontFace.unitsPerEm/k;e=="italic"&&c.transform(1,0,0.4,1,0,0);h.render(c);e=="italic"&&c.transform(1,0,-0.4,1,0,0);c.lineWidth=o;c.scale(1/
103
103
  l,-1/l);c.translate(-this.x,-this.y);this.x+=k*(h.horizAdvX||b.horizAdvX)/b.fontFace.unitsPerEm;typeof g[j]!="undefined"&&!isNaN(g[j])&&(this.x+=g[j])}}else c.strokeStyle!=""&&c.strokeText(a.compressSpaces(this.getText()),this.x,this.y),c.fillStyle!=""&&c.fillText(a.compressSpaces(this.getText()),this.x,this.y)};this.getText=function(){};this.measureText=function(c){var b=this.parent.style("font-family").Definition.getDefinition();if(b!=null){var c=this.parent.style("font-size").numValueOrDefault(a.Font.Parse(a.ctx.font).fontSize),
104
104
  k=0,e=this.getText();b.isRTL&&(e=e.split("").reverse().join(""));for(var f=a.ToNumberArray(this.parent.attribute("dx").value),g=0;g<e.length;g++){var j=this.getGlyph(b,e,g);k+=(j.horizAdvX||b.horizAdvX)*c/b.fontFace.unitsPerEm;typeof f[g]!="undefined"&&!isNaN(f[g])&&(k+=f[g])}return k}b=a.compressSpaces(this.getText());if(!c.measureText)return b.length*10;c.save();this.setContext(c);b=c.measureText(b).width;c.restore();return b}};a.Element.TextElementBase.prototype=new a.Element.RenderedElementBase;
@@ -127,7 +127,7 @@ a.intervalID=setInterval(function(){var b=!1;g&&a.ImagesLoaded()&&(g=!1,b=!0);a.
127
127
  this.events[b];d.isPointInBox(k.x,k.y)&&(this.eventElements[b]=a)}};this.runEvents=function(){a.ctx.canvas.style.cursor="";for(var c=0;c<this.events.length;c++)for(var d=this.events[c],b=this.eventElements[c];b;)d.run(b),b=b.parent;this.events=[];this.eventElements=[]}};return a}this.canvg=function(a,c,d){if(a==null&&c==null&&d==null)for(var c=document.getElementsByTagName("svg"),b=0;b<c.length;b++){a=c[b];d=document.createElement("canvas");d.width=a.clientWidth;d.height=a.clientHeight;a.parentNode.insertBefore(d,
128
128
  a);a.parentNode.removeChild(a);var k=document.createElement("div");k.appendChild(a);canvg(d,k.innerHTML)}else d=d||{},typeof a=="string"&&(a=document.getElementById(a)),a.svg==null?(b=m(),a.svg=b):(b=a.svg,b.stop()),b.opts=d,a=a.getContext("2d"),typeof c.documentElement!="undefined"?b.loadXmlDoc(a,c):c.substr(0,1)=="<"?b.loadXml(a,c):b.load(a,c)}})();
129
129
  if(CanvasRenderingContext2D)CanvasRenderingContext2D.prototype.drawSvg=function(m,a,c,d,b){canvg(this.canvas,m,{ignoreMouse:!0,ignoreAnimation:!0,ignoreDimensions:!0,ignoreClear:!0,offsetX:a,offsetY:c,scaleWidth:d,scaleHeight:b})};
130
- (function(m){var a=m.css,c=m.CanVGRenderer,d=m.SVGRenderer,b=m.extend,k=m.merge,e=m.addEvent,f=m.createElement,g=m.discardElement;b(c.prototype,d.prototype);b(c.prototype,{create:function(a,b,c,d){this.setContainer(b,c,d);this.configure(a)},setContainer:function(a,b,c){var d=a.style,e=a.parentNode,g=d.left,d=d.top,k=a.offsetWidth,m=a.offsetHeight,s={visibility:"hidden",position:"absolute"};this.init.apply(this,[a,b,c]);this.canvas=f("canvas",{width:k,height:m},{position:"relative",left:g,top:d},a);
131
- this.ttLine=f("div",null,s,e);this.ttDiv=f("div",null,s,e);this.ttTimer=void 0;this.hiddenSvg=a=f("div",{width:k,height:m},{visibility:"hidden",left:g,top:d},e);a.appendChild(this.box)},configure:function(b){var c=this,d=b.options.tooltip,f=d.borderWidth,g=c.ttDiv,m=d.style,p=c.ttLine,t=parseInt(m.padding,10),m=k(m,{padding:t+"px","background-color":d.backgroundColor,"border-style":"solid","border-width":f+"px","border-radius":d.borderRadius+"px"});d.shadow&&(m=k(m,{"box-shadow":"1px 1px 3px gray",
132
- "-webkit-box-shadow":"1px 1px 3px gray"}));a(g,m);a(p,{"border-left":"1px solid darkgray"});e(b,"tooltipRefresh",function(d){var e=b.container,f=e.offsetLeft,e=e.offsetTop,k;g.innerHTML=d.text;k=b.tooltip.getPosition(g.offsetWidth,g.offsetHeight,{plotX:d.x,plotY:d.y});a(g,{visibility:"visible",left:k.x+"px",top:k.y+"px","border-color":d.borderColor});a(p,{visibility:"visible",left:f+d.x+"px",top:e+b.plotTop+"px",height:b.plotHeight+"px"});c.ttTimer!==void 0&&clearTimeout(c.ttTimer);c.ttTimer=setTimeout(function(){a(g,
133
- {visibility:"hidden"});a(p,{visibility:"hidden"})},3E3)})},destroy:function(){g(this.canvas);this.ttTimer!==void 0&&clearTimeout(this.ttTimer);g(this.ttLine);g(this.ttDiv);g(this.hiddenSvg);return d.prototype.destroy.apply(this)},color:function(a,b,c){a&&a.linearGradient&&(a=a.stops[a.stops.length-1][1]);return d.prototype.color.call(this,a,b,c)},draw:function(){window.canvg(this.canvas,this.hiddenSvg.innerHTML)}})})(Highcharts);
130
+ (function(m){var a=m.css,c=m.CanVGRenderer,d=m.SVGRenderer,b=m.extend,k=m.merge,e=m.addEvent,f=m.createElement,g=m.discardElement;b(c.prototype,d.prototype);b(c.prototype,{create:function(a,b,c,d){this.setContainer(b,c,d);this.configure(a)},setContainer:function(a,b,c){var d=a.style,e=a.parentNode,g=d.left,d=d.top,k=a.offsetWidth,m=a.offsetHeight,s={visibility:"hidden",position:"absolute"};this.init(a,b,c);this.canvas=f("canvas",{width:k,height:m},{position:"relative",left:g,top:d},a);this.ttLine=
131
+ f("div",null,s,e);this.ttDiv=f("div",null,s,e);this.ttTimer=void 0;this.hiddenSvg=a=f("div",{width:k,height:m},{visibility:"hidden",left:g,top:d},e);a.appendChild(this.box)},configure:function(b){var c=this,d=b.options.tooltip,f=d.borderWidth,g=c.ttDiv,m=d.style,p=c.ttLine,t=parseInt(m.padding,10),m=k(m,{padding:t+"px","background-color":d.backgroundColor,"border-style":"solid","border-width":f+"px","border-radius":d.borderRadius+"px"});d.shadow&&(m=k(m,{"box-shadow":"1px 1px 3px gray","-webkit-box-shadow":"1px 1px 3px gray"}));
132
+ a(g,m);a(p,{"border-left":"1px solid darkgray"});e(b,"tooltipRefresh",function(d){var e=b.container,f=e.offsetLeft,e=e.offsetTop,k;g.innerHTML=d.text;k=b.tooltip.getPosition(g.offsetWidth,g.offsetHeight,{plotX:d.x,plotY:d.y});a(g,{visibility:"visible",left:k.x+"px",top:k.y+"px","border-color":d.borderColor});a(p,{visibility:"visible",left:f+d.x+"px",top:e+b.plotTop+"px",height:b.plotHeight+"px"});c.ttTimer!==void 0&&clearTimeout(c.ttTimer);c.ttTimer=setTimeout(function(){a(g,{visibility:"hidden"});
133
+ a(p,{visibility:"hidden"})},3E3)})},destroy:function(){g(this.canvas);this.ttTimer!==void 0&&clearTimeout(this.ttTimer);g(this.ttLine);g(this.ttDiv);g(this.hiddenSvg);return d.prototype.destroy.apply(this)},color:function(a,b,c){a&&a.linearGradient&&(a=a.stops[a.stops.length-1][1]);return d.prototype.color.call(this,a,b,c)},draw:function(){window.canvg(this.canvas,this.hiddenSvg.innerHTML)}})})(Highcharts);
@@ -0,0 +1,3114 @@
1
+ /**
2
+ * @license A class to parse color values
3
+ * @author Stoyan Stefanov <sstoo@gmail.com>
4
+ * @link http://www.phpied.com/rgb-color-parser-in-javascript/
5
+ * Use it if you like it
6
+ *
7
+ */
8
+ function RGBColor(color_string)
9
+ {
10
+ this.ok = false;
11
+
12
+ // strip any leading #
13
+ if (color_string.charAt(0) == '#') { // remove # if any
14
+ color_string = color_string.substr(1,6);
15
+ }
16
+
17
+ color_string = color_string.replace(/ /g,'');
18
+ color_string = color_string.toLowerCase();
19
+
20
+ // before getting into regexps, try simple matches
21
+ // and overwrite the input
22
+ var simple_colors = {
23
+ aliceblue: 'f0f8ff',
24
+ antiquewhite: 'faebd7',
25
+ aqua: '00ffff',
26
+ aquamarine: '7fffd4',
27
+ azure: 'f0ffff',
28
+ beige: 'f5f5dc',
29
+ bisque: 'ffe4c4',
30
+ black: '000000',
31
+ blanchedalmond: 'ffebcd',
32
+ blue: '0000ff',
33
+ blueviolet: '8a2be2',
34
+ brown: 'a52a2a',
35
+ burlywood: 'deb887',
36
+ cadetblue: '5f9ea0',
37
+ chartreuse: '7fff00',
38
+ chocolate: 'd2691e',
39
+ coral: 'ff7f50',
40
+ cornflowerblue: '6495ed',
41
+ cornsilk: 'fff8dc',
42
+ crimson: 'dc143c',
43
+ cyan: '00ffff',
44
+ darkblue: '00008b',
45
+ darkcyan: '008b8b',
46
+ darkgoldenrod: 'b8860b',
47
+ darkgray: 'a9a9a9',
48
+ darkgreen: '006400',
49
+ darkkhaki: 'bdb76b',
50
+ darkmagenta: '8b008b',
51
+ darkolivegreen: '556b2f',
52
+ darkorange: 'ff8c00',
53
+ darkorchid: '9932cc',
54
+ darkred: '8b0000',
55
+ darksalmon: 'e9967a',
56
+ darkseagreen: '8fbc8f',
57
+ darkslateblue: '483d8b',
58
+ darkslategray: '2f4f4f',
59
+ darkturquoise: '00ced1',
60
+ darkviolet: '9400d3',
61
+ deeppink: 'ff1493',
62
+ deepskyblue: '00bfff',
63
+ dimgray: '696969',
64
+ dodgerblue: '1e90ff',
65
+ feldspar: 'd19275',
66
+ firebrick: 'b22222',
67
+ floralwhite: 'fffaf0',
68
+ forestgreen: '228b22',
69
+ fuchsia: 'ff00ff',
70
+ gainsboro: 'dcdcdc',
71
+ ghostwhite: 'f8f8ff',
72
+ gold: 'ffd700',
73
+ goldenrod: 'daa520',
74
+ gray: '808080',
75
+ green: '008000',
76
+ greenyellow: 'adff2f',
77
+ honeydew: 'f0fff0',
78
+ hotpink: 'ff69b4',
79
+ indianred : 'cd5c5c',
80
+ indigo : '4b0082',
81
+ ivory: 'fffff0',
82
+ khaki: 'f0e68c',
83
+ lavender: 'e6e6fa',
84
+ lavenderblush: 'fff0f5',
85
+ lawngreen: '7cfc00',
86
+ lemonchiffon: 'fffacd',
87
+ lightblue: 'add8e6',
88
+ lightcoral: 'f08080',
89
+ lightcyan: 'e0ffff',
90
+ lightgoldenrodyellow: 'fafad2',
91
+ lightgrey: 'd3d3d3',
92
+ lightgreen: '90ee90',
93
+ lightpink: 'ffb6c1',
94
+ lightsalmon: 'ffa07a',
95
+ lightseagreen: '20b2aa',
96
+ lightskyblue: '87cefa',
97
+ lightslateblue: '8470ff',
98
+ lightslategray: '778899',
99
+ lightsteelblue: 'b0c4de',
100
+ lightyellow: 'ffffe0',
101
+ lime: '00ff00',
102
+ limegreen: '32cd32',
103
+ linen: 'faf0e6',
104
+ magenta: 'ff00ff',
105
+ maroon: '800000',
106
+ mediumaquamarine: '66cdaa',
107
+ mediumblue: '0000cd',
108
+ mediumorchid: 'ba55d3',
109
+ mediumpurple: '9370d8',
110
+ mediumseagreen: '3cb371',
111
+ mediumslateblue: '7b68ee',
112
+ mediumspringgreen: '00fa9a',
113
+ mediumturquoise: '48d1cc',
114
+ mediumvioletred: 'c71585',
115
+ midnightblue: '191970',
116
+ mintcream: 'f5fffa',
117
+ mistyrose: 'ffe4e1',
118
+ moccasin: 'ffe4b5',
119
+ navajowhite: 'ffdead',
120
+ navy: '000080',
121
+ oldlace: 'fdf5e6',
122
+ olive: '808000',
123
+ olivedrab: '6b8e23',
124
+ orange: 'ffa500',
125
+ orangered: 'ff4500',
126
+ orchid: 'da70d6',
127
+ palegoldenrod: 'eee8aa',
128
+ palegreen: '98fb98',
129
+ paleturquoise: 'afeeee',
130
+ palevioletred: 'd87093',
131
+ papayawhip: 'ffefd5',
132
+ peachpuff: 'ffdab9',
133
+ peru: 'cd853f',
134
+ pink: 'ffc0cb',
135
+ plum: 'dda0dd',
136
+ powderblue: 'b0e0e6',
137
+ purple: '800080',
138
+ red: 'ff0000',
139
+ rosybrown: 'bc8f8f',
140
+ royalblue: '4169e1',
141
+ saddlebrown: '8b4513',
142
+ salmon: 'fa8072',
143
+ sandybrown: 'f4a460',
144
+ seagreen: '2e8b57',
145
+ seashell: 'fff5ee',
146
+ sienna: 'a0522d',
147
+ silver: 'c0c0c0',
148
+ skyblue: '87ceeb',
149
+ slateblue: '6a5acd',
150
+ slategray: '708090',
151
+ snow: 'fffafa',
152
+ springgreen: '00ff7f',
153
+ steelblue: '4682b4',
154
+ tan: 'd2b48c',
155
+ teal: '008080',
156
+ thistle: 'd8bfd8',
157
+ tomato: 'ff6347',
158
+ turquoise: '40e0d0',
159
+ violet: 'ee82ee',
160
+ violetred: 'd02090',
161
+ wheat: 'f5deb3',
162
+ white: 'ffffff',
163
+ whitesmoke: 'f5f5f5',
164
+ yellow: 'ffff00',
165
+ yellowgreen: '9acd32'
166
+ };
167
+ for (var key in simple_colors) {
168
+ if (color_string == key) {
169
+ color_string = simple_colors[key];
170
+ }
171
+ }
172
+ // emd of simple type-in colors
173
+
174
+ // array of color definition objects
175
+ var color_defs = [
176
+ {
177
+ re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
178
+ example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
179
+ process: function (bits){
180
+ return [
181
+ parseInt(bits[1]),
182
+ parseInt(bits[2]),
183
+ parseInt(bits[3])
184
+ ];
185
+ }
186
+ },
187
+ {
188
+ re: /^(\w{2})(\w{2})(\w{2})$/,
189
+ example: ['#00ff00', '336699'],
190
+ process: function (bits){
191
+ return [
192
+ parseInt(bits[1], 16),
193
+ parseInt(bits[2], 16),
194
+ parseInt(bits[3], 16)
195
+ ];
196
+ }
197
+ },
198
+ {
199
+ re: /^(\w{1})(\w{1})(\w{1})$/,
200
+ example: ['#fb0', 'f0f'],
201
+ process: function (bits){
202
+ return [
203
+ parseInt(bits[1] + bits[1], 16),
204
+ parseInt(bits[2] + bits[2], 16),
205
+ parseInt(bits[3] + bits[3], 16)
206
+ ];
207
+ }
208
+ }
209
+ ];
210
+
211
+ // search through the definitions to find a match
212
+ for (var i = 0; i < color_defs.length; i++) {
213
+ var re = color_defs[i].re;
214
+ var processor = color_defs[i].process;
215
+ var bits = re.exec(color_string);
216
+ if (bits) {
217
+ channels = processor(bits);
218
+ this.r = channels[0];
219
+ this.g = channels[1];
220
+ this.b = channels[2];
221
+ this.ok = true;
222
+ }
223
+
224
+ }
225
+
226
+ // validate/cleanup values
227
+ this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
228
+ this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
229
+ this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
230
+
231
+ // some getters
232
+ this.toRGB = function () {
233
+ return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
234
+ }
235
+ this.toHex = function () {
236
+ var r = this.r.toString(16);
237
+ var g = this.g.toString(16);
238
+ var b = this.b.toString(16);
239
+ if (r.length == 1) r = '0' + r;
240
+ if (g.length == 1) g = '0' + g;
241
+ if (b.length == 1) b = '0' + b;
242
+ return '#' + r + g + b;
243
+ }
244
+
245
+ // help
246
+ this.getHelpXML = function () {
247
+
248
+ var examples = new Array();
249
+ // add regexps
250
+ for (var i = 0; i < color_defs.length; i++) {
251
+ var example = color_defs[i].example;
252
+ for (var j = 0; j < example.length; j++) {
253
+ examples[examples.length] = example[j];
254
+ }
255
+ }
256
+ // add type-in colors
257
+ for (var sc in simple_colors) {
258
+ examples[examples.length] = sc;
259
+ }
260
+
261
+ var xml = document.createElement('ul');
262
+ xml.setAttribute('id', 'rgbcolor-examples');
263
+ for (var i = 0; i < examples.length; i++) {
264
+ try {
265
+ var list_item = document.createElement('li');
266
+ var list_color = new RGBColor(examples[i]);
267
+ var example_div = document.createElement('div');
268
+ example_div.style.cssText =
269
+ 'margin: 3px; '
270
+ + 'border: 1px solid black; '
271
+ + 'background:' + list_color.toHex() + '; '
272
+ + 'color:' + list_color.toHex()
273
+ ;
274
+ example_div.appendChild(document.createTextNode('test'));
275
+ var list_item_value = document.createTextNode(
276
+ ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
277
+ );
278
+ list_item.appendChild(example_div);
279
+ list_item.appendChild(list_item_value);
280
+ xml.appendChild(list_item);
281
+
282
+ } catch(e){}
283
+ }
284
+ return xml;
285
+
286
+ }
287
+
288
+ }
289
+
290
+ /**
291
+ * @license canvg.js - Javascript SVG parser and renderer on Canvas
292
+ * MIT Licensed
293
+ * Gabe Lerner (gabelerner@gmail.com)
294
+ * http://code.google.com/p/canvg/
295
+ *
296
+ * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
297
+ *
298
+ */
299
+ if(!window.console) {
300
+ window.console = {};
301
+ window.console.log = function(str) {};
302
+ window.console.dir = function(str) {};
303
+ }
304
+
305
+ if(!Array.prototype.indexOf){
306
+ Array.prototype.indexOf = function(obj){
307
+ for(var i=0; i<this.length; i++){
308
+ if(this[i]==obj){
309
+ return i;
310
+ }
311
+ }
312
+ return -1;
313
+ }
314
+ }
315
+
316
+ (function(){
317
+ // canvg(target, s)
318
+ // empty parameters: replace all 'svg' elements on page with 'canvas' elements
319
+ // target: canvas element or the id of a canvas element
320
+ // s: svg string, url to svg file, or xml document
321
+ // opts: optional hash of options
322
+ // ignoreMouse: true => ignore mouse events
323
+ // ignoreAnimation: true => ignore animations
324
+ // ignoreDimensions: true => does not try to resize canvas
325
+ // ignoreClear: true => does not clear canvas
326
+ // offsetX: int => draws at a x offset
327
+ // offsetY: int => draws at a y offset
328
+ // scaleWidth: int => scales horizontally to width
329
+ // scaleHeight: int => scales vertically to height
330
+ // renderCallback: function => will call the function after the first render is completed
331
+ // forceRedraw: function => will call the function on every frame, if it returns true, will redraw
332
+ this.canvg = function (target, s, opts) {
333
+ // no parameters
334
+ if (target == null && s == null && opts == null) {
335
+ var svgTags = document.getElementsByTagName('svg');
336
+ for (var i=0; i<svgTags.length; i++) {
337
+ var svgTag = svgTags[i];
338
+ var c = document.createElement('canvas');
339
+ c.width = svgTag.clientWidth;
340
+ c.height = svgTag.clientHeight;
341
+ svgTag.parentNode.insertBefore(c, svgTag);
342
+ svgTag.parentNode.removeChild(svgTag);
343
+ var div = document.createElement('div');
344
+ div.appendChild(svgTag);
345
+ canvg(c, div.innerHTML);
346
+ }
347
+ return;
348
+ }
349
+ opts = opts || {};
350
+
351
+ if (typeof target == 'string') {
352
+ target = document.getElementById(target);
353
+ }
354
+
355
+ // reuse class per canvas
356
+ var svg;
357
+ if (target.svg == null) {
358
+ svg = build();
359
+ target.svg = svg;
360
+ }
361
+ else {
362
+ svg = target.svg;
363
+ svg.stop();
364
+ }
365
+ svg.opts = opts;
366
+
367
+ var ctx = target.getContext('2d');
368
+ if (typeof(s.documentElement) != 'undefined') {
369
+ // load from xml doc
370
+ svg.loadXmlDoc(ctx, s);
371
+ }
372
+ else if (s.substr(0,1) == '<') {
373
+ // load from xml string
374
+ svg.loadXml(ctx, s);
375
+ }
376
+ else {
377
+ // load from url
378
+ svg.load(ctx, s);
379
+ }
380
+ }
381
+
382
+ function build() {
383
+ var svg = { };
384
+
385
+ svg.FRAMERATE = 30;
386
+ svg.MAX_VIRTUAL_PIXELS = 30000;
387
+
388
+ // globals
389
+ svg.init = function(ctx) {
390
+ svg.Definitions = {};
391
+ svg.Styles = {};
392
+ svg.Animations = [];
393
+ svg.Images = [];
394
+ svg.ctx = ctx;
395
+ svg.ViewPort = new (function () {
396
+ this.viewPorts = [];
397
+ this.Clear = function() { this.viewPorts = []; }
398
+ this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
399
+ this.RemoveCurrent = function() { this.viewPorts.pop(); }
400
+ this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }
401
+ this.width = function() { return this.Current().width; }
402
+ this.height = function() { return this.Current().height; }
403
+ this.ComputeSize = function(d) {
404
+ if (d != null && typeof(d) == 'number') return d;
405
+ if (d == 'x') return this.width();
406
+ if (d == 'y') return this.height();
407
+ return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
408
+ }
409
+ });
410
+ }
411
+ svg.init();
412
+
413
+ // images loaded
414
+ svg.ImagesLoaded = function() {
415
+ for (var i=0; i<svg.Images.length; i++) {
416
+ if (!svg.Images[i].loaded) return false;
417
+ }
418
+ return true;
419
+ }
420
+
421
+ // trim
422
+ svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); }
423
+
424
+ // compress spaces
425
+ svg.compressSpaces = function(s) { return s ? s.replace(/[\s\r\t\n]+/gm,' ') : ''; }
426
+
427
+ // ajax
428
+ svg.ajax = function(url) {
429
+ var AJAX;
430
+ if(window.XMLHttpRequest){AJAX=new XMLHttpRequest();}
431
+ else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
432
+ if(AJAX){
433
+ AJAX.open('GET',url,false);
434
+ AJAX.send(null);
435
+ return AJAX.responseText;
436
+ }
437
+ return null;
438
+ }
439
+
440
+ // parse xml
441
+ svg.parseXml = function(xml) {
442
+ if (window.DOMParser)
443
+ {
444
+ var parser = new DOMParser();
445
+ return parser.parseFromString(xml, 'text/xml');
446
+ }
447
+ else
448
+ {
449
+ xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
450
+ var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
451
+ xmlDoc.async = 'false';
452
+ xmlDoc.loadXML(xml);
453
+ return xmlDoc;
454
+ }
455
+ }
456
+
457
+ svg.Property = function(name, value) {
458
+ this.name = name;
459
+ this.value = value;
460
+
461
+ this.hasValue = function() {
462
+ return (this.value != null && this.value !== '');
463
+ }
464
+
465
+ // return the numerical value of the property
466
+ this.numValue = function() {
467
+ if (!this.hasValue()) return 0;
468
+
469
+ var n = parseFloat(this.value);
470
+ if ((this.value + '').match(/%$/)) {
471
+ n = n / 100.0;
472
+ }
473
+ return n;
474
+ }
475
+
476
+ this.valueOrDefault = function(def) {
477
+ if (this.hasValue()) return this.value;
478
+ return def;
479
+ }
480
+
481
+ this.numValueOrDefault = function(def) {
482
+ if (this.hasValue()) return this.numValue();
483
+ return def;
484
+ }
485
+
486
+ /* EXTENSIONS */
487
+ var that = this;
488
+
489
+ // color extensions
490
+ this.Color = {
491
+ // augment the current color value with the opacity
492
+ addOpacity: function(opacity) {
493
+ var newValue = that.value;
494
+ if (opacity != null && opacity != '') {
495
+ var color = new RGBColor(that.value);
496
+ if (color.ok) {
497
+ newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')';
498
+ }
499
+ }
500
+ return new svg.Property(that.name, newValue);
501
+ }
502
+ }
503
+
504
+ // definition extensions
505
+ this.Definition = {
506
+ // get the definition from the definitions table
507
+ getDefinition: function() {
508
+ var name = that.value.replace(/^(url\()?#([^\)]+)\)?$/, '$2');
509
+ return svg.Definitions[name];
510
+ },
511
+
512
+ isUrl: function() {
513
+ return that.value.indexOf('url(') == 0
514
+ },
515
+
516
+ getFillStyle: function(e) {
517
+ var def = this.getDefinition();
518
+
519
+ // gradient
520
+ if (def != null && def.createGradient) {
521
+ return def.createGradient(svg.ctx, e);
522
+ }
523
+
524
+ // pattern
525
+ if (def != null && def.createPattern) {
526
+ return def.createPattern(svg.ctx, e);
527
+ }
528
+
529
+ return null;
530
+ }
531
+ }
532
+
533
+ // length extensions
534
+ this.Length = {
535
+ DPI: function(viewPort) {
536
+ return 96.0; // TODO: compute?
537
+ },
538
+
539
+ EM: function(viewPort) {
540
+ var em = 12;
541
+
542
+ var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
543
+ if (fontSize.hasValue()) em = fontSize.Length.toPixels(viewPort);
544
+
545
+ return em;
546
+ },
547
+
548
+ // get the length as pixels
549
+ toPixels: function(viewPort) {
550
+ if (!that.hasValue()) return 0;
551
+ var s = that.value+'';
552
+ if (s.match(/em$/)) return that.numValue() * this.EM(viewPort);
553
+ if (s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0;
554
+ if (s.match(/px$/)) return that.numValue();
555
+ if (s.match(/pt$/)) return that.numValue() * 1.25;
556
+ if (s.match(/pc$/)) return that.numValue() * 15;
557
+ if (s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54;
558
+ if (s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4;
559
+ if (s.match(/in$/)) return that.numValue() * this.DPI(viewPort);
560
+ if (s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort);
561
+ return that.numValue();
562
+ }
563
+ }
564
+
565
+ // time extensions
566
+ this.Time = {
567
+ // get the time as milliseconds
568
+ toMilliseconds: function() {
569
+ if (!that.hasValue()) return 0;
570
+ var s = that.value+'';
571
+ if (s.match(/s$/)) return that.numValue() * 1000;
572
+ if (s.match(/ms$/)) return that.numValue();
573
+ return that.numValue();
574
+ }
575
+ }
576
+
577
+ // angle extensions
578
+ this.Angle = {
579
+ // get the angle as radians
580
+ toRadians: function() {
581
+ if (!that.hasValue()) return 0;
582
+ var s = that.value+'';
583
+ if (s.match(/deg$/)) return that.numValue() * (Math.PI / 180.0);
584
+ if (s.match(/grad$/)) return that.numValue() * (Math.PI / 200.0);
585
+ if (s.match(/rad$/)) return that.numValue();
586
+ return that.numValue() * (Math.PI / 180.0);
587
+ }
588
+ }
589
+ }
590
+
591
+ // fonts
592
+ svg.Font = new (function() {
593
+ this.Styles = ['normal','italic','oblique','inherit'];
594
+ this.Variants = ['normal','small-caps','inherit'];
595
+ this.Weights = ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit'];
596
+
597
+ this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
598
+ var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
599
+ return {
600
+ fontFamily: fontFamily || f.fontFamily,
601
+ fontSize: fontSize || f.fontSize,
602
+ fontStyle: fontStyle || f.fontStyle,
603
+ fontWeight: fontWeight || f.fontWeight,
604
+ fontVariant: fontVariant || f.fontVariant,
605
+ toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
606
+ }
607
+ }
608
+
609
+ var that = this;
610
+ this.Parse = function(s) {
611
+ var f = {};
612
+ var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
613
+ var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
614
+ var ff = '';
615
+ for (var i=0; i<d.length; i++) {
616
+ if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
617
+ else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; }
618
+ else if (!set.fontWeight && that.Weights.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontWeight = d[i]; set.fontStyle = set.fontVariant = set.fontWeight = true; }
619
+ else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
620
+ else { if (d[i] != 'inherit') ff += d[i]; }
621
+ } if (ff != '') f.fontFamily = ff;
622
+ return f;
623
+ }
624
+ });
625
+
626
+ // points and paths
627
+ svg.ToNumberArray = function(s) {
628
+ var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
629
+ for (var i=0; i<a.length; i++) {
630
+ a[i] = parseFloat(a[i]);
631
+ }
632
+ return a;
633
+ }
634
+ svg.Point = function(x, y) {
635
+ this.x = x;
636
+ this.y = y;
637
+
638
+ this.angleTo = function(p) {
639
+ return Math.atan2(p.y - this.y, p.x - this.x);
640
+ }
641
+
642
+ this.applyTransform = function(v) {
643
+ var xp = this.x * v[0] + this.y * v[2] + v[4];
644
+ var yp = this.x * v[1] + this.y * v[3] + v[5];
645
+ this.x = xp;
646
+ this.y = yp;
647
+ }
648
+ }
649
+ svg.CreatePoint = function(s) {
650
+ var a = svg.ToNumberArray(s);
651
+ return new svg.Point(a[0], a[1]);
652
+ }
653
+ svg.CreatePath = function(s) {
654
+ var a = svg.ToNumberArray(s);
655
+ var path = [];
656
+ for (var i=0; i<a.length; i+=2) {
657
+ path.push(new svg.Point(a[i], a[i+1]));
658
+ }
659
+ return path;
660
+ }
661
+
662
+ // bounding box
663
+ svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want
664
+ this.x1 = Number.NaN;
665
+ this.y1 = Number.NaN;
666
+ this.x2 = Number.NaN;
667
+ this.y2 = Number.NaN;
668
+
669
+ this.x = function() { return this.x1; }
670
+ this.y = function() { return this.y1; }
671
+ this.width = function() { return this.x2 - this.x1; }
672
+ this.height = function() { return this.y2 - this.y1; }
673
+
674
+ this.addPoint = function(x, y) {
675
+ if (x != null) {
676
+ if (isNaN(this.x1) || isNaN(this.x2)) {
677
+ this.x1 = x;
678
+ this.x2 = x;
679
+ }
680
+ if (x < this.x1) this.x1 = x;
681
+ if (x > this.x2) this.x2 = x;
682
+ }
683
+
684
+ if (y != null) {
685
+ if (isNaN(this.y1) || isNaN(this.y2)) {
686
+ this.y1 = y;
687
+ this.y2 = y;
688
+ }
689
+ if (y < this.y1) this.y1 = y;
690
+ if (y > this.y2) this.y2 = y;
691
+ }
692
+ }
693
+ this.addX = function(x) { this.addPoint(x, null); }
694
+ this.addY = function(y) { this.addPoint(null, y); }
695
+
696
+ this.addBoundingBox = function(bb) {
697
+ this.addPoint(bb.x1, bb.y1);
698
+ this.addPoint(bb.x2, bb.y2);
699
+ }
700
+
701
+ this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
702
+ var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
703
+ var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
704
+ var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
705
+ var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
706
+ this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
707
+ }
708
+
709
+ this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
710
+ // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
711
+ var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
712
+ this.addPoint(p0[0], p0[1]);
713
+ this.addPoint(p3[0], p3[1]);
714
+
715
+ for (i=0; i<=1; i++) {
716
+ var f = function(t) {
717
+ return Math.pow(1-t, 3) * p0[i]
718
+ + 3 * Math.pow(1-t, 2) * t * p1[i]
719
+ + 3 * (1-t) * Math.pow(t, 2) * p2[i]
720
+ + Math.pow(t, 3) * p3[i];
721
+ }
722
+
723
+ var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
724
+ var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
725
+ var c = 3 * p1[i] - 3 * p0[i];
726
+
727
+ if (a == 0) {
728
+ if (b == 0) continue;
729
+ var t = -c / b;
730
+ if (0 < t && t < 1) {
731
+ if (i == 0) this.addX(f(t));
732
+ if (i == 1) this.addY(f(t));
733
+ }
734
+ continue;
735
+ }
736
+
737
+ var b2ac = Math.pow(b, 2) - 4 * c * a;
738
+ if (b2ac < 0) continue;
739
+ var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
740
+ if (0 < t1 && t1 < 1) {
741
+ if (i == 0) this.addX(f(t1));
742
+ if (i == 1) this.addY(f(t1));
743
+ }
744
+ var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
745
+ if (0 < t2 && t2 < 1) {
746
+ if (i == 0) this.addX(f(t2));
747
+ if (i == 1) this.addY(f(t2));
748
+ }
749
+ }
750
+ }
751
+
752
+ this.isPointInBox = function(x, y) {
753
+ return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
754
+ }
755
+
756
+ this.addPoint(x1, y1);
757
+ this.addPoint(x2, y2);
758
+ }
759
+
760
+ // transforms
761
+ svg.Transform = function(v) {
762
+ var that = this;
763
+ this.Type = {}
764
+
765
+ // translate
766
+ this.Type.translate = function(s) {
767
+ this.p = svg.CreatePoint(s);
768
+ this.apply = function(ctx) {
769
+ ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
770
+ }
771
+ this.applyToPoint = function(p) {
772
+ p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
773
+ }
774
+ }
775
+
776
+ // rotate
777
+ this.Type.rotate = function(s) {
778
+ var a = svg.ToNumberArray(s);
779
+ this.angle = new svg.Property('angle', a[0]);
780
+ this.cx = a[1] || 0;
781
+ this.cy = a[2] || 0;
782
+ this.apply = function(ctx) {
783
+ ctx.translate(this.cx, this.cy);
784
+ ctx.rotate(this.angle.Angle.toRadians());
785
+ ctx.translate(-this.cx, -this.cy);
786
+ }
787
+ this.applyToPoint = function(p) {
788
+ var a = this.angle.Angle.toRadians();
789
+ p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
790
+ p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
791
+ p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
792
+ }
793
+ }
794
+
795
+ this.Type.scale = function(s) {
796
+ this.p = svg.CreatePoint(s);
797
+ this.apply = function(ctx) {
798
+ ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
799
+ }
800
+ this.applyToPoint = function(p) {
801
+ p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
802
+ }
803
+ }
804
+
805
+ this.Type.matrix = function(s) {
806
+ this.m = svg.ToNumberArray(s);
807
+ this.apply = function(ctx) {
808
+ ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
809
+ }
810
+ this.applyToPoint = function(p) {
811
+ p.applyTransform(this.m);
812
+ }
813
+ }
814
+
815
+ this.Type.SkewBase = function(s) {
816
+ this.base = that.Type.matrix;
817
+ this.base(s);
818
+ this.angle = new svg.Property('angle', s);
819
+ }
820
+ this.Type.SkewBase.prototype = new this.Type.matrix;
821
+
822
+ this.Type.skewX = function(s) {
823
+ this.base = that.Type.SkewBase;
824
+ this.base(s);
825
+ this.m = [1, 0, Math.tan(this.angle.Angle.toRadians()), 1, 0, 0];
826
+ }
827
+ this.Type.skewX.prototype = new this.Type.SkewBase;
828
+
829
+ this.Type.skewY = function(s) {
830
+ this.base = that.Type.SkewBase;
831
+ this.base(s);
832
+ this.m = [1, Math.tan(this.angle.Angle.toRadians()), 0, 1, 0, 0];
833
+ }
834
+ this.Type.skewY.prototype = new this.Type.SkewBase;
835
+
836
+ this.transforms = [];
837
+
838
+ this.apply = function(ctx) {
839
+ for (var i=0; i<this.transforms.length; i++) {
840
+ this.transforms[i].apply(ctx);
841
+ }
842
+ }
843
+
844
+ this.applyToPoint = function(p) {
845
+ for (var i=0; i<this.transforms.length; i++) {
846
+ this.transforms[i].applyToPoint(p);
847
+ }
848
+ }
849
+
850
+ var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/);
851
+ for (var i=0; i<data.length; i++) {
852
+ var type = data[i].split('(')[0];
853
+ var s = data[i].split('(')[1].replace(')','');
854
+ var transform = new this.Type[type](s);
855
+ this.transforms.push(transform);
856
+ }
857
+ }
858
+
859
+ // aspect ratio
860
+ svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
861
+ // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
862
+ aspectRatio = svg.compressSpaces(aspectRatio);
863
+ aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer
864
+ var align = aspectRatio.split(' ')[0] || 'xMidYMid';
865
+ var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';
866
+
867
+ // calculate scale
868
+ var scaleX = width / desiredWidth;
869
+ var scaleY = height / desiredHeight;
870
+ var scaleMin = Math.min(scaleX, scaleY);
871
+ var scaleMax = Math.max(scaleX, scaleY);
872
+ if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
873
+ if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }
874
+
875
+ refX = new svg.Property('refX', refX);
876
+ refY = new svg.Property('refY', refY);
877
+ if (refX.hasValue() && refY.hasValue()) {
878
+ ctx.translate(-scaleMin * refX.Length.toPixels('x'), -scaleMin * refY.Length.toPixels('y'));
879
+ }
880
+ else {
881
+ // align
882
+ if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0);
883
+ if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0);
884
+ if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0);
885
+ if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight);
886
+ }
887
+
888
+ // scale
889
+ if (align == 'none') ctx.scale(scaleX, scaleY);
890
+ else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin);
891
+ else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax);
892
+
893
+ // translate
894
+ ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);
895
+ }
896
+
897
+ // elements
898
+ svg.Element = {}
899
+
900
+ svg.Element.ElementBase = function(node) {
901
+ this.attributes = {};
902
+ this.styles = {};
903
+ this.children = [];
904
+
905
+ // get or create attribute
906
+ this.attribute = function(name, createIfNotExists) {
907
+ var a = this.attributes[name];
908
+ if (a != null) return a;
909
+
910
+ a = new svg.Property(name, '');
911
+ if (createIfNotExists == true) this.attributes[name] = a;
912
+ return a;
913
+ }
914
+
915
+ // get or create style, crawls up node tree
916
+ this.style = function(name, createIfNotExists) {
917
+ var s = this.styles[name];
918
+ if (s != null) return s;
919
+
920
+ var a = this.attribute(name);
921
+ if (a != null && a.hasValue()) {
922
+ return a;
923
+ }
924
+
925
+ var p = this.parent;
926
+ if (p != null) {
927
+ var ps = p.style(name);
928
+ if (ps != null && ps.hasValue()) {
929
+ return ps;
930
+ }
931
+ }
932
+
933
+ s = new svg.Property(name, '');
934
+ if (createIfNotExists == true) this.styles[name] = s;
935
+ return s;
936
+ }
937
+
938
+ // base render
939
+ this.render = function(ctx) {
940
+ // don't render display=none
941
+ if (this.style('display').value == 'none') return;
942
+
943
+ // don't render visibility=hidden
944
+ if (this.attribute('visibility').value == 'hidden') return;
945
+
946
+ ctx.save();
947
+ this.setContext(ctx);
948
+ // mask
949
+ if (this.attribute('mask').hasValue()) {
950
+ var mask = this.attribute('mask').Definition.getDefinition();
951
+ if (mask != null) mask.apply(ctx, this);
952
+ }
953
+ else if (this.style('filter').hasValue()) {
954
+ var filter = this.style('filter').Definition.getDefinition();
955
+ if (filter != null) filter.apply(ctx, this);
956
+ }
957
+ else this.renderChildren(ctx);
958
+ this.clearContext(ctx);
959
+ ctx.restore();
960
+ }
961
+
962
+ // base set context
963
+ this.setContext = function(ctx) {
964
+ // OVERRIDE ME!
965
+ }
966
+
967
+ // base clear context
968
+ this.clearContext = function(ctx) {
969
+ // OVERRIDE ME!
970
+ }
971
+
972
+ // base render children
973
+ this.renderChildren = function(ctx) {
974
+ for (var i=0; i<this.children.length; i++) {
975
+ this.children[i].render(ctx);
976
+ }
977
+ }
978
+
979
+ this.addChild = function(childNode, create) {
980
+ var child = childNode;
981
+ if (create) child = svg.CreateElement(childNode);
982
+ child.parent = this;
983
+ this.children.push(child);
984
+ }
985
+
986
+ if (node != null && node.nodeType == 1) { //ELEMENT_NODE
987
+ // add children
988
+ for (var i=0; i<node.childNodes.length; i++) {
989
+ var childNode = node.childNodes[i];
990
+ if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
991
+ }
992
+
993
+ // add attributes
994
+ for (var i=0; i<node.attributes.length; i++) {
995
+ var attribute = node.attributes[i];
996
+ this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue);
997
+ }
998
+
999
+ // add tag styles
1000
+ var styles = svg.Styles[node.nodeName];
1001
+ if (styles != null) {
1002
+ for (var name in styles) {
1003
+ this.styles[name] = styles[name];
1004
+ }
1005
+ }
1006
+
1007
+ // add class styles
1008
+ if (this.attribute('class').hasValue()) {
1009
+ var classes = svg.compressSpaces(this.attribute('class').value).split(' ');
1010
+ for (var j=0; j<classes.length; j++) {
1011
+ styles = svg.Styles['.'+classes[j]];
1012
+ if (styles != null) {
1013
+ for (var name in styles) {
1014
+ this.styles[name] = styles[name];
1015
+ }
1016
+ }
1017
+ styles = svg.Styles[node.nodeName+'.'+classes[j]];
1018
+ if (styles != null) {
1019
+ for (var name in styles) {
1020
+ this.styles[name] = styles[name];
1021
+ }
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+ // add inline styles
1027
+ if (this.attribute('style').hasValue()) {
1028
+ var styles = this.attribute('style').value.split(';');
1029
+ for (var i=0; i<styles.length; i++) {
1030
+ if (svg.trim(styles[i]) != '') {
1031
+ var style = styles[i].split(':');
1032
+ var name = svg.trim(style[0]);
1033
+ var value = svg.trim(style[1]);
1034
+ this.styles[name] = new svg.Property(name, value);
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ // add id
1040
+ if (this.attribute('id').hasValue()) {
1041
+ if (svg.Definitions[this.attribute('id').value] == null) {
1042
+ svg.Definitions[this.attribute('id').value] = this;
1043
+ }
1044
+ }
1045
+ }
1046
+ }
1047
+
1048
+ svg.Element.RenderedElementBase = function(node) {
1049
+ this.base = svg.Element.ElementBase;
1050
+ this.base(node);
1051
+
1052
+ this.setContext = function(ctx) {
1053
+ // fill
1054
+ if (this.style('fill').Definition.isUrl()) {
1055
+ var fs = this.style('fill').Definition.getFillStyle(this);
1056
+ if (fs != null) ctx.fillStyle = fs;
1057
+ }
1058
+ else if (this.style('fill').hasValue()) {
1059
+ var fillStyle = this.style('fill');
1060
+ if (this.style('fill-opacity').hasValue()) fillStyle = fillStyle.Color.addOpacity(this.style('fill-opacity').value);
1061
+ ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
1062
+ }
1063
+
1064
+ // stroke
1065
+ if (this.style('stroke').Definition.isUrl()) {
1066
+ var fs = this.style('stroke').Definition.getFillStyle(this);
1067
+ if (fs != null) ctx.strokeStyle = fs;
1068
+ }
1069
+ else if (this.style('stroke').hasValue()) {
1070
+ var strokeStyle = this.style('stroke');
1071
+ if (this.style('stroke-opacity').hasValue()) strokeStyle = strokeStyle.Color.addOpacity(this.style('stroke-opacity').value);
1072
+ ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
1073
+ }
1074
+ if (this.style('stroke-width').hasValue()) ctx.lineWidth = this.style('stroke-width').Length.toPixels();
1075
+ if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
1076
+ if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
1077
+ if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
1078
+
1079
+ // font
1080
+ if (typeof(ctx.font) != 'undefined') {
1081
+ ctx.font = svg.Font.CreateFont(
1082
+ this.style('font-style').value,
1083
+ this.style('font-variant').value,
1084
+ this.style('font-weight').value,
1085
+ this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' : '',
1086
+ this.style('font-family').value).toString();
1087
+ }
1088
+
1089
+ // transform
1090
+ if (this.attribute('transform').hasValue()) {
1091
+ var transform = new svg.Transform(this.attribute('transform').value);
1092
+ transform.apply(ctx);
1093
+ }
1094
+
1095
+ // clip
1096
+ if (this.attribute('clip-path').hasValue()) {
1097
+ var clip = this.attribute('clip-path').Definition.getDefinition();
1098
+ if (clip != null) clip.apply(ctx);
1099
+ }
1100
+
1101
+ // opacity
1102
+ if (this.style('opacity').hasValue()) {
1103
+ ctx.globalAlpha = this.style('opacity').numValue();
1104
+ }
1105
+ }
1106
+ }
1107
+ svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
1108
+
1109
+ svg.Element.PathElementBase = function(node) {
1110
+ this.base = svg.Element.RenderedElementBase;
1111
+ this.base(node);
1112
+
1113
+ this.path = function(ctx) {
1114
+ if (ctx != null) ctx.beginPath();
1115
+ return new svg.BoundingBox();
1116
+ }
1117
+
1118
+ this.renderChildren = function(ctx) {
1119
+ this.path(ctx);
1120
+ svg.Mouse.checkPath(this, ctx);
1121
+ if (ctx.fillStyle != '') ctx.fill();
1122
+ if (ctx.strokeStyle != '') ctx.stroke();
1123
+
1124
+ var markers = this.getMarkers();
1125
+ if (markers != null) {
1126
+ if (this.style('marker-start').Definition.isUrl()) {
1127
+ var marker = this.style('marker-start').Definition.getDefinition();
1128
+ marker.render(ctx, markers[0][0], markers[0][1]);
1129
+ }
1130
+ if (this.style('marker-mid').Definition.isUrl()) {
1131
+ var marker = this.style('marker-mid').Definition.getDefinition();
1132
+ for (var i=1;i<markers.length-1;i++) {
1133
+ marker.render(ctx, markers[i][0], markers[i][1]);
1134
+ }
1135
+ }
1136
+ if (this.style('marker-end').Definition.isUrl()) {
1137
+ var marker = this.style('marker-end').Definition.getDefinition();
1138
+ marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]);
1139
+ }
1140
+ }
1141
+ }
1142
+
1143
+ this.getBoundingBox = function() {
1144
+ return this.path();
1145
+ }
1146
+
1147
+ this.getMarkers = function() {
1148
+ return null;
1149
+ }
1150
+ }
1151
+ svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
1152
+
1153
+ // svg element
1154
+ svg.Element.svg = function(node) {
1155
+ this.base = svg.Element.RenderedElementBase;
1156
+ this.base(node);
1157
+
1158
+ this.baseClearContext = this.clearContext;
1159
+ this.clearContext = function(ctx) {
1160
+ this.baseClearContext(ctx);
1161
+ svg.ViewPort.RemoveCurrent();
1162
+ }
1163
+
1164
+ this.baseSetContext = this.setContext;
1165
+ this.setContext = function(ctx) {
1166
+ // initial values
1167
+ ctx.strokeStyle = 'rgba(0,0,0,0)';
1168
+ ctx.lineCap = 'butt';
1169
+ ctx.lineJoin = 'miter';
1170
+ ctx.miterLimit = 4;
1171
+
1172
+ this.baseSetContext(ctx);
1173
+
1174
+ // create new view port
1175
+ if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
1176
+ ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y'));
1177
+ }
1178
+
1179
+ var width = svg.ViewPort.width();
1180
+ var height = svg.ViewPort.height();
1181
+ if (typeof(this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
1182
+ width = this.attribute('width').Length.toPixels('x');
1183
+ height = this.attribute('height').Length.toPixels('y');
1184
+
1185
+ var x = 0;
1186
+ var y = 0;
1187
+ if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
1188
+ x = -this.attribute('refX').Length.toPixels('x');
1189
+ y = -this.attribute('refY').Length.toPixels('y');
1190
+ }
1191
+
1192
+ ctx.beginPath();
1193
+ ctx.moveTo(x, y);
1194
+ ctx.lineTo(width, y);
1195
+ ctx.lineTo(width, height);
1196
+ ctx.lineTo(x, height);
1197
+ ctx.closePath();
1198
+ ctx.clip();
1199
+ }
1200
+ svg.ViewPort.SetCurrent(width, height);
1201
+
1202
+ // viewbox
1203
+ if (this.attribute('viewBox').hasValue()) {
1204
+ var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
1205
+ var minX = viewBox[0];
1206
+ var minY = viewBox[1];
1207
+ width = viewBox[2];
1208
+ height = viewBox[3];
1209
+
1210
+ svg.AspectRatio(ctx,
1211
+ this.attribute('preserveAspectRatio').value,
1212
+ svg.ViewPort.width(),
1213
+ width,
1214
+ svg.ViewPort.height(),
1215
+ height,
1216
+ minX,
1217
+ minY,
1218
+ this.attribute('refX').value,
1219
+ this.attribute('refY').value);
1220
+
1221
+ svg.ViewPort.RemoveCurrent();
1222
+ svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
1223
+ }
1224
+ }
1225
+ }
1226
+ svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
1227
+
1228
+ // rect element
1229
+ svg.Element.rect = function(node) {
1230
+ this.base = svg.Element.PathElementBase;
1231
+ this.base(node);
1232
+
1233
+ this.path = function(ctx) {
1234
+ var x = this.attribute('x').Length.toPixels('x');
1235
+ var y = this.attribute('y').Length.toPixels('y');
1236
+ var width = this.attribute('width').Length.toPixels('x');
1237
+ var height = this.attribute('height').Length.toPixels('y');
1238
+ var rx = this.attribute('rx').Length.toPixels('x');
1239
+ var ry = this.attribute('ry').Length.toPixels('y');
1240
+ if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
1241
+ if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
1242
+
1243
+ if (ctx != null) {
1244
+ ctx.beginPath();
1245
+ ctx.moveTo(x + rx, y);
1246
+ ctx.lineTo(x + width - rx, y);
1247
+ ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
1248
+ ctx.lineTo(x + width, y + height - ry);
1249
+ ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
1250
+ ctx.lineTo(x + rx, y + height);
1251
+ ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
1252
+ ctx.lineTo(x, y + ry);
1253
+ ctx.quadraticCurveTo(x, y, x + rx, y)
1254
+ ctx.closePath();
1255
+ }
1256
+
1257
+ return new svg.BoundingBox(x, y, x + width, y + height);
1258
+ }
1259
+ }
1260
+ svg.Element.rect.prototype = new svg.Element.PathElementBase;
1261
+
1262
+ // circle element
1263
+ svg.Element.circle = function(node) {
1264
+ this.base = svg.Element.PathElementBase;
1265
+ this.base(node);
1266
+
1267
+ this.path = function(ctx) {
1268
+ var cx = this.attribute('cx').Length.toPixels('x');
1269
+ var cy = this.attribute('cy').Length.toPixels('y');
1270
+ var r = this.attribute('r').Length.toPixels();
1271
+
1272
+ if (ctx != null) {
1273
+ ctx.beginPath();
1274
+ ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
1275
+ ctx.closePath();
1276
+ }
1277
+
1278
+ return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
1279
+ }
1280
+ }
1281
+ svg.Element.circle.prototype = new svg.Element.PathElementBase;
1282
+
1283
+ // ellipse element
1284
+ svg.Element.ellipse = function(node) {
1285
+ this.base = svg.Element.PathElementBase;
1286
+ this.base(node);
1287
+
1288
+ this.path = function(ctx) {
1289
+ var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
1290
+ var rx = this.attribute('rx').Length.toPixels('x');
1291
+ var ry = this.attribute('ry').Length.toPixels('y');
1292
+ var cx = this.attribute('cx').Length.toPixels('x');
1293
+ var cy = this.attribute('cy').Length.toPixels('y');
1294
+
1295
+ if (ctx != null) {
1296
+ ctx.beginPath();
1297
+ ctx.moveTo(cx, cy - ry);
1298
+ ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy);
1299
+ ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
1300
+ ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
1301
+ ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
1302
+ ctx.closePath();
1303
+ }
1304
+
1305
+ return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
1306
+ }
1307
+ }
1308
+ svg.Element.ellipse.prototype = new svg.Element.PathElementBase;
1309
+
1310
+ // line element
1311
+ svg.Element.line = function(node) {
1312
+ this.base = svg.Element.PathElementBase;
1313
+ this.base(node);
1314
+
1315
+ this.getPoints = function() {
1316
+ return [
1317
+ new svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')),
1318
+ new svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))];
1319
+ }
1320
+
1321
+ this.path = function(ctx) {
1322
+ var points = this.getPoints();
1323
+
1324
+ if (ctx != null) {
1325
+ ctx.beginPath();
1326
+ ctx.moveTo(points[0].x, points[0].y);
1327
+ ctx.lineTo(points[1].x, points[1].y);
1328
+ }
1329
+
1330
+ return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
1331
+ }
1332
+
1333
+ this.getMarkers = function() {
1334
+ var points = this.getPoints();
1335
+ var a = points[0].angleTo(points[1]);
1336
+ return [[points[0], a], [points[1], a]];
1337
+ }
1338
+ }
1339
+ svg.Element.line.prototype = new svg.Element.PathElementBase;
1340
+
1341
+ // polyline element
1342
+ svg.Element.polyline = function(node) {
1343
+ this.base = svg.Element.PathElementBase;
1344
+ this.base(node);
1345
+
1346
+ this.points = svg.CreatePath(this.attribute('points').value);
1347
+ this.path = function(ctx) {
1348
+ var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
1349
+ if (ctx != null) {
1350
+ ctx.beginPath();
1351
+ ctx.moveTo(this.points[0].x, this.points[0].y);
1352
+ }
1353
+ for (var i=1; i<this.points.length; i++) {
1354
+ bb.addPoint(this.points[i].x, this.points[i].y);
1355
+ if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
1356
+ }
1357
+ return bb;
1358
+ }
1359
+
1360
+ this.getMarkers = function() {
1361
+ var markers = [];
1362
+ for (var i=0; i<this.points.length - 1; i++) {
1363
+ markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
1364
+ }
1365
+ markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
1366
+ return markers;
1367
+ }
1368
+ }
1369
+ svg.Element.polyline.prototype = new svg.Element.PathElementBase;
1370
+
1371
+ // polygon element
1372
+ svg.Element.polygon = function(node) {
1373
+ this.base = svg.Element.polyline;
1374
+ this.base(node);
1375
+
1376
+ this.basePath = this.path;
1377
+ this.path = function(ctx) {
1378
+ var bb = this.basePath(ctx);
1379
+ if (ctx != null) {
1380
+ ctx.lineTo(this.points[0].x, this.points[0].y);
1381
+ ctx.closePath();
1382
+ }
1383
+ return bb;
1384
+ }
1385
+ }
1386
+ svg.Element.polygon.prototype = new svg.Element.polyline;
1387
+
1388
+ // path element
1389
+ svg.Element.path = function(node) {
1390
+ this.base = svg.Element.PathElementBase;
1391
+ this.base(node);
1392
+
1393
+ var d = this.attribute('d').value;
1394
+ // TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
1395
+ d = d.replace(/,/gm,' '); // get rid of all commas
1396
+ d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
1397
+ d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
1398
+ d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points
1399
+ d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
1400
+ d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma
1401
+ d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma
1402
+ d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
1403
+ d = svg.compressSpaces(d); // compress multiple spaces
1404
+ d = svg.trim(d);
1405
+ this.PathParser = new (function(d) {
1406
+ this.tokens = d.split(' ');
1407
+
1408
+ this.reset = function() {
1409
+ this.i = -1;
1410
+ this.command = '';
1411
+ this.previousCommand = '';
1412
+ this.start = new svg.Point(0, 0);
1413
+ this.control = new svg.Point(0, 0);
1414
+ this.current = new svg.Point(0, 0);
1415
+ this.points = [];
1416
+ this.angles = [];
1417
+ }
1418
+
1419
+ this.isEnd = function() {
1420
+ return this.i >= this.tokens.length - 1;
1421
+ }
1422
+
1423
+ this.isCommandOrEnd = function() {
1424
+ if (this.isEnd()) return true;
1425
+ return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
1426
+ }
1427
+
1428
+ this.isRelativeCommand = function() {
1429
+ return this.command == this.command.toLowerCase();
1430
+ }
1431
+
1432
+ this.getToken = function() {
1433
+ this.i = this.i + 1;
1434
+ return this.tokens[this.i];
1435
+ }
1436
+
1437
+ this.getScalar = function() {
1438
+ return parseFloat(this.getToken());
1439
+ }
1440
+
1441
+ this.nextCommand = function() {
1442
+ this.previousCommand = this.command;
1443
+ this.command = this.getToken();
1444
+ }
1445
+
1446
+ this.getPoint = function() {
1447
+ var p = new svg.Point(this.getScalar(), this.getScalar());
1448
+ return this.makeAbsolute(p);
1449
+ }
1450
+
1451
+ this.getAsControlPoint = function() {
1452
+ var p = this.getPoint();
1453
+ this.control = p;
1454
+ return p;
1455
+ }
1456
+
1457
+ this.getAsCurrentPoint = function() {
1458
+ var p = this.getPoint();
1459
+ this.current = p;
1460
+ return p;
1461
+ }
1462
+
1463
+ this.getReflectedControlPoint = function() {
1464
+ if (this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') {
1465
+ return this.current;
1466
+ }
1467
+
1468
+ // reflect point
1469
+ var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
1470
+ return p;
1471
+ }
1472
+
1473
+ this.makeAbsolute = function(p) {
1474
+ if (this.isRelativeCommand()) {
1475
+ p.x = this.current.x + p.x;
1476
+ p.y = this.current.y + p.y;
1477
+ }
1478
+ return p;
1479
+ }
1480
+
1481
+ this.addMarker = function(p, from, priorTo) {
1482
+ // if the last angle isn't filled in because we didn't have this point yet ...
1483
+ if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
1484
+ this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
1485
+ }
1486
+ this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
1487
+ }
1488
+
1489
+ this.addMarkerAngle = function(p, a) {
1490
+ this.points.push(p);
1491
+ this.angles.push(a);
1492
+ }
1493
+
1494
+ this.getMarkerPoints = function() { return this.points; }
1495
+ this.getMarkerAngles = function() {
1496
+ for (var i=0; i<this.angles.length; i++) {
1497
+ if (this.angles[i] == null) {
1498
+ for (var j=i+1; j<this.angles.length; j++) {
1499
+ if (this.angles[j] != null) {
1500
+ this.angles[i] = this.angles[j];
1501
+ break;
1502
+ }
1503
+ }
1504
+ }
1505
+ }
1506
+ return this.angles;
1507
+ }
1508
+ })(d);
1509
+
1510
+ this.path = function(ctx) {
1511
+ var pp = this.PathParser;
1512
+ pp.reset();
1513
+
1514
+ var bb = new svg.BoundingBox();
1515
+ if (ctx != null) ctx.beginPath();
1516
+ while (!pp.isEnd()) {
1517
+ pp.nextCommand();
1518
+ switch (pp.command.toUpperCase()) {
1519
+ case 'M':
1520
+ var p = pp.getAsCurrentPoint();
1521
+ pp.addMarker(p);
1522
+ bb.addPoint(p.x, p.y);
1523
+ if (ctx != null) ctx.moveTo(p.x, p.y);
1524
+ pp.start = pp.current;
1525
+ while (!pp.isCommandOrEnd()) {
1526
+ var p = pp.getAsCurrentPoint();
1527
+ pp.addMarker(p, pp.start);
1528
+ bb.addPoint(p.x, p.y);
1529
+ if (ctx != null) ctx.lineTo(p.x, p.y);
1530
+ }
1531
+ break;
1532
+ case 'L':
1533
+ while (!pp.isCommandOrEnd()) {
1534
+ var c = pp.current;
1535
+ var p = pp.getAsCurrentPoint();
1536
+ pp.addMarker(p, c);
1537
+ bb.addPoint(p.x, p.y);
1538
+ if (ctx != null) ctx.lineTo(p.x, p.y);
1539
+ }
1540
+ break;
1541
+ case 'H':
1542
+ while (!pp.isCommandOrEnd()) {
1543
+ var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
1544
+ pp.addMarker(newP, pp.current);
1545
+ pp.current = newP;
1546
+ bb.addPoint(pp.current.x, pp.current.y);
1547
+ if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
1548
+ }
1549
+ break;
1550
+ case 'V':
1551
+ while (!pp.isCommandOrEnd()) {
1552
+ var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
1553
+ pp.addMarker(newP, pp.current);
1554
+ pp.current = newP;
1555
+ bb.addPoint(pp.current.x, pp.current.y);
1556
+ if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
1557
+ }
1558
+ break;
1559
+ case 'C':
1560
+ while (!pp.isCommandOrEnd()) {
1561
+ var curr = pp.current;
1562
+ var p1 = pp.getPoint();
1563
+ var cntrl = pp.getAsControlPoint();
1564
+ var cp = pp.getAsCurrentPoint();
1565
+ pp.addMarker(cp, cntrl, p1);
1566
+ bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
1567
+ if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
1568
+ }
1569
+ break;
1570
+ case 'S':
1571
+ while (!pp.isCommandOrEnd()) {
1572
+ var curr = pp.current;
1573
+ var p1 = pp.getReflectedControlPoint();
1574
+ var cntrl = pp.getAsControlPoint();
1575
+ var cp = pp.getAsCurrentPoint();
1576
+ pp.addMarker(cp, cntrl, p1);
1577
+ bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
1578
+ if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
1579
+ }
1580
+ break;
1581
+ case 'Q':
1582
+ while (!pp.isCommandOrEnd()) {
1583
+ var curr = pp.current;
1584
+ var cntrl = pp.getAsControlPoint();
1585
+ var cp = pp.getAsCurrentPoint();
1586
+ pp.addMarker(cp, cntrl, cntrl);
1587
+ bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
1588
+ if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
1589
+ }
1590
+ break;
1591
+ case 'T':
1592
+ while (!pp.isCommandOrEnd()) {
1593
+ var curr = pp.current;
1594
+ var cntrl = pp.getReflectedControlPoint();
1595
+ pp.control = cntrl;
1596
+ var cp = pp.getAsCurrentPoint();
1597
+ pp.addMarker(cp, cntrl, cntrl);
1598
+ bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
1599
+ if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
1600
+ }
1601
+ break;
1602
+ case 'A':
1603
+ while (!pp.isCommandOrEnd()) {
1604
+ var curr = pp.current;
1605
+ var rx = pp.getScalar();
1606
+ var ry = pp.getScalar();
1607
+ var xAxisRotation = pp.getScalar() * (Math.PI / 180.0);
1608
+ var largeArcFlag = pp.getScalar();
1609
+ var sweepFlag = pp.getScalar();
1610
+ var cp = pp.getAsCurrentPoint();
1611
+
1612
+ // Conversion from endpoint to center parameterization
1613
+ // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1614
+ // x1', y1'
1615
+ var currp = new svg.Point(
1616
+ Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0,
1617
+ -Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0
1618
+ );
1619
+ // adjust radii
1620
+ var l = Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2);
1621
+ if (l > 1) {
1622
+ rx *= Math.sqrt(l);
1623
+ ry *= Math.sqrt(l);
1624
+ }
1625
+ // cx', cy'
1626
+ var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
1627
+ ((Math.pow(rx,2)*Math.pow(ry,2))-(Math.pow(rx,2)*Math.pow(currp.y,2))-(Math.pow(ry,2)*Math.pow(currp.x,2))) /
1628
+ (Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
1629
+ );
1630
+ if (isNaN(s)) s = 0;
1631
+ var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
1632
+ // cx, cy
1633
+ var centp = new svg.Point(
1634
+ (curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
1635
+ (curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
1636
+ );
1637
+ // vector magnitude
1638
+ var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
1639
+ // ratio between two vectors
1640
+ var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
1641
+ // angle between two vectors
1642
+ var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); }
1643
+ // initial angle
1644
+ var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
1645
+ // angle delta
1646
+ var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
1647
+ var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
1648
+ var ad = a(u, v);
1649
+ if (r(u,v) <= -1) ad = Math.PI;
1650
+ if (r(u,v) >= 1) ad = 0;
1651
+
1652
+ if (sweepFlag == 0 && ad > 0) ad = ad - 2 * Math.PI;
1653
+ if (sweepFlag == 1 && ad < 0) ad = ad + 2 * Math.PI;
1654
+
1655
+ // for markers
1656
+ var halfWay = new svg.Point(
1657
+ centp.x - rx * Math.cos((a1 + ad) / 2),
1658
+ centp.y - ry * Math.sin((a1 + ad) / 2)
1659
+ );
1660
+ pp.addMarkerAngle(halfWay, (a1 + ad) / 2 + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
1661
+ pp.addMarkerAngle(cp, ad + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
1662
+
1663
+ bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
1664
+ if (ctx != null) {
1665
+ var r = rx > ry ? rx : ry;
1666
+ var sx = rx > ry ? 1 : rx / ry;
1667
+ var sy = rx > ry ? ry / rx : 1;
1668
+
1669
+ ctx.translate(centp.x, centp.y);
1670
+ ctx.rotate(xAxisRotation);
1671
+ ctx.scale(sx, sy);
1672
+ ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
1673
+ ctx.scale(1/sx, 1/sy);
1674
+ ctx.rotate(-xAxisRotation);
1675
+ ctx.translate(-centp.x, -centp.y);
1676
+ }
1677
+ }
1678
+ break;
1679
+ case 'Z':
1680
+ if (ctx != null) ctx.closePath();
1681
+ pp.current = pp.start;
1682
+ }
1683
+ }
1684
+
1685
+ return bb;
1686
+ }
1687
+
1688
+ this.getMarkers = function() {
1689
+ var points = this.PathParser.getMarkerPoints();
1690
+ var angles = this.PathParser.getMarkerAngles();
1691
+
1692
+ var markers = [];
1693
+ for (var i=0; i<points.length; i++) {
1694
+ markers.push([points[i], angles[i]]);
1695
+ }
1696
+ return markers;
1697
+ }
1698
+ }
1699
+ svg.Element.path.prototype = new svg.Element.PathElementBase;
1700
+
1701
+ // pattern element
1702
+ svg.Element.pattern = function(node) {
1703
+ this.base = svg.Element.ElementBase;
1704
+ this.base(node);
1705
+
1706
+ this.createPattern = function(ctx, element) {
1707
+ // render me using a temporary svg element
1708
+ var tempSvg = new svg.Element.svg();
1709
+ tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
1710
+ tempSvg.attributes['x'] = new svg.Property('x', this.attribute('x').value);
1711
+ tempSvg.attributes['y'] = new svg.Property('y', this.attribute('y').value);
1712
+ tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value);
1713
+ tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value);
1714
+ tempSvg.children = this.children;
1715
+
1716
+ var c = document.createElement('canvas');
1717
+ c.width = this.attribute('width').Length.toPixels('x');
1718
+ c.height = this.attribute('height').Length.toPixels('y');
1719
+ tempSvg.render(c.getContext('2d'));
1720
+ return ctx.createPattern(c, 'repeat');
1721
+ }
1722
+ }
1723
+ svg.Element.pattern.prototype = new svg.Element.ElementBase;
1724
+
1725
+ // marker element
1726
+ svg.Element.marker = function(node) {
1727
+ this.base = svg.Element.ElementBase;
1728
+ this.base(node);
1729
+
1730
+ this.baseRender = this.render;
1731
+ this.render = function(ctx, point, angle) {
1732
+ ctx.translate(point.x, point.y);
1733
+ if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle);
1734
+ if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth);
1735
+ ctx.save();
1736
+
1737
+ // render me using a temporary svg element
1738
+ var tempSvg = new svg.Element.svg();
1739
+ tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
1740
+ tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value);
1741
+ tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value);
1742
+ tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value);
1743
+ tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value);
1744
+ tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black'));
1745
+ tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
1746
+ tempSvg.children = this.children;
1747
+ tempSvg.render(ctx);
1748
+
1749
+ ctx.restore();
1750
+ if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1/ctx.lineWidth, 1/ctx.lineWidth);
1751
+ if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle);
1752
+ ctx.translate(-point.x, -point.y);
1753
+ }
1754
+ }
1755
+ svg.Element.marker.prototype = new svg.Element.ElementBase;
1756
+
1757
+ // definitions element
1758
+ svg.Element.defs = function(node) {
1759
+ this.base = svg.Element.ElementBase;
1760
+ this.base(node);
1761
+
1762
+ this.render = function(ctx) {
1763
+ // NOOP
1764
+ }
1765
+ }
1766
+ svg.Element.defs.prototype = new svg.Element.ElementBase;
1767
+
1768
+ // base for gradients
1769
+ svg.Element.GradientBase = function(node) {
1770
+ this.base = svg.Element.ElementBase;
1771
+ this.base(node);
1772
+
1773
+ this.gradientUnits = this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
1774
+
1775
+ this.stops = [];
1776
+ for (var i=0; i<this.children.length; i++) {
1777
+ var child = this.children[i];
1778
+ this.stops.push(child);
1779
+ }
1780
+
1781
+ this.getGradient = function() {
1782
+ // OVERRIDE ME!
1783
+ }
1784
+
1785
+ this.createGradient = function(ctx, element) {
1786
+ var stopsContainer = this;
1787
+ if (this.attribute('xlink:href').hasValue()) {
1788
+ stopsContainer = this.attribute('xlink:href').Definition.getDefinition();
1789
+ }
1790
+
1791
+ var g = this.getGradient(ctx, element);
1792
+ for (var i=0; i<stopsContainer.stops.length; i++) {
1793
+ g.addColorStop(stopsContainer.stops[i].offset, stopsContainer.stops[i].color);
1794
+ }
1795
+
1796
+ if (this.attribute('gradientTransform').hasValue()) {
1797
+ // render as transformed pattern on temporary canvas
1798
+ var rootView = svg.ViewPort.viewPorts[0];
1799
+
1800
+ var rect = new svg.Element.rect();
1801
+ rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0);
1802
+ rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0);
1803
+ rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS);
1804
+ rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS);
1805
+
1806
+ var group = new svg.Element.g();
1807
+ group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value);
1808
+ group.children = [ rect ];
1809
+
1810
+ var tempSvg = new svg.Element.svg();
1811
+ tempSvg.attributes['x'] = new svg.Property('x', 0);
1812
+ tempSvg.attributes['y'] = new svg.Property('y', 0);
1813
+ tempSvg.attributes['width'] = new svg.Property('width', rootView.width);
1814
+ tempSvg.attributes['height'] = new svg.Property('height', rootView.height);
1815
+ tempSvg.children = [ group ];
1816
+
1817
+ var c = document.createElement('canvas');
1818
+ c.width = rootView.width;
1819
+ c.height = rootView.height;
1820
+ var tempCtx = c.getContext('2d');
1821
+ tempCtx.fillStyle = g;
1822
+ tempSvg.render(tempCtx);
1823
+ return tempCtx.createPattern(c, 'no-repeat');
1824
+ }
1825
+
1826
+ return g;
1827
+ }
1828
+ }
1829
+ svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
1830
+
1831
+ // linear gradient element
1832
+ svg.Element.linearGradient = function(node) {
1833
+ this.base = svg.Element.GradientBase;
1834
+ this.base(node);
1835
+
1836
+ this.getGradient = function(ctx, element) {
1837
+ var bb = element.getBoundingBox();
1838
+
1839
+ var x1 = (this.gradientUnits == 'objectBoundingBox'
1840
+ ? bb.x() + bb.width() * this.attribute('x1').numValue()
1841
+ : this.attribute('x1').Length.toPixels('x'));
1842
+ var y1 = (this.gradientUnits == 'objectBoundingBox'
1843
+ ? bb.y() + bb.height() * this.attribute('y1').numValue()
1844
+ : this.attribute('y1').Length.toPixels('y'));
1845
+ var x2 = (this.gradientUnits == 'objectBoundingBox'
1846
+ ? bb.x() + bb.width() * this.attribute('x2').numValue()
1847
+ : this.attribute('x2').Length.toPixels('x'));
1848
+ var y2 = (this.gradientUnits == 'objectBoundingBox'
1849
+ ? bb.y() + bb.height() * this.attribute('y2').numValue()
1850
+ : this.attribute('y2').Length.toPixels('y'));
1851
+
1852
+ return ctx.createLinearGradient(x1, y1, x2, y2);
1853
+ }
1854
+ }
1855
+ svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
1856
+
1857
+ // radial gradient element
1858
+ svg.Element.radialGradient = function(node) {
1859
+ this.base = svg.Element.GradientBase;
1860
+ this.base(node);
1861
+
1862
+ this.getGradient = function(ctx, element) {
1863
+ var bb = element.getBoundingBox();
1864
+
1865
+ var cx = (this.gradientUnits == 'objectBoundingBox'
1866
+ ? bb.x() + bb.width() * this.attribute('cx').numValue()
1867
+ : this.attribute('cx').Length.toPixels('x'));
1868
+ var cy = (this.gradientUnits == 'objectBoundingBox'
1869
+ ? bb.y() + bb.height() * this.attribute('cy').numValue()
1870
+ : this.attribute('cy').Length.toPixels('y'));
1871
+
1872
+ var fx = cx;
1873
+ var fy = cy;
1874
+ if (this.attribute('fx').hasValue()) {
1875
+ fx = (this.gradientUnits == 'objectBoundingBox'
1876
+ ? bb.x() + bb.width() * this.attribute('fx').numValue()
1877
+ : this.attribute('fx').Length.toPixels('x'));
1878
+ }
1879
+ if (this.attribute('fy').hasValue()) {
1880
+ fy = (this.gradientUnits == 'objectBoundingBox'
1881
+ ? bb.y() + bb.height() * this.attribute('fy').numValue()
1882
+ : this.attribute('fy').Length.toPixels('y'));
1883
+ }
1884
+
1885
+ var r = (this.gradientUnits == 'objectBoundingBox'
1886
+ ? (bb.width() + bb.height()) / 2.0 * this.attribute('r').numValue()
1887
+ : this.attribute('r').Length.toPixels());
1888
+
1889
+ return ctx.createRadialGradient(fx, fy, 0, cx, cy, r);
1890
+ }
1891
+ }
1892
+ svg.Element.radialGradient.prototype = new svg.Element.GradientBase;
1893
+
1894
+ // gradient stop element
1895
+ svg.Element.stop = function(node) {
1896
+ this.base = svg.Element.ElementBase;
1897
+ this.base(node);
1898
+
1899
+ this.offset = this.attribute('offset').numValue();
1900
+
1901
+ var stopColor = this.style('stop-color');
1902
+ if (this.style('stop-opacity').hasValue()) stopColor = stopColor.Color.addOpacity(this.style('stop-opacity').value);
1903
+ this.color = stopColor.value;
1904
+ }
1905
+ svg.Element.stop.prototype = new svg.Element.ElementBase;
1906
+
1907
+ // animation base element
1908
+ svg.Element.AnimateBase = function(node) {
1909
+ this.base = svg.Element.ElementBase;
1910
+ this.base(node);
1911
+
1912
+ svg.Animations.push(this);
1913
+
1914
+ this.duration = 0.0;
1915
+ this.begin = this.attribute('begin').Time.toMilliseconds();
1916
+ this.maxDuration = this.begin + this.attribute('dur').Time.toMilliseconds();
1917
+
1918
+ this.getProperty = function() {
1919
+ var attributeType = this.attribute('attributeType').value;
1920
+ var attributeName = this.attribute('attributeName').value;
1921
+
1922
+ if (attributeType == 'CSS') {
1923
+ return this.parent.style(attributeName, true);
1924
+ }
1925
+ return this.parent.attribute(attributeName, true);
1926
+ };
1927
+
1928
+ this.initialValue = null;
1929
+ this.removed = false;
1930
+
1931
+ this.calcValue = function() {
1932
+ // OVERRIDE ME!
1933
+ return '';
1934
+ }
1935
+
1936
+ this.update = function(delta) {
1937
+ // set initial value
1938
+ if (this.initialValue == null) {
1939
+ this.initialValue = this.getProperty().value;
1940
+ }
1941
+
1942
+ // if we're past the end time
1943
+ if (this.duration > this.maxDuration) {
1944
+ // loop for indefinitely repeating animations
1945
+ if (this.attribute('repeatCount').value == 'indefinite') {
1946
+ this.duration = 0.0
1947
+ }
1948
+ else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
1949
+ this.removed = true;
1950
+ this.getProperty().value = this.initialValue;
1951
+ return true;
1952
+ }
1953
+ else {
1954
+ return false; // no updates made
1955
+ }
1956
+ }
1957
+ this.duration = this.duration + delta;
1958
+
1959
+ // if we're past the begin time
1960
+ var updated = false;
1961
+ if (this.begin < this.duration) {
1962
+ var newValue = this.calcValue(); // tween
1963
+
1964
+ if (this.attribute('type').hasValue()) {
1965
+ // for transform, etc.
1966
+ var type = this.attribute('type').value;
1967
+ newValue = type + '(' + newValue + ')';
1968
+ }
1969
+
1970
+ this.getProperty().value = newValue;
1971
+ updated = true;
1972
+ }
1973
+
1974
+ return updated;
1975
+ }
1976
+
1977
+ // fraction of duration we've covered
1978
+ this.progress = function() {
1979
+ return ((this.duration - this.begin) / (this.maxDuration - this.begin));
1980
+ }
1981
+ }
1982
+ svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
1983
+
1984
+ // animate element
1985
+ svg.Element.animate = function(node) {
1986
+ this.base = svg.Element.AnimateBase;
1987
+ this.base(node);
1988
+
1989
+ this.calcValue = function() {
1990
+ var from = this.attribute('from').numValue();
1991
+ var to = this.attribute('to').numValue();
1992
+
1993
+ // tween value linearly
1994
+ return from + (to - from) * this.progress();
1995
+ };
1996
+ }
1997
+ svg.Element.animate.prototype = new svg.Element.AnimateBase;
1998
+
1999
+ // animate color element
2000
+ svg.Element.animateColor = function(node) {
2001
+ this.base = svg.Element.AnimateBase;
2002
+ this.base(node);
2003
+
2004
+ this.calcValue = function() {
2005
+ var from = new RGBColor(this.attribute('from').value);
2006
+ var to = new RGBColor(this.attribute('to').value);
2007
+
2008
+ if (from.ok && to.ok) {
2009
+ // tween color linearly
2010
+ var r = from.r + (to.r - from.r) * this.progress();
2011
+ var g = from.g + (to.g - from.g) * this.progress();
2012
+ var b = from.b + (to.b - from.b) * this.progress();
2013
+ return 'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')';
2014
+ }
2015
+ return this.attribute('from').value;
2016
+ };
2017
+ }
2018
+ svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
2019
+
2020
+ // animate transform element
2021
+ svg.Element.animateTransform = function(node) {
2022
+ this.base = svg.Element.animate;
2023
+ this.base(node);
2024
+ }
2025
+ svg.Element.animateTransform.prototype = new svg.Element.animate;
2026
+
2027
+ // font element
2028
+ svg.Element.font = function(node) {
2029
+ this.base = svg.Element.ElementBase;
2030
+ this.base(node);
2031
+
2032
+ this.horizAdvX = this.attribute('horiz-adv-x').numValue();
2033
+
2034
+ this.isRTL = false;
2035
+ this.isArabic = false;
2036
+ this.fontFace = null;
2037
+ this.missingGlyph = null;
2038
+ this.glyphs = [];
2039
+ for (var i=0; i<this.children.length; i++) {
2040
+ var child = this.children[i];
2041
+ if (child.type == 'font-face') {
2042
+ this.fontFace = child;
2043
+ if (child.style('font-family').hasValue()) {
2044
+ svg.Definitions[child.style('font-family').value] = this;
2045
+ }
2046
+ }
2047
+ else if (child.type == 'missing-glyph') this.missingGlyph = child;
2048
+ else if (child.type == 'glyph') {
2049
+ if (child.arabicForm != '') {
2050
+ this.isRTL = true;
2051
+ this.isArabic = true;
2052
+ if (typeof(this.glyphs[child.unicode]) == 'undefined') this.glyphs[child.unicode] = [];
2053
+ this.glyphs[child.unicode][child.arabicForm] = child;
2054
+ }
2055
+ else {
2056
+ this.glyphs[child.unicode] = child;
2057
+ }
2058
+ }
2059
+ }
2060
+ }
2061
+ svg.Element.font.prototype = new svg.Element.ElementBase;
2062
+
2063
+ // font-face element
2064
+ svg.Element.fontface = function(node) {
2065
+ this.base = svg.Element.ElementBase;
2066
+ this.base(node);
2067
+
2068
+ this.ascent = this.attribute('ascent').value;
2069
+ this.descent = this.attribute('descent').value;
2070
+ this.unitsPerEm = this.attribute('units-per-em').numValue();
2071
+ }
2072
+ svg.Element.fontface.prototype = new svg.Element.ElementBase;
2073
+
2074
+ // missing-glyph element
2075
+ svg.Element.missingglyph = function(node) {
2076
+ this.base = svg.Element.path;
2077
+ this.base(node);
2078
+
2079
+ this.horizAdvX = 0;
2080
+ }
2081
+ svg.Element.missingglyph.prototype = new svg.Element.path;
2082
+
2083
+ // glyph element
2084
+ svg.Element.glyph = function(node) {
2085
+ this.base = svg.Element.path;
2086
+ this.base(node);
2087
+
2088
+ this.horizAdvX = this.attribute('horiz-adv-x').numValue();
2089
+ this.unicode = this.attribute('unicode').value;
2090
+ this.arabicForm = this.attribute('arabic-form').value;
2091
+ }
2092
+ svg.Element.glyph.prototype = new svg.Element.path;
2093
+
2094
+ // text element
2095
+ svg.Element.text = function(node) {
2096
+ this.base = svg.Element.RenderedElementBase;
2097
+ this.base(node);
2098
+
2099
+ if (node != null) {
2100
+ // add children
2101
+ this.children = [];
2102
+ for (var i=0; i<node.childNodes.length; i++) {
2103
+ var childNode = node.childNodes[i];
2104
+ if (childNode.nodeType == 1) { // capture tspan and tref nodes
2105
+ this.addChild(childNode, true);
2106
+ }
2107
+ else if (childNode.nodeType == 3) { // capture text
2108
+ this.addChild(new svg.Element.tspan(childNode), false);
2109
+ }
2110
+ }
2111
+ }
2112
+
2113
+ this.baseSetContext = this.setContext;
2114
+ this.setContext = function(ctx) {
2115
+ this.baseSetContext(ctx);
2116
+ if (this.style('dominant-baseline').hasValue()) ctx.textBaseline = this.style('dominant-baseline').value;
2117
+ if (this.style('alignment-baseline').hasValue()) ctx.textBaseline = this.style('alignment-baseline').value;
2118
+ }
2119
+
2120
+ this.renderChildren = function(ctx) {
2121
+ var textAnchor = this.style('text-anchor').valueOrDefault('start');
2122
+ var x = this.attribute('x').Length.toPixels('x');
2123
+ var y = this.attribute('y').Length.toPixels('y');
2124
+ for (var i=0; i<this.children.length; i++) {
2125
+ var child = this.children[i];
2126
+
2127
+ if (child.attribute('x').hasValue()) {
2128
+ child.x = child.attribute('x').Length.toPixels('x');
2129
+ }
2130
+ else {
2131
+ if (child.attribute('dx').hasValue()) x += child.attribute('dx').Length.toPixels('x');
2132
+ child.x = x;
2133
+ }
2134
+
2135
+ var childLength = child.measureText ? child.measureText(ctx) : 0;
2136
+ if (textAnchor != 'start' && (i==0 || child.attribute('x').hasValue())) { // new group?
2137
+ // loop through rest of children
2138
+ var groupLength = childLength;
2139
+ for (var j=i+1; j<this.children.length; j++) {
2140
+ var childInGroup = this.children[j];
2141
+ if (childInGroup.attribute('x').hasValue()) break; // new group
2142
+ groupLength += childInGroup.measureText ? childInGroup.measureText(ctx) : 0;
2143
+ }
2144
+ child.x -= (textAnchor == 'end' ? groupLength : groupLength / 2.0);
2145
+ }
2146
+ x = child.x + childLength;
2147
+
2148
+ if (child.attribute('y').hasValue()) {
2149
+ child.y = child.attribute('y').Length.toPixels('y');
2150
+ }
2151
+ else {
2152
+ if (child.attribute('dy').hasValue()) y += child.attribute('dy').Length.toPixels('y');
2153
+ child.y = y;
2154
+ }
2155
+ y = child.y;
2156
+
2157
+ child.render(ctx);
2158
+ }
2159
+ }
2160
+ }
2161
+ svg.Element.text.prototype = new svg.Element.RenderedElementBase;
2162
+
2163
+ // text base
2164
+ svg.Element.TextElementBase = function(node) {
2165
+ this.base = svg.Element.RenderedElementBase;
2166
+ this.base(node);
2167
+
2168
+ this.getGlyph = function(font, text, i) {
2169
+ var c = text[i];
2170
+ var glyph = null;
2171
+ if (font.isArabic) {
2172
+ var arabicForm = 'isolated';
2173
+ if ((i==0 || text[i-1]==' ') && i<text.length-2 && text[i+1]!=' ') arabicForm = 'terminal';
2174
+ if (i>0 && text[i-1]!=' ' && i<text.length-2 && text[i+1]!=' ') arabicForm = 'medial';
2175
+ if (i>0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial';
2176
+ if (typeof(font.glyphs[c]) != 'undefined') {
2177
+ glyph = font.glyphs[c][arabicForm];
2178
+ if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c];
2179
+ }
2180
+ }
2181
+ else {
2182
+ glyph = font.glyphs[c];
2183
+ }
2184
+ if (glyph == null) glyph = font.missingGlyph;
2185
+ return glyph;
2186
+ }
2187
+
2188
+ this.renderChildren = function(ctx) {
2189
+ var customFont = this.parent.style('font-family').Definition.getDefinition();
2190
+ if (customFont != null) {
2191
+ var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
2192
+ var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
2193
+ var text = this.getText();
2194
+ if (customFont.isRTL) text = text.split("").reverse().join("");
2195
+
2196
+ var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
2197
+ for (var i=0; i<text.length; i++) {
2198
+ var glyph = this.getGlyph(customFont, text, i);
2199
+ var scale = fontSize / customFont.fontFace.unitsPerEm;
2200
+ ctx.translate(this.x, this.y);
2201
+ ctx.scale(scale, -scale);
2202
+ var lw = ctx.lineWidth;
2203
+ ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize;
2204
+ if (fontStyle == 'italic') ctx.transform(1, 0, .4, 1, 0, 0);
2205
+ glyph.render(ctx);
2206
+ if (fontStyle == 'italic') ctx.transform(1, 0, -.4, 1, 0, 0);
2207
+ ctx.lineWidth = lw;
2208
+ ctx.scale(1/scale, -1/scale);
2209
+ ctx.translate(-this.x, -this.y);
2210
+
2211
+ this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm;
2212
+ if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
2213
+ this.x += dx[i];
2214
+ }
2215
+ }
2216
+ return;
2217
+ }
2218
+
2219
+ if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y);
2220
+ if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y);
2221
+ }
2222
+
2223
+ this.getText = function() {
2224
+ // OVERRIDE ME
2225
+ }
2226
+
2227
+ this.measureText = function(ctx) {
2228
+ var customFont = this.parent.style('font-family').Definition.getDefinition();
2229
+ if (customFont != null) {
2230
+ var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
2231
+ var measure = 0;
2232
+ var text = this.getText();
2233
+ if (customFont.isRTL) text = text.split("").reverse().join("");
2234
+ var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
2235
+ for (var i=0; i<text.length; i++) {
2236
+ var glyph = this.getGlyph(customFont, text, i);
2237
+ measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm;
2238
+ if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
2239
+ measure += dx[i];
2240
+ }
2241
+ }
2242
+ return measure;
2243
+ }
2244
+
2245
+ var textToMeasure = svg.compressSpaces(this.getText());
2246
+ if (!ctx.measureText) return textToMeasure.length * 10;
2247
+
2248
+ ctx.save();
2249
+ this.setContext(ctx);
2250
+ var width = ctx.measureText(textToMeasure).width;
2251
+ ctx.restore();
2252
+ return width;
2253
+ }
2254
+ }
2255
+ svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase;
2256
+
2257
+ // tspan
2258
+ svg.Element.tspan = function(node) {
2259
+ this.base = svg.Element.TextElementBase;
2260
+ this.base(node);
2261
+
2262
+ this.text = node.nodeType == 3 ? node.nodeValue : // text
2263
+ node.childNodes.length > 0 ? node.childNodes[0].nodeValue : // element
2264
+ node.text;
2265
+ this.getText = function() {
2266
+ return this.text;
2267
+ }
2268
+ }
2269
+ svg.Element.tspan.prototype = new svg.Element.TextElementBase;
2270
+
2271
+ // tref
2272
+ svg.Element.tref = function(node) {
2273
+ this.base = svg.Element.TextElementBase;
2274
+ this.base(node);
2275
+
2276
+ this.getText = function() {
2277
+ var element = this.attribute('xlink:href').Definition.getDefinition();
2278
+ if (element != null) return element.children[0].getText();
2279
+ }
2280
+ }
2281
+ svg.Element.tref.prototype = new svg.Element.TextElementBase;
2282
+
2283
+ // a element
2284
+ svg.Element.a = function(node) {
2285
+ this.base = svg.Element.TextElementBase;
2286
+ this.base(node);
2287
+
2288
+ this.hasText = true;
2289
+ for (var i=0; i<node.childNodes.length; i++) {
2290
+ if (node.childNodes[i].nodeType != 3) this.hasText = false;
2291
+ }
2292
+
2293
+ // this might contain text
2294
+ this.text = this.hasText ? node.childNodes[0].nodeValue : '';
2295
+ this.getText = function() {
2296
+ return this.text;
2297
+ }
2298
+
2299
+ this.baseRenderChildren = this.renderChildren;
2300
+ this.renderChildren = function(ctx) {
2301
+ if (this.hasText) {
2302
+ // render as text element
2303
+ this.baseRenderChildren(ctx);
2304
+ var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
2305
+ svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.Length.toPixels('y'), this.x + this.measureText(ctx), this.y));
2306
+ }
2307
+ else {
2308
+ // render as temporary group
2309
+ var g = new svg.Element.g();
2310
+ g.children = this.children;
2311
+ g.parent = this;
2312
+ g.render(ctx);
2313
+ }
2314
+ }
2315
+
2316
+ this.onclick = function() {
2317
+ window.open(this.attribute('xlink:href').value);
2318
+ }
2319
+
2320
+ this.onmousemove = function() {
2321
+ svg.ctx.canvas.style.cursor = 'pointer';
2322
+ }
2323
+ }
2324
+ svg.Element.a.prototype = new svg.Element.TextElementBase;
2325
+
2326
+ // image element
2327
+ svg.Element.image = function(node) {
2328
+ this.base = svg.Element.RenderedElementBase;
2329
+ this.base(node);
2330
+
2331
+ svg.Images.push(this);
2332
+ this.img = document.createElement('img');
2333
+ this.loaded = false;
2334
+ var that = this;
2335
+ this.img.onload = function() { that.loaded = true; }
2336
+ this.img.src = this.attribute('xlink:href').value;
2337
+
2338
+ this.renderChildren = function(ctx) {
2339
+ var x = this.attribute('x').Length.toPixels('x');
2340
+ var y = this.attribute('y').Length.toPixels('y');
2341
+
2342
+ var width = this.attribute('width').Length.toPixels('x');
2343
+ var height = this.attribute('height').Length.toPixels('y');
2344
+ if (width == 0 || height == 0) return;
2345
+
2346
+ ctx.save();
2347
+ ctx.translate(x, y);
2348
+ svg.AspectRatio(ctx,
2349
+ this.attribute('preserveAspectRatio').value,
2350
+ width,
2351
+ this.img.width,
2352
+ height,
2353
+ this.img.height,
2354
+ 0,
2355
+ 0);
2356
+ ctx.drawImage(this.img, 0, 0);
2357
+ ctx.restore();
2358
+ }
2359
+ }
2360
+ svg.Element.image.prototype = new svg.Element.RenderedElementBase;
2361
+
2362
+ // group element
2363
+ svg.Element.g = function(node) {
2364
+ this.base = svg.Element.RenderedElementBase;
2365
+ this.base(node);
2366
+
2367
+ this.getBoundingBox = function() {
2368
+ var bb = new svg.BoundingBox();
2369
+ for (var i=0; i<this.children.length; i++) {
2370
+ bb.addBoundingBox(this.children[i].getBoundingBox());
2371
+ }
2372
+ return bb;
2373
+ };
2374
+ }
2375
+ svg.Element.g.prototype = new svg.Element.RenderedElementBase;
2376
+
2377
+ // symbol element
2378
+ svg.Element.symbol = function(node) {
2379
+ this.base = svg.Element.RenderedElementBase;
2380
+ this.base(node);
2381
+
2382
+ this.baseSetContext = this.setContext;
2383
+ this.setContext = function(ctx) {
2384
+ this.baseSetContext(ctx);
2385
+
2386
+ // viewbox
2387
+ if (this.attribute('viewBox').hasValue()) {
2388
+ var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
2389
+ var minX = viewBox[0];
2390
+ var minY = viewBox[1];
2391
+ width = viewBox[2];
2392
+ height = viewBox[3];
2393
+
2394
+ svg.AspectRatio(ctx,
2395
+ this.attribute('preserveAspectRatio').value,
2396
+ this.attribute('width').Length.toPixels('x'),
2397
+ width,
2398
+ this.attribute('height').Length.toPixels('y'),
2399
+ height,
2400
+ minX,
2401
+ minY);
2402
+
2403
+ svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
2404
+ }
2405
+ }
2406
+ }
2407
+ svg.Element.symbol.prototype = new svg.Element.RenderedElementBase;
2408
+
2409
+ // style element
2410
+ svg.Element.style = function(node) {
2411
+ this.base = svg.Element.ElementBase;
2412
+ this.base(node);
2413
+
2414
+ // text, or spaces then CDATA
2415
+ var css = node.childNodes[0].nodeValue + (node.childNodes.length > 1 ? node.childNodes[1].nodeValue : '');
2416
+ css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments
2417
+ css = svg.compressSpaces(css); // replace whitespace
2418
+ var cssDefs = css.split('}');
2419
+ for (var i=0; i<cssDefs.length; i++) {
2420
+ if (svg.trim(cssDefs[i]) != '') {
2421
+ var cssDef = cssDefs[i].split('{');
2422
+ var cssClasses = cssDef[0].split(',');
2423
+ var cssProps = cssDef[1].split(';');
2424
+ for (var j=0; j<cssClasses.length; j++) {
2425
+ var cssClass = svg.trim(cssClasses[j]);
2426
+ if (cssClass != '') {
2427
+ var props = {};
2428
+ for (var k=0; k<cssProps.length; k++) {
2429
+ var prop = cssProps[k].indexOf(':');
2430
+ var name = cssProps[k].substr(0, prop);
2431
+ var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop);
2432
+ if (name != null && value != null) {
2433
+ props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value));
2434
+ }
2435
+ }
2436
+ svg.Styles[cssClass] = props;
2437
+ if (cssClass == '@font-face') {
2438
+ var fontFamily = props['font-family'].value.replace(/"/g,'');
2439
+ var srcs = props['src'].value.split(',');
2440
+ for (var s=0; s<srcs.length; s++) {
2441
+ if (srcs[s].indexOf('format("svg")') > 0) {
2442
+ var urlStart = srcs[s].indexOf('url');
2443
+ var urlEnd = srcs[s].indexOf(')', urlStart);
2444
+ var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
2445
+ var doc = svg.parseXml(svg.ajax(url));
2446
+ var fonts = doc.getElementsByTagName('font');
2447
+ for (var f=0; f<fonts.length; f++) {
2448
+ var font = svg.CreateElement(fonts[f]);
2449
+ svg.Definitions[fontFamily] = font;
2450
+ }
2451
+ }
2452
+ }
2453
+ }
2454
+ }
2455
+ }
2456
+ }
2457
+ }
2458
+ }
2459
+ svg.Element.style.prototype = new svg.Element.ElementBase;
2460
+
2461
+ // use element
2462
+ svg.Element.use = function(node) {
2463
+ this.base = svg.Element.RenderedElementBase;
2464
+ this.base(node);
2465
+
2466
+ this.baseSetContext = this.setContext;
2467
+ this.setContext = function(ctx) {
2468
+ this.baseSetContext(ctx);
2469
+ if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').Length.toPixels('x'), 0);
2470
+ if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').Length.toPixels('y'));
2471
+ }
2472
+
2473
+ this.getDefinition = function() {
2474
+ var element = this.attribute('xlink:href').Definition.getDefinition();
2475
+ if (this.attribute('width').hasValue()) element.attribute('width', true).value = this.attribute('width').value;
2476
+ if (this.attribute('height').hasValue()) element.attribute('height', true).value = this.attribute('height').value;
2477
+ return element;
2478
+ }
2479
+
2480
+ this.path = function(ctx) {
2481
+ var element = this.getDefinition();
2482
+ if (element != null) element.path(ctx);
2483
+ }
2484
+
2485
+ this.renderChildren = function(ctx) {
2486
+ var element = this.getDefinition();
2487
+ if (element != null) element.render(ctx);
2488
+ }
2489
+ }
2490
+ svg.Element.use.prototype = new svg.Element.RenderedElementBase;
2491
+
2492
+ // mask element
2493
+ svg.Element.mask = function(node) {
2494
+ this.base = svg.Element.ElementBase;
2495
+ this.base(node);
2496
+
2497
+ this.apply = function(ctx, element) {
2498
+ // render as temp svg
2499
+ var x = this.attribute('x').Length.toPixels('x');
2500
+ var y = this.attribute('y').Length.toPixels('y');
2501
+ var width = this.attribute('width').Length.toPixels('x');
2502
+ var height = this.attribute('height').Length.toPixels('y');
2503
+
2504
+ // temporarily remove mask to avoid recursion
2505
+ var mask = element.attribute('mask').value;
2506
+ element.attribute('mask').value = '';
2507
+
2508
+ var cMask = document.createElement('canvas');
2509
+ cMask.width = x + width;
2510
+ cMask.height = y + height;
2511
+ var maskCtx = cMask.getContext('2d');
2512
+ this.renderChildren(maskCtx);
2513
+
2514
+ var c = document.createElement('canvas');
2515
+ c.width = x + width;
2516
+ c.height = y + height;
2517
+ var tempCtx = c.getContext('2d');
2518
+ element.render(tempCtx);
2519
+ tempCtx.globalCompositeOperation = 'destination-in';
2520
+ tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat');
2521
+ tempCtx.fillRect(0, 0, x + width, y + height);
2522
+
2523
+ ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat');
2524
+ ctx.fillRect(0, 0, x + width, y + height);
2525
+
2526
+ // reassign mask
2527
+ element.attribute('mask').value = mask;
2528
+ }
2529
+
2530
+ this.render = function(ctx) {
2531
+ // NO RENDER
2532
+ }
2533
+ }
2534
+ svg.Element.mask.prototype = new svg.Element.ElementBase;
2535
+
2536
+ // clip element
2537
+ svg.Element.clipPath = function(node) {
2538
+ this.base = svg.Element.ElementBase;
2539
+ this.base(node);
2540
+
2541
+ this.apply = function(ctx) {
2542
+ for (var i=0; i<this.children.length; i++) {
2543
+ if (this.children[i].path) {
2544
+ this.children[i].path(ctx);
2545
+ ctx.clip();
2546
+ }
2547
+ }
2548
+ }
2549
+
2550
+ this.render = function(ctx) {
2551
+ // NO RENDER
2552
+ }
2553
+ }
2554
+ svg.Element.clipPath.prototype = new svg.Element.ElementBase;
2555
+
2556
+ // filters
2557
+ svg.Element.filter = function(node) {
2558
+ this.base = svg.Element.ElementBase;
2559
+ this.base(node);
2560
+
2561
+ this.apply = function(ctx, element) {
2562
+ // render as temp svg
2563
+ var bb = element.getBoundingBox();
2564
+ var x = this.attribute('x').Length.toPixels('x');
2565
+ var y = this.attribute('y').Length.toPixels('y');
2566
+ if (x == 0 || y == 0) {
2567
+ x = bb.x1;
2568
+ y = bb.y1;
2569
+ }
2570
+ var width = this.attribute('width').Length.toPixels('x');
2571
+ var height = this.attribute('height').Length.toPixels('y');
2572
+ if (width == 0 || height == 0) {
2573
+ width = bb.width();
2574
+ height = bb.height();
2575
+ }
2576
+
2577
+ // temporarily remove filter to avoid recursion
2578
+ var filter = element.style('filter').value;
2579
+ element.style('filter').value = '';
2580
+
2581
+ // max filter distance
2582
+ var extraPercent = .20;
2583
+ var px = extraPercent * width;
2584
+ var py = extraPercent * height;
2585
+
2586
+ var c = document.createElement('canvas');
2587
+ c.width = width + 2*px;
2588
+ c.height = height + 2*py;
2589
+ var tempCtx = c.getContext('2d');
2590
+ tempCtx.translate(-x + px, -y + py);
2591
+ element.render(tempCtx);
2592
+
2593
+ // apply filters
2594
+ for (var i=0; i<this.children.length; i++) {
2595
+ this.children[i].apply(tempCtx, 0, 0, width + 2*px, height + 2*py);
2596
+ }
2597
+
2598
+ // render on me
2599
+ ctx.drawImage(c, 0, 0, width + 2*px, height + 2*py, x - px, y - py, width + 2*px, height + 2*py);
2600
+
2601
+ // reassign filter
2602
+ element.style('filter', true).value = filter;
2603
+ }
2604
+
2605
+ this.render = function(ctx) {
2606
+ // NO RENDER
2607
+ }
2608
+ }
2609
+ svg.Element.filter.prototype = new svg.Element.ElementBase;
2610
+
2611
+ svg.Element.feGaussianBlur = function(node) {
2612
+ this.base = svg.Element.ElementBase;
2613
+ this.base(node);
2614
+
2615
+ function make_fgauss(sigma) {
2616
+ sigma = Math.max(sigma, 0.01);
2617
+ var len = Math.ceil(sigma * 4.0) + 1;
2618
+ mask = [];
2619
+ for (var i = 0; i < len; i++) {
2620
+ mask[i] = Math.exp(-0.5 * (i / sigma) * (i / sigma));
2621
+ }
2622
+ return mask;
2623
+ }
2624
+
2625
+ function normalize(mask) {
2626
+ var sum = 0;
2627
+ for (var i = 1; i < mask.length; i++) {
2628
+ sum += Math.abs(mask[i]);
2629
+ }
2630
+ sum = 2 * sum + Math.abs(mask[0]);
2631
+ for (var i = 0; i < mask.length; i++) {
2632
+ mask[i] /= sum;
2633
+ }
2634
+ return mask;
2635
+ }
2636
+
2637
+ function convolve_even(src, dst, mask, width, height) {
2638
+ for (var y = 0; y < height; y++) {
2639
+ for (var x = 0; x < width; x++) {
2640
+ var a = imGet(src, x, y, width, height, 3)/255;
2641
+ for (var rgba = 0; rgba < 4; rgba++) {
2642
+ var sum = mask[0] * (a==0?255:imGet(src, x, y, width, height, rgba)) * (a==0||rgba==3?1:a);
2643
+ for (var i = 1; i < mask.length; i++) {
2644
+ var a1 = imGet(src, Math.max(x-i,0), y, width, height, 3)/255;
2645
+ var a2 = imGet(src, Math.min(x+i, width-1), y, width, height, 3)/255;
2646
+ sum += mask[i] *
2647
+ ((a1==0?255:imGet(src, Math.max(x-i,0), y, width, height, rgba)) * (a1==0||rgba==3?1:a1) +
2648
+ (a2==0?255:imGet(src, Math.min(x+i, width-1), y, width, height, rgba)) * (a2==0||rgba==3?1:a2));
2649
+ }
2650
+ imSet(dst, y, x, height, width, rgba, sum);
2651
+ }
2652
+ }
2653
+ }
2654
+ }
2655
+
2656
+ function imGet(img, x, y, width, height, rgba) {
2657
+ return img[y*width*4 + x*4 + rgba];
2658
+ }
2659
+
2660
+ function imSet(img, x, y, width, height, rgba, val) {
2661
+ img[y*width*4 + x*4 + rgba] = val;
2662
+ }
2663
+
2664
+ function blur(ctx, width, height, sigma)
2665
+ {
2666
+ var srcData = ctx.getImageData(0, 0, width, height);
2667
+ var mask = make_fgauss(sigma);
2668
+ mask = normalize(mask);
2669
+ tmp = [];
2670
+ convolve_even(srcData.data, tmp, mask, width, height);
2671
+ convolve_even(tmp, srcData.data, mask, height, width);
2672
+ ctx.clearRect(0, 0, width, height);
2673
+ ctx.putImageData(srcData, 0, 0);
2674
+ }
2675
+
2676
+ this.apply = function(ctx, x, y, width, height) {
2677
+ // assuming x==0 && y==0 for now
2678
+ blur(ctx, width, height, this.attribute('stdDeviation').numValue());
2679
+ }
2680
+ }
2681
+ svg.Element.filter.prototype = new svg.Element.feGaussianBlur;
2682
+
2683
+ // title element, do nothing
2684
+ svg.Element.title = function(node) {
2685
+ }
2686
+ svg.Element.title.prototype = new svg.Element.ElementBase;
2687
+
2688
+ // desc element, do nothing
2689
+ svg.Element.desc = function(node) {
2690
+ }
2691
+ svg.Element.desc.prototype = new svg.Element.ElementBase;
2692
+
2693
+ svg.Element.MISSING = function(node) {
2694
+ console.log('ERROR: Element \'' + node.nodeName + '\' not yet implemented.');
2695
+ }
2696
+ svg.Element.MISSING.prototype = new svg.Element.ElementBase;
2697
+
2698
+ // element factory
2699
+ svg.CreateElement = function(node) {
2700
+ var className = node.nodeName.replace(/^[^:]+:/,''); // remove namespace
2701
+ className = className.replace(/\-/g,''); // remove dashes
2702
+ var e = null;
2703
+ if (typeof(svg.Element[className]) != 'undefined') {
2704
+ e = new svg.Element[className](node);
2705
+ }
2706
+ else {
2707
+ e = new svg.Element.MISSING(node);
2708
+ }
2709
+
2710
+ e.type = node.nodeName;
2711
+ return e;
2712
+ }
2713
+
2714
+ // load from url
2715
+ svg.load = function(ctx, url) {
2716
+ svg.loadXml(ctx, svg.ajax(url));
2717
+ }
2718
+
2719
+ // load from xml
2720
+ svg.loadXml = function(ctx, xml) {
2721
+ svg.loadXmlDoc(ctx, svg.parseXml(xml));
2722
+ }
2723
+
2724
+ svg.loadXmlDoc = function(ctx, dom) {
2725
+ svg.init(ctx);
2726
+
2727
+ var mapXY = function(p) {
2728
+ var e = ctx.canvas;
2729
+ while (e) {
2730
+ p.x -= e.offsetLeft;
2731
+ p.y -= e.offsetTop;
2732
+ e = e.offsetParent;
2733
+ }
2734
+ if (window.scrollX) p.x += window.scrollX;
2735
+ if (window.scrollY) p.y += window.scrollY;
2736
+ return p;
2737
+ }
2738
+
2739
+ // bind mouse
2740
+ if (svg.opts['ignoreMouse'] != true) {
2741
+ ctx.canvas.onclick = function(e) {
2742
+ var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
2743
+ svg.Mouse.onclick(p.x, p.y);
2744
+ };
2745
+ ctx.canvas.onmousemove = function(e) {
2746
+ var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
2747
+ svg.Mouse.onmousemove(p.x, p.y);
2748
+ };
2749
+ }
2750
+
2751
+ var e = svg.CreateElement(dom.documentElement);
2752
+ e.root = true;
2753
+
2754
+ // render loop
2755
+ var isFirstRender = true;
2756
+ var draw = function() {
2757
+ svg.ViewPort.Clear();
2758
+ if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight);
2759
+
2760
+ if (svg.opts['ignoreDimensions'] != true) {
2761
+ // set canvas size
2762
+ if (e.style('width').hasValue()) {
2763
+ ctx.canvas.width = e.style('width').Length.toPixels('x');
2764
+ ctx.canvas.style.width = ctx.canvas.width + 'px';
2765
+ }
2766
+ if (e.style('height').hasValue()) {
2767
+ ctx.canvas.height = e.style('height').Length.toPixels('y');
2768
+ ctx.canvas.style.height = ctx.canvas.height + 'px';
2769
+ }
2770
+ }
2771
+ var cWidth = ctx.canvas.clientWidth || ctx.canvas.width;
2772
+ var cHeight = ctx.canvas.clientHeight || ctx.canvas.height;
2773
+ svg.ViewPort.SetCurrent(cWidth, cHeight);
2774
+
2775
+ if (svg.opts != null && svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX'];
2776
+ if (svg.opts != null && svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY'];
2777
+ if (svg.opts != null && svg.opts['scaleWidth'] != null && svg.opts['scaleHeight'] != null) {
2778
+ var xRatio = 1, yRatio = 1;
2779
+ if (e.attribute('width').hasValue()) xRatio = e.attribute('width').Length.toPixels('x') / svg.opts['scaleWidth'];
2780
+ if (e.attribute('height').hasValue()) yRatio = e.attribute('height').Length.toPixels('y') / svg.opts['scaleHeight'];
2781
+
2782
+ e.attribute('width', true).value = svg.opts['scaleWidth'];
2783
+ e.attribute('height', true).value = svg.opts['scaleHeight'];
2784
+ e.attribute('viewBox', true).value = '0 0 ' + (cWidth * xRatio) + ' ' + (cHeight * yRatio);
2785
+ e.attribute('preserveAspectRatio', true).value = 'none';
2786
+ }
2787
+
2788
+ // clear and render
2789
+ if (svg.opts['ignoreClear'] != true) {
2790
+ ctx.clearRect(0, 0, cWidth, cHeight);
2791
+ }
2792
+ e.render(ctx);
2793
+ if (isFirstRender) {
2794
+ isFirstRender = false;
2795
+ if (svg.opts != null && typeof(svg.opts['renderCallback']) == 'function') svg.opts['renderCallback']();
2796
+ }
2797
+ }
2798
+
2799
+ var waitingForImages = true;
2800
+ if (svg.ImagesLoaded()) {
2801
+ waitingForImages = false;
2802
+ draw();
2803
+ }
2804
+ svg.intervalID = setInterval(function() {
2805
+ var needUpdate = false;
2806
+
2807
+ if (waitingForImages && svg.ImagesLoaded()) {
2808
+ waitingForImages = false;
2809
+ needUpdate = true;
2810
+ }
2811
+
2812
+ // need update from mouse events?
2813
+ if (svg.opts['ignoreMouse'] != true) {
2814
+ needUpdate = needUpdate | svg.Mouse.hasEvents();
2815
+ }
2816
+
2817
+ // need update from animations?
2818
+ if (svg.opts['ignoreAnimation'] != true) {
2819
+ for (var i=0; i<svg.Animations.length; i++) {
2820
+ needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE);
2821
+ }
2822
+ }
2823
+
2824
+ // need update from redraw?
2825
+ if (svg.opts != null && typeof(svg.opts['forceRedraw']) == 'function') {
2826
+ if (svg.opts['forceRedraw']() == true) needUpdate = true;
2827
+ }
2828
+
2829
+ // render if needed
2830
+ if (needUpdate) {
2831
+ draw();
2832
+ svg.Mouse.runEvents(); // run and clear our events
2833
+ }
2834
+ }, 1000 / svg.FRAMERATE);
2835
+ }
2836
+
2837
+ svg.stop = function() {
2838
+ if (svg.intervalID) {
2839
+ clearInterval(svg.intervalID);
2840
+ }
2841
+ }
2842
+
2843
+ svg.Mouse = new (function() {
2844
+ this.events = [];
2845
+ this.hasEvents = function() { return this.events.length != 0; }
2846
+
2847
+ this.onclick = function(x, y) {
2848
+ this.events.push({ type: 'onclick', x: x, y: y,
2849
+ run: function(e) { if (e.onclick) e.onclick(); }
2850
+ });
2851
+ }
2852
+
2853
+ this.onmousemove = function(x, y) {
2854
+ this.events.push({ type: 'onmousemove', x: x, y: y,
2855
+ run: function(e) { if (e.onmousemove) e.onmousemove(); }
2856
+ });
2857
+ }
2858
+
2859
+ this.eventElements = [];
2860
+
2861
+ this.checkPath = function(element, ctx) {
2862
+ for (var i=0; i<this.events.length; i++) {
2863
+ var e = this.events[i];
2864
+ if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element;
2865
+ }
2866
+ }
2867
+
2868
+ this.checkBoundingBox = function(element, bb) {
2869
+ for (var i=0; i<this.events.length; i++) {
2870
+ var e = this.events[i];
2871
+ if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element;
2872
+ }
2873
+ }
2874
+
2875
+ this.runEvents = function() {
2876
+ svg.ctx.canvas.style.cursor = '';
2877
+
2878
+ for (var i=0; i<this.events.length; i++) {
2879
+ var e = this.events[i];
2880
+ var element = this.eventElements[i];
2881
+ while (element) {
2882
+ e.run(element);
2883
+ element = element.parent;
2884
+ }
2885
+ }
2886
+
2887
+ // done running, clear
2888
+ this.events = [];
2889
+ this.eventElements = [];
2890
+ }
2891
+ });
2892
+
2893
+ return svg;
2894
+ }
2895
+ })();
2896
+
2897
+ if (CanvasRenderingContext2D) {
2898
+ CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh) {
2899
+ canvg(this.canvas, s, {
2900
+ ignoreMouse: true,
2901
+ ignoreAnimation: true,
2902
+ ignoreDimensions: true,
2903
+ ignoreClear: true,
2904
+ offsetX: dx,
2905
+ offsetY: dy,
2906
+ scaleWidth: dw,
2907
+ scaleHeight: dh
2908
+ });
2909
+ }
2910
+ }/**
2911
+ * @license Highcharts JS v4.1.10 (2015-12-07)
2912
+ * CanVGRenderer Extension module
2913
+ *
2914
+ * (c) 2011-2012 Torstein Honsi, Erik Olsson
2915
+ *
2916
+ * License: www.highcharts.com/license
2917
+ */
2918
+
2919
+ (function (Highcharts) {
2920
+ var UNDEFINED,
2921
+ DIV = 'div',
2922
+ ABSOLUTE = 'absolute',
2923
+ RELATIVE = 'relative',
2924
+ HIDDEN = 'hidden',
2925
+ VISIBLE = 'visible',
2926
+ PX = 'px',
2927
+ css = Highcharts.css,
2928
+ CanVGRenderer = Highcharts.CanVGRenderer,
2929
+ SVGRenderer = Highcharts.SVGRenderer,
2930
+ extend = Highcharts.extend,
2931
+ merge = Highcharts.merge,
2932
+ addEvent = Highcharts.addEvent,
2933
+ createElement = Highcharts.createElement,
2934
+ discardElement = Highcharts.discardElement;
2935
+
2936
+ // Extend CanVG renderer on demand, inherit from SVGRenderer
2937
+ extend(CanVGRenderer.prototype, SVGRenderer.prototype);
2938
+
2939
+ // Add additional functionality:
2940
+ extend(CanVGRenderer.prototype, {
2941
+ create: function (chart, container, chartWidth, chartHeight) {
2942
+ this.setContainer(container, chartWidth, chartHeight);
2943
+ this.configure(chart);
2944
+ },
2945
+ setContainer: function (container, chartWidth, chartHeight) {
2946
+ var containerStyle = container.style,
2947
+ containerParent = container.parentNode,
2948
+ containerLeft = containerStyle.left,
2949
+ containerTop = containerStyle.top,
2950
+ containerOffsetWidth = container.offsetWidth,
2951
+ containerOffsetHeight = container.offsetHeight,
2952
+ canvas,
2953
+ initialHiddenStyle = { visibility: HIDDEN, position: ABSOLUTE };
2954
+
2955
+ this.init(container, chartWidth, chartHeight);
2956
+
2957
+ // add the canvas above it
2958
+ canvas = createElement('canvas', {
2959
+ width: containerOffsetWidth,
2960
+ height: containerOffsetHeight
2961
+ }, {
2962
+ position: RELATIVE,
2963
+ left: containerLeft,
2964
+ top: containerTop
2965
+ }, container);
2966
+ this.canvas = canvas;
2967
+
2968
+ // Create the tooltip line and div, they are placed as siblings to
2969
+ // the container (and as direct childs to the div specified in the html page)
2970
+ this.ttLine = createElement(DIV, null, initialHiddenStyle, containerParent);
2971
+ this.ttDiv = createElement(DIV, null, initialHiddenStyle, containerParent);
2972
+ this.ttTimer = UNDEFINED;
2973
+
2974
+ // Move away the svg node to a new div inside the container's parent so we can hide it.
2975
+ var hiddenSvg = createElement(DIV, {
2976
+ width: containerOffsetWidth,
2977
+ height: containerOffsetHeight
2978
+ }, {
2979
+ visibility: HIDDEN,
2980
+ left: containerLeft,
2981
+ top: containerTop
2982
+ }, containerParent);
2983
+ this.hiddenSvg = hiddenSvg;
2984
+ hiddenSvg.appendChild(this.box);
2985
+ },
2986
+
2987
+ /**
2988
+ * Configures the renderer with the chart. Attach a listener to the event tooltipRefresh.
2989
+ **/
2990
+ configure: function (chart) {
2991
+ var renderer = this,
2992
+ options = chart.options.tooltip,
2993
+ borderWidth = options.borderWidth,
2994
+ tooltipDiv = renderer.ttDiv,
2995
+ tooltipDivStyle = options.style,
2996
+ tooltipLine = renderer.ttLine,
2997
+ padding = parseInt(tooltipDivStyle.padding, 10);
2998
+
2999
+ // Add border styling from options to the style
3000
+ tooltipDivStyle = merge(tooltipDivStyle, {
3001
+ padding: padding + PX,
3002
+ 'background-color': options.backgroundColor,
3003
+ 'border-style': 'solid',
3004
+ 'border-width': borderWidth + PX,
3005
+ 'border-radius': options.borderRadius + PX
3006
+ });
3007
+
3008
+ // Optionally add shadow
3009
+ if (options.shadow) {
3010
+ tooltipDivStyle = merge(tooltipDivStyle, {
3011
+ 'box-shadow': '1px 1px 3px gray', // w3c
3012
+ '-webkit-box-shadow': '1px 1px 3px gray' // webkit
3013
+ });
3014
+ }
3015
+ css(tooltipDiv, tooltipDivStyle);
3016
+
3017
+ // Set simple style on the line
3018
+ css(tooltipLine, {
3019
+ 'border-left': '1px solid darkgray'
3020
+ });
3021
+
3022
+ // This event is triggered when a new tooltip should be shown
3023
+ addEvent(chart, 'tooltipRefresh', function (args) {
3024
+ var chartContainer = chart.container,
3025
+ offsetLeft = chartContainer.offsetLeft,
3026
+ offsetTop = chartContainer.offsetTop,
3027
+ position;
3028
+
3029
+ // Set the content of the tooltip
3030
+ tooltipDiv.innerHTML = args.text;
3031
+
3032
+ // Compute the best position for the tooltip based on the divs size and container size.
3033
+ position = chart.tooltip.getPosition(
3034
+ tooltipDiv.offsetWidth,
3035
+ tooltipDiv.offsetHeight,
3036
+ { plotX: args.x, plotY: args.y }
3037
+ );
3038
+
3039
+ css(tooltipDiv, {
3040
+ visibility: VISIBLE,
3041
+ left: position.x + PX,
3042
+ top: position.y + PX,
3043
+ 'border-color': args.borderColor
3044
+ });
3045
+
3046
+ // Position the tooltip line
3047
+ css(tooltipLine, {
3048
+ visibility: VISIBLE,
3049
+ left: offsetLeft + args.x + PX,
3050
+ top: offsetTop + chart.plotTop + PX,
3051
+ height: chart.plotHeight + PX
3052
+ });
3053
+
3054
+ // This timeout hides the tooltip after 3 seconds
3055
+ // First clear any existing timer
3056
+ if (renderer.ttTimer !== UNDEFINED) {
3057
+ clearTimeout(renderer.ttTimer);
3058
+ }
3059
+
3060
+ // Start a new timer that hides tooltip and line
3061
+ renderer.ttTimer = setTimeout(function () {
3062
+ css(tooltipDiv, { visibility: HIDDEN });
3063
+ css(tooltipLine, { visibility: HIDDEN });
3064
+ }, 3000);
3065
+ });
3066
+ },
3067
+
3068
+ /**
3069
+ * Extend SVGRenderer.destroy to also destroy the elements added by CanVGRenderer.
3070
+ */
3071
+ destroy: function () {
3072
+ var renderer = this;
3073
+
3074
+ // Remove the canvas
3075
+ discardElement(renderer.canvas);
3076
+
3077
+ // Kill the timer
3078
+ if (renderer.ttTimer !== UNDEFINED) {
3079
+ clearTimeout(renderer.ttTimer);
3080
+ }
3081
+
3082
+ // Remove the divs for tooltip and line
3083
+ discardElement(renderer.ttLine);
3084
+ discardElement(renderer.ttDiv);
3085
+ discardElement(renderer.hiddenSvg);
3086
+
3087
+ // Continue with base class
3088
+ return SVGRenderer.prototype.destroy.apply(renderer);
3089
+ },
3090
+
3091
+ /**
3092
+ * Take a color and return it if it's a string, do not make it a gradient even if it is a
3093
+ * gradient. Currently canvg cannot render gradients (turns out black),
3094
+ * see: http://code.google.com/p/canvg/issues/detail?id=104
3095
+ *
3096
+ * @param {Object} color The color or config object
3097
+ */
3098
+ color: function (color, elem, prop) {
3099
+ if (color && color.linearGradient) {
3100
+ // Pick the end color and forward to base implementation
3101
+ color = color.stops[color.stops.length - 1][1];
3102
+ }
3103
+ return SVGRenderer.prototype.color.call(this, color, elem, prop);
3104
+ },
3105
+
3106
+ /**
3107
+ * Draws the SVG on the canvas or adds a draw invokation to the deferred list.
3108
+ */
3109
+ draw: function () {
3110
+ var renderer = this;
3111
+ window.canvg(renderer.canvas, renderer.hiddenSvg.innerHTML);
3112
+ }
3113
+ });
3114
+ }(Highcharts));