Skip to content
Browse files

First import of SVG-edit code from trunk

  • Loading branch information...
1 parent 143b9b1 commit 5b77ed02274126f9489405f032c032e3b3ae8d16 @adityab committed Apr 7, 2012
Showing with 13,868 additions and 0 deletions.
  1. +28 −0 AUTHORS
  2. +19 −0 LICENSE
  3. 0 README
  4. +12 −0 README.md
  5. +27 −0 editor/browser-not-supported.html
  6. +178 −0 editor/browser.js
  7. +2,620 −0 editor/canvg/canvg.js
  8. +287 −0 editor/canvg/rgbcolor.js
  9. +67 −0 editor/contextmenu.js
  10. +203 −0 editor/contextmenu/jquery.contextMenu.js
  11. +528 −0 editor/draw.js
  12. +56 −0 editor/embedapi.html
  13. +173 −0 editor/embedapi.js
  14. +41 −0 editor/extensions/closepath_icons.svg
  15. +298 −0 editor/extensions/ext-arrows.js
  16. +92 −0 editor/extensions/ext-closepath.js
  17. +587 −0 editor/extensions/ext-connector.js
  18. +109 −0 editor/extensions/ext-eyedropper.js
  19. +277 −0 editor/extensions/ext-foreignobject.js
  20. +184 −0 editor/extensions/ext-grid.js
  21. +78 −0 editor/extensions/ext-helloworld.js
  22. +444 −0 editor/extensions/ext-imagelib.js
  23. +14 −0 editor/extensions/ext-imagelib.xml
  24. +572 −0 editor/extensions/ext-markers.js
  25. +56 −0 editor/extensions/ext-server_moinsave.js
  26. +180 −0 editor/extensions/ext-server_opensave.js
  27. +387 −0 editor/extensions/ext-shapes.js
  28. +10 −0 editor/extensions/ext-shapes.xml
  29. +34 −0 editor/extensions/eyedropper-icon.xml
  30. BIN editor/extensions/eyedropper.png
  31. +31 −0 editor/extensions/fileopen.php
  32. +44 −0 editor/extensions/filesave.php
  33. +96 −0 editor/extensions/foreignobject-icons.xml
  34. +30 −0 editor/extensions/grid-icon.xml
  35. +21 −0 editor/extensions/helloworld-icon.xml
  36. +64 −0 editor/extensions/imagelib/index.html
  37. +12 −0 editor/extensions/imagelib/smiley.svg
  38. +115 −0 editor/extensions/markers-icons.xml
  39. +21 −0 editor/extensions/shapelib/animal.json
  40. +28 −0 editor/extensions/shapelib/arrow.json
  41. +9 −0 editor/extensions/shapelib/dialog_balloon.json
  42. +20 −0 editor/extensions/shapelib/electronics.json
  43. +25 −0 editor/extensions/shapelib/flowchart.json
  44. +13 −0 editor/extensions/shapelib/game.json
  45. +9 −0 editor/extensions/shapelib/math.json
  46. +37 −0 editor/extensions/shapelib/misc.json
  47. +21 −0 editor/extensions/shapelib/music.json
  48. +19 −0 editor/extensions/shapelib/object.json
  49. +12 −0 editor/extensions/shapelib/raphael.txt
  50. +67 −0 editor/extensions/shapelib/raphael_1.json
  51. +64 −0 editor/extensions/shapelib/raphael_2.json
  52. +28 −0 editor/extensions/shapelib/symbol.json
  53. +601 −0 editor/history.js
  54. +61 −0 editor/images/README.txt
  55. BIN editor/images/align-bottom.png
  56. +277 −0 editor/images/align-bottom.svg
  57. BIN editor/images/align-center.png
  58. +252 −0 editor/images/align-center.svg
  59. BIN editor/images/align-left.png
  60. +235 −0 editor/images/align-left.svg
  61. BIN editor/images/align-middle.png
  62. +250 −0 editor/images/align-middle.svg
  63. BIN editor/images/align-right.png
  64. +233 −0 editor/images/align-right.svg
  65. BIN editor/images/align-top.png
  66. +233 −0 editor/images/align-top.svg
  67. BIN editor/images/bold.png
  68. BIN editor/images/cancel.png
  69. BIN editor/images/circle.png
  70. BIN editor/images/clear.png
  71. BIN editor/images/clone.png
  72. +29 −0 editor/images/conn.svg
  73. BIN editor/images/copy.png
  74. BIN editor/images/cut.png
  75. BIN editor/images/delete.png
  76. BIN editor/images/document-properties.png
  77. BIN editor/images/dropdown.gif
  78. BIN editor/images/ellipse.png
  79. BIN editor/images/eye.png
  80. BIN editor/images/fhpath.png
  81. BIN editor/images/flyouth.png
  82. BIN editor/images/flyup.gif
  83. BIN editor/images/freehand-circle.png
  84. BIN editor/images/freehand-square.png
  85. BIN editor/images/go-down.png
  86. BIN editor/images/go-up.png
  87. BIN editor/images/image.png
  88. BIN editor/images/italic.png
  89. BIN editor/images/line.png
  90. BIN editor/images/link_controls.png
  91. BIN editor/images/logo.png
  92. +32 −0 editor/images/logo.svg
  93. BIN editor/images/move_bottom.png
  94. BIN editor/images/move_top.png
  95. BIN editor/images/node_clone.png
  96. BIN editor/images/node_delete.png
  97. BIN editor/images/none.png
  98. BIN editor/images/open.png
  99. BIN editor/images/paste.png
  100. BIN editor/images/path.png
  101. BIN editor/images/polygon.png
  102. +219 −0 editor/images/polygon.svg
  103. BIN editor/images/rect.png
  104. BIN editor/images/redo.png
  105. BIN editor/images/reorient.png
  106. BIN editor/images/rotate.png
  107. BIN editor/images/save.png
  108. BIN editor/images/select.png
  109. BIN editor/images/select_node.png
  110. BIN editor/images/sep.png
  111. BIN editor/images/shape_group.png
  112. BIN editor/images/shape_ungroup.png
  113. BIN editor/images/source.png
  114. BIN editor/images/spinbtn_updn_big.png
  115. BIN editor/images/square.png
  116. +1,034 −0 editor/images/svg_edit_icons.svg
  117. BIN editor/images/svg_edit_icons.svgz
  118. BIN editor/images/text.png
  119. +157 −0 editor/images/text.svg
  120. BIN editor/images/to_path.png
  121. BIN editor/images/undo.png
  122. BIN editor/images/view-refresh.png
  123. BIN editor/images/wave.png
  124. BIN editor/images/wireframe.png
  125. BIN editor/images/zoom.png
  126. +202 −0 editor/jgraduate/LICENSE
  127. +3 −0 editor/jgraduate/README
  128. +1 −0 editor/jgraduate/css/jPicker.css
  129. +351 −0 editor/jgraduate/css/jgraduate.css
  130. BIN editor/jgraduate/images/AlphaBar.png
  131. BIN editor/jgraduate/images/Bars.png
  132. BIN editor/jgraduate/images/Maps.png
  133. BIN editor/jgraduate/images/NoColor.png
  134. BIN editor/jgraduate/images/bar-opacity.png
  135. BIN editor/jgraduate/images/map-opacity.png
  136. BIN editor/jgraduate/images/mappoint.gif
  137. BIN editor/jgraduate/images/mappoint_c.png
  138. BIN editor/jgraduate/images/mappoint_f.png
  139. BIN editor/jgraduate/images/picker.gif
  140. BIN editor/jgraduate/images/preview-opacity.png
  141. BIN editor/jgraduate/images/rangearrows.gif
  142. BIN editor/jgraduate/images/rangearrows2.gif
  143. +1 −0 editor/jgraduate/jpicker.min.js
  144. +1,175 −0 editor/jgraduate/jquery.jgraduate.js
  145. +37 −0 editor/jgraduate/jquery.jgraduate.min.js
  146. +54 −0 editor/jquery-ui/jquery-ui-1.8.17.custom.min.js
  147. +84 −0 editor/jquery-ui/jquery-ui-1.8.custom.min.js
Sorry, we could not display the entire diff because it was too big.
View
28 AUTHORS
@@ -0,0 +1,28 @@
+Awwation:
+
+Aditya Bhatt <aditya@adityabhatt.org>
+
+SVG-edit:
+
+Narendra Sisodiya <narendra.sisodiya@gmail.com>
+Pavol Rusnak <rusnakp@gmail.com>
+Jeff Schiller <codedread@gmail.com>
+Vidar Hokstad <vidar.hokstad@gmail.com>
+Alexis Deveria <adeveria@gmail.com>
+
+Translation credits:
+
+ar: Tarik Belaam <dubraise@gmail.com> (العربية)
+cs: Jan Ptacek <jan.ptacek@gmail.com> (Čeština)
+de: Reimar Bauer <rb.proj@googlemail.com> (Deutsch)
+es: Alicia Puerto <alicia.puerto.g@gmail.com> (Español)
+fa: Payman Delshad <payman@opera.com> (فارسی)
+fr: wormsxulla <wormsxulla@yahoo.com> (Français)
+fy: Wander Nauta <info@wandernauta.nl> (Frysk)
+hi: Tavish Naruka <tavishnaruka@gmail.com> (हिन्दी)
+ja: Dong <iwadon@yarhalla.jpn.org> (日本語)
+nl: Jaap Blom <jaap.blom@gmail.com> (Nederlands)
+ro: Christian Tzurcanu <christian.tzurcanu@gmail.com> (Româneşte)
+ru: Laurent Dufloux <laurent.dufloux@etu.upmc.fr> (Русский)
+sk: Pavol Rusnak <rusnakp@gmail.com> (Slovenčina)
+zh-TW: 黃瀚生 (Han Sheng Huang) <zenixls2@gmail.com> (台灣正體)
View
19 LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2009-2011 by SVG-edit authors (see AUTHORS file)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
View
0 README
No changes.
View
12 README.md
@@ -0,0 +1,12 @@
+# Awwation
+
+This is an HTML5 Prezi clone app using SVG and Javascript.
+It is made using **SVG-edit**, an awesome client-side SVG editor.
+It uses **Sozi** for animating the presentation.
+
+
+This is a **Hack** right now, and was written mostly as an experiment to determine how easy it would be to build something like Prezi using modern browser tech. The code is buggy and will hopefully become better over time. I welcome people to contribute changes once I set the code in order.
+
+The 'results' of the experiment are detailed in my blog post here: http://adityabhatt.wordpress.com/2012/04/06/writing-a-prezi-clone-with-html5-svg-and-javascript/
+
+**This is the Master branch of Awwwation. I am rewriting the code from scratch, nicely in this branch, till it gets to the state of progress in the online gh-pages branch. Then this one will be put online.**
View
27 editor/browser-not-supported.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
+<meta http-equiv="X-UA-Compatible" content="chrome=1"/>
+<link rel="icon" type="image/png" href="images/logo.png"/>
+<link rel="stylesheet" href="svg-editor.css" type="text/css"/>
+<script type="text/javascript" src="jquery.js"></script>
+<title>Browser does not support SVG | SVG-edit</title>
+</head>
+<body>
+
+<div id="browser-not-supported">
+<img style="float:left;padding:10px;" src="images/logo.png" width="48" height="48" alt="SVG-edit logo" /><br />
+<p>Sorry, but your browser does not support SVG. Below is a list of alternate browsers and versions that support SVG and SVG-edit (from <a href="http://caniuse.com/#cats=SVG">caniuse.com</a>).</p>
+<p>Try the latest version of <a href="http://www.getfirefox.com">Firefox</a>, <a href="http://www.google.com/chrome/">Google Chrome</a>, <a href="http://www.apple.com/safari/download/">Safari</a>, <a href="http://www.opera.com/download/">Opera</a> or <a href="http://windows.microsoft.com/ie9">Internet Explorer<a/>.</p>
+<p>If you are unable to install one of these and must use an old version of Internet Explorer, you can install the <a href="http://code.google.com/chrome/chromeframe/">Google Chrome Frame plugin</a>.</p>
+
+<script type="text/javascript">
+var viewportHeight =(window.innerHeight ? window.innerHeight : $(window).height()) - 140;
+document.write('<iframe width="100%" height="'+viewportHeight+'" src="http://caniuse.com/#cats=SVG"></iframe>');
+</script>
+
+</div>
+
+</body>
+</html>
View
178 editor/browser.js
@@ -0,0 +1,178 @@
+/**
+ * Package: svgedit.browser
+ *
+ * Licensed under the Apache License, Version 2
+ *
+ * Copyright(c) 2010 Jeff Schiller
+ * Copyright(c) 2010 Alexis Deveria
+ */
+
+// Dependencies:
+// 1) jQuery (for $.alert())
+
+var svgedit = svgedit || {};
+
+(function() {
+
+if (!svgedit.browser) {
+ svgedit.browser = {};
+}
+var supportsSvg_ = (function() {
+ return !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
+})();
+svgedit.browser.supportsSvg = function() { return supportsSvg_; }
+if(!svgedit.browser.supportsSvg()) {
+ window.location = "browser-not-supported.html";
+}
+else{
+
+var svgns = 'http://www.w3.org/2000/svg';
+var userAgent = navigator.userAgent;
+var svg = document.createElementNS(svgns, 'svg');
+
+// Note: Browser sniffing should only be used if no other detection method is possible
+var isOpera_ = !!window.opera;
+var isWebkit_ = userAgent.indexOf("AppleWebKit") >= 0;
+var isGecko_ = userAgent.indexOf('Gecko/') >= 0;
+var isIE_ = userAgent.indexOf('MSIE') >= 0;
+var isChrome_ = userAgent.indexOf('Chrome/') >= 0;
+var isWindows_ = userAgent.indexOf('Windows') >= 0;
+var isMac_ = userAgent.indexOf('Macintosh') >= 0;
+
+var supportsSelectors_ = (function() {
+ return !!svg.querySelector;
+})();
+
+var supportsXpath_ = (function() {
+ return !!document.evaluate;
+})();
+
+// segList functions (for FF1.5 and 2.0)
+var supportsPathReplaceItem_ = (function() {
+ var path = document.createElementNS(svgns, 'path');
+ path.setAttribute('d','M0,0 10,10');
+ var seglist = path.pathSegList;
+ var seg = path.createSVGPathSegLinetoAbs(5,5);
+ try {
+ seglist.replaceItem(seg, 0);
+ return true;
+ } catch(err) {}
+ return false;
+})();
+
+var supportsPathInsertItemBefore_ = (function() {
+ var path = document.createElementNS(svgns,'path');
+ path.setAttribute('d','M0,0 10,10');
+ var seglist = path.pathSegList;
+ var seg = path.createSVGPathSegLinetoAbs(5,5);
+ try {
+ seglist.insertItemBefore(seg, 0);
+ return true;
+ } catch(err) {}
+ return false;
+})();
+
+// text character positioning (for IE9)
+var supportsGoodTextCharPos_ = (function() {
+ var retValue = false;
+ var svgroot = document.createElementNS(svgns, 'svg');
+ var svgcontent = document.createElementNS(svgns, 'svg');
+ document.documentElement.appendChild(svgroot);
+ svgcontent.setAttribute('x', 5);
+ svgroot.appendChild(svgcontent);
+ var text = document.createElementNS(svgns,'text');
+ text.textContent = 'a';
+ svgcontent.appendChild(text);
+ var pos = text.getStartPositionOfChar(0).x;
+ document.documentElement.removeChild(svgroot);
+ return (pos === 0);
+})();
+
+var supportsPathBBox_ = (function() {
+ var svgcontent = document.createElementNS(svgns, 'svg');
+ document.documentElement.appendChild(svgcontent);
+ var path = document.createElementNS(svgns, 'path');
+ path.setAttribute('d','M0,0 C0,0 10,10 10,0');
+ svgcontent.appendChild(path);
+ var bbox = path.getBBox();
+ document.documentElement.removeChild(svgcontent);
+ return (bbox.height > 4 && bbox.height < 5);
+})();
+
+// Support for correct bbox sizing on groups with horizontal/vertical lines
+var supportsHVLineContainerBBox_ = (function() {
+ var svgcontent = document.createElementNS(svgns, 'svg');
+ document.documentElement.appendChild(svgcontent);
+ var path = document.createElementNS(svgns, 'path');
+ path.setAttribute('d','M0,0 10,0');
+ var path2 = document.createElementNS(svgns, 'path');
+ path2.setAttribute('d','M5,0 15,0');
+ var g = document.createElementNS(svgns, 'g');
+ g.appendChild(path);
+ g.appendChild(path2);
+ svgcontent.appendChild(g);
+ var bbox = g.getBBox();
+ document.documentElement.removeChild(svgcontent);
+ // Webkit gives 0, FF gives 10, Opera (correctly) gives 15
+ return (bbox.width == 15);
+})();
+
+var supportsEditableText_ = (function() {
+ // TODO: Find better way to check support for this
+ return isOpera_;
+})();
+
+var supportsGoodDecimals_ = (function() {
+ // Correct decimals on clone attributes (Opera < 10.5/win/non-en)
+ var rect = document.createElementNS(svgns, 'rect');
+ rect.setAttribute('x',.1);
+ var crect = rect.cloneNode(false);
+ var retValue = (crect.getAttribute('x').indexOf(',') == -1);
+ if(!retValue) {
+ $.alert("NOTE: This version of Opera is known to contain bugs in SVG-edit.\n\
+ Please upgrade to the <a href='http://opera.com'>latest version</a> in which the problems have been fixed.");
+ }
+ return retValue;
+})();
+
+var supportsNonScalingStroke_ = (function() {
+ var rect = document.createElementNS(svgns, 'rect');
+ rect.setAttribute('style','vector-effect:non-scaling-stroke');
+ return rect.style.vectorEffect === 'non-scaling-stroke';
+})();
+
+var supportsNativeSVGTransformLists_ = (function() {
+ var rect = document.createElementNS(svgns, 'rect');
+ var rxform = rect.transform.baseVal;
+
+ var t1 = svg.createSVGTransform();
+ rxform.appendItem(t1);
+ return rxform.getItem(0) == t1;
+})();
+
+// Public API
+
+svgedit.browser.isOpera = function() { return isOpera_; }
+svgedit.browser.isWebkit = function() { return isWebkit_; }
+svgedit.browser.isGecko = function() { return isGecko_; }
+svgedit.browser.isIE = function() { return isIE_; }
+svgedit.browser.isChrome = function() { return isChrome_; }
+svgedit.browser.isWindows = function() { return isWindows_; }
+svgedit.browser.isMac = function() { return isMac_; }
+
+svgedit.browser.supportsSelectors = function() { return supportsSelectors_; }
+svgedit.browser.supportsXpath = function() { return supportsXpath_; }
+
+svgedit.browser.supportsPathReplaceItem = function() { return supportsPathReplaceItem_; }
+svgedit.browser.supportsPathInsertItemBefore = function() { return supportsPathInsertItemBefore_; }
+svgedit.browser.supportsPathBBox = function() { return supportsPathBBox_; }
+svgedit.browser.supportsHVLineContainerBBox = function() { return supportsHVLineContainerBBox_; }
+svgedit.browser.supportsGoodTextCharPos = function() { return supportsGoodTextCharPos_; }
+svgedit.browser.supportsEditableText = function() { return supportsEditableText_; }
+svgedit.browser.supportsGoodDecimals = function() { return supportsGoodDecimals_; }
+svgedit.browser.supportsNonScalingStroke = function() { return supportsNonScalingStroke_; }
+svgedit.browser.supportsNativeTransformLists = function() { return supportsNativeSVGTransformLists_; }
+
+}
+
+})();
View
2,620 editor/canvg/canvg.js
@@ -0,0 +1,2620 @@
+/*
+ * canvg.js - Javascript SVG parser and renderer on Canvas
+ * MIT Licensed
+ * Gabe Lerner (gabelerner@gmail.com)
+ * http://code.google.com/p/canvg/
+ *
+ * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
+ */
+if(!window.console) {
+ window.console = {};
+ window.console.log = function(str) {};
+ window.console.dir = function(str) {};
+}
+
+if(!Array.prototype.indexOf){
+ Array.prototype.indexOf = function(obj){
+ for(var i=0; i<this.length; i++){
+ if(this[i]==obj){
+ return i;
+ }
+ }
+ return -1;
+ }
+}
+
+(function(){
+ // canvg(target, s)
+ // empty parameters: replace all 'svg' elements on page with 'canvas' elements
+ // target: canvas element or the id of a canvas element
+ // s: svg string, url to svg file, or xml document
+ // opts: optional hash of options
+ // ignoreMouse: true => ignore mouse events
+ // ignoreAnimation: true => ignore animations
+ // ignoreDimensions: true => does not try to resize canvas
+ // ignoreClear: true => does not clear canvas
+ // offsetX: int => draws at a x offset
+ // offsetY: int => draws at a y offset
+ // scaleWidth: int => scales horizontally to width
+ // scaleHeight: int => scales vertically to height
+ // renderCallback: function => will call the function after the first render is completed
+ // forceRedraw: function => will call the function on every frame, if it returns true, will redraw
+ this.canvg = function (target, s, opts) {
+ // no parameters
+ if (target == null && s == null && opts == null) {
+ var svgTags = document.getElementsByTagName('svg');
+ for (var i=0; i<svgTags.length; i++) {
+ var svgTag = svgTags[i];
+ var c = document.createElement('canvas');
+ c.width = svgTag.clientWidth;
+ c.height = svgTag.clientHeight;
+ svgTag.parentNode.insertBefore(c, svgTag);
+ svgTag.parentNode.removeChild(svgTag);
+ var div = document.createElement('div');
+ div.appendChild(svgTag);
+ canvg(c, div.innerHTML);
+ }
+ return;
+ }
+ opts = opts || {};
+
+ if (typeof target == 'string') {
+ target = document.getElementById(target);
+ }
+
+ // reuse class per canvas
+ var svg;
+ if (target.svg == null) {
+ svg = build();
+ target.svg = svg;
+ }
+ else {
+ svg = target.svg;
+ svg.stop();
+ }
+ svg.opts = opts;
+
+ var ctx = target.getContext('2d');
+ if (typeof(s.documentElement) != 'undefined') {
+ // load from xml doc
+ svg.loadXmlDoc(ctx, s);
+ }
+ else if (s.substr(0,1) == '<') {
+ // load from xml string
+ svg.loadXml(ctx, s);
+ }
+ else {
+ // load from url
+ svg.load(ctx, s);
+ }
+ }
+
+ function build() {
+ var svg = { };
+
+ svg.FRAMERATE = 30;
+ svg.MAX_VIRTUAL_PIXELS = 30000;
+
+ // globals
+ svg.init = function(ctx) {
+ svg.Definitions = {};
+ svg.Styles = {};
+ svg.Animations = [];
+ svg.Images = [];
+ svg.ctx = ctx;
+ svg.ViewPort = new (function () {
+ this.viewPorts = [];
+ this.Clear = function() { this.viewPorts = []; }
+ this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
+ 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; }
+ this.ComputeSize = function(d) {
+ if (d != null && typeof(d) == 'number') return d;
+ if (d == 'x') return this.width();
+ if (d == 'y') return this.height();
+ return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
+ }
+ });
+ }
+ svg.init();
+
+ // images loaded
+ svg.ImagesLoaded = function() {
+ for (var i=0; i<svg.Images.length; i++) {
+ if (!svg.Images[i].loaded) return false;
+ }
+ return true;
+ }
+
+ // trim
+ svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); }
+
+ // compress spaces
+ svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm,' '); }
+
+ // ajax
+ svg.ajax = function(url) {
+ var AJAX;
+ if(window.XMLHttpRequest){AJAX=new XMLHttpRequest();}
+ else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
+ if(AJAX){
+ AJAX.open('GET',url,false);
+ AJAX.send(null);
+ return AJAX.responseText;
+ }
+ return null;
+ }
+
+ // parse xml
+ svg.parseXml = function(xml) {
+ if (window.DOMParser)
+ {
+ var parser = new DOMParser();
+ return parser.parseFromString(xml, 'text/xml');
+ }
+ else
+ {
+ xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
+ var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
+ xmlDoc.async = 'false';
+ xmlDoc.loadXML(xml);
+ return xmlDoc;
+ }
+ }
+
+ svg.Property = function(name, value) {
+ this.name = name;
+ this.value = value;
+
+ this.hasValue = function() {
+ return (this.value != null && this.value !== '');
+ }
+
+ // return the numerical value of the property
+ this.numValue = function() {
+ if (!this.hasValue()) return 0;
+
+ var n = parseFloat(this.value);
+ if ((this.value + '').match(/%$/)) {
+ n = n / 100.0;
+ }
+ return n;
+ }
+
+ this.valueOrDefault = function(def) {
+ if (this.hasValue()) return this.value;
+ return def;
+ }
+
+ this.numValueOrDefault = function(def) {
+ if (this.hasValue()) return this.numValue();
+ return def;
+ }
+
+ /* EXTENSIONS */
+ var that = this;
+
+ // color extensions
+ this.Color = {
+ // augment the current color value with the opacity
+ addOpacity: function(opacity) {
+ var newValue = that.value;
+ if (opacity != null && opacity != '') {
+ var color = new RGBColor(that.value);
+ if (color.ok) {
+ newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')';
+ }
+ }
+ return new svg.Property(that.name, newValue);
+ }
+ }
+
+ // definition extensions
+ this.Definition = {
+ // get the definition from the definitions table
+ getDefinition: function() {
+ var name = that.value.replace(/^(url\()?#([^\)]+)\)?$/, '$2');
+ return svg.Definitions[name];
+ },
+
+ isUrl: function() {
+ return that.value.indexOf('url(') == 0
+ },
+
+ getFillStyle: function(e) {
+ var def = this.getDefinition();
+
+ // gradient
+ if (def != null && def.createGradient) {
+ return def.createGradient(svg.ctx, e);
+ }
+
+ // pattern
+ if (def != null && def.createPattern) {
+ return def.createPattern(svg.ctx, e);
+ }
+
+ return null;
+ }
+ }
+
+ // length extensions
+ this.Length = {
+ DPI: function(viewPort) {
+ return 96.0; // TODO: compute?
+ },
+
+ EM: function(viewPort) {
+ var em = 12;
+
+ var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
+ if (fontSize.hasValue()) em = fontSize.Length.toPixels(viewPort);
+
+ return em;
+ },
+
+ // get the length as pixels
+ toPixels: function(viewPort) {
+ if (!that.hasValue()) return 0;
+ var s = that.value+'';
+ if (s.match(/em$/)) return that.numValue() * this.EM(viewPort);
+ if (s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0;
+ if (s.match(/px$/)) return that.numValue();
+ if (s.match(/pt$/)) return that.numValue() * 1.25;
+ if (s.match(/pc$/)) return that.numValue() * 15;
+ if (s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54;
+ if (s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4;
+ if (s.match(/in$/)) return that.numValue() * this.DPI(viewPort);
+ if (s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort);
+ return that.numValue();
+ }
+ }
+
+ // time extensions
+ this.Time = {
+ // get the time as milliseconds
+ toMilliseconds: function() {
+ if (!that.hasValue()) return 0;
+ var s = that.value+'';
+ if (s.match(/s$/)) return that.numValue() * 1000;
+ if (s.match(/ms$/)) return that.numValue();
+ return that.numValue();
+ }
+ }
+
+ // angle extensions
+ this.Angle = {
+ // get the angle as radians
+ toRadians: function() {
+ if (!that.hasValue()) return 0;
+ var s = that.value+'';
+ if (s.match(/deg$/)) return that.numValue() * (Math.PI / 180.0);
+ if (s.match(/grad$/)) return that.numValue() * (Math.PI / 200.0);
+ if (s.match(/rad$/)) return that.numValue();
+ return that.numValue() * (Math.PI / 180.0);
+ }
+ }
+ }
+
+ // fonts
+ svg.Font = new (function() {
+ this.Styles = ['normal','italic','oblique','inherit'];
+ this.Variants = ['normal','small-caps','inherit'];
+ this.Weights = ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit'];
+
+ this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
+ var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
+ return {
+ fontFamily: fontFamily || f.fontFamily,
+ fontSize: fontSize || f.fontSize,
+ fontStyle: fontStyle || f.fontStyle,
+ fontWeight: fontWeight || f.fontWeight,
+ fontVariant: fontVariant || f.fontVariant,
+ toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
+ }
+ }
+
+ var that = this;
+ this.Parse = function(s) {
+ var f = {};
+ var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
+ var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
+ var ff = '';
+ for (var i=0; i<d.length; i++) {
+ if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
+ else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; }
+ 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; }
+ else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
+ else { if (d[i] != 'inherit') ff += d[i]; }
+ } if (ff != '') f.fontFamily = ff;
+ return f;
+ }
+ });
+
+ // points and paths
+ svg.ToNumberArray = function(s) {
+ var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
+ for (var i=0; i<a.length; i++) {
+ a[i] = parseFloat(a[i]);
+ }
+ return a;
+ }
+ svg.Point = function(x, y) {
+ this.x = x;
+ this.y = y;
+
+ this.angleTo = function(p) {
+ return Math.atan2(p.y - this.y, p.x - this.x);
+ }
+
+ this.applyTransform = function(v) {
+ var xp = this.x * v[0] + this.y * v[2] + v[4];
+ var yp = this.x * v[1] + this.y * v[3] + v[5];
+ this.x = xp;
+ this.y = yp;
+ }
+ }
+ svg.CreatePoint = function(s) {
+ var a = svg.ToNumberArray(s);
+ return new svg.Point(a[0], a[1]);
+ }
+ svg.CreatePath = function(s) {
+ var a = svg.ToNumberArray(s);
+ var path = [];
+ for (var i=0; i<a.length; i+=2) {
+ path.push(new svg.Point(a[i], a[i+1]));
+ }
+ return path;
+ }
+
+ // bounding box
+ svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want
+ this.x1 = Number.NaN;
+ this.y1 = Number.NaN;
+ this.x2 = Number.NaN;
+ this.y2 = Number.NaN;
+
+ this.x = function() { return this.x1; }
+ this.y = function() { return this.y1; }
+ this.width = function() { return this.x2 - this.x1; }
+ this.height = function() { return this.y2 - this.y1; }
+
+ this.addPoint = function(x, y) {
+ if (x != null) {
+ if (isNaN(this.x1) || isNaN(this.x2)) {
+ this.x1 = x;
+ this.x2 = x;
+ }
+ if (x < this.x1) this.x1 = x;
+ if (x > this.x2) this.x2 = x;
+ }
+
+ if (y != null) {
+ if (isNaN(this.y1) || isNaN(this.y2)) {
+ this.y1 = y;
+ this.y2 = y;
+ }
+ if (y < this.y1) this.y1 = y;
+ if (y > this.y2) this.y2 = y;
+ }
+ }
+ this.addX = function(x) { this.addPoint(x, null); }
+ this.addY = function(y) { this.addPoint(null, y); }
+
+ this.addBoundingBox = function(bb) {
+ this.addPoint(bb.x1, bb.y1);
+ this.addPoint(bb.x2, bb.y2);
+ }
+
+ this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
+ var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
+ var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
+ var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
+ var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
+ this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
+ }
+
+ this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
+ // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
+ var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
+ this.addPoint(p0[0], p0[1]);
+ this.addPoint(p3[0], p3[1]);
+
+ for (i=0; i<=1; i++) {
+ var f = function(t) {
+ return Math.pow(1-t, 3) * p0[i]
+ + 3 * Math.pow(1-t, 2) * t * p1[i]
+ + 3 * (1-t) * Math.pow(t, 2) * p2[i]
+ + Math.pow(t, 3) * p3[i];
+ }
+
+ var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
+ var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
+ var c = 3 * p1[i] - 3 * p0[i];
+
+ if (a == 0) {
+ if (b == 0) continue;
+ var t = -c / b;
+ if (0 < t && t < 1) {
+ if (i == 0) this.addX(f(t));
+ if (i == 1) this.addY(f(t));
+ }
+ continue;
+ }
+
+ var b2ac = Math.pow(b, 2) - 4 * c * a;
+ if (b2ac < 0) continue;
+ var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
+ if (0 < t1 && t1 < 1) {
+ if (i == 0) this.addX(f(t1));
+ if (i == 1) this.addY(f(t1));
+ }
+ var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
+ if (0 < t2 && t2 < 1) {
+ if (i == 0) this.addX(f(t2));
+ if (i == 1) this.addY(f(t2));
+ }
+ }
+ }
+
+ this.isPointInBox = function(x, y) {
+ return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
+ }
+
+ this.addPoint(x1, y1);
+ this.addPoint(x2, y2);
+ }
+
+ // transforms
+ svg.Transform = function(v) {
+ var that = this;
+ this.Type = {}
+
+ // translate
+ this.Type.translate = function(s) {
+ this.p = svg.CreatePoint(s);
+ this.apply = function(ctx) {
+ ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
+ }
+ this.applyToPoint = function(p) {
+ p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
+ }
+ }
+
+ // rotate
+ this.Type.rotate = function(s) {
+ var a = svg.ToNumberArray(s);
+ this.angle = new svg.Property('angle', a[0]);
+ this.cx = a[1] || 0;
+ this.cy = a[2] || 0;
+ this.apply = function(ctx) {
+ ctx.translate(this.cx, this.cy);
+ ctx.rotate(this.angle.Angle.toRadians());
+ ctx.translate(-this.cx, -this.cy);
+ }
+ this.applyToPoint = function(p) {
+ var a = this.angle.Angle.toRadians();
+ p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
+ p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
+ p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
+ }
+ }
+
+ this.Type.scale = function(s) {
+ this.p = svg.CreatePoint(s);
+ this.apply = function(ctx) {
+ ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
+ }
+ this.applyToPoint = function(p) {
+ p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
+ }
+ }
+
+ this.Type.matrix = function(s) {
+ this.m = svg.ToNumberArray(s);
+ this.apply = function(ctx) {
+ ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
+ }
+ this.applyToPoint = function(p) {
+ p.applyTransform(this.m);
+ }
+ }
+
+ this.Type.SkewBase = function(s) {
+ this.base = that.Type.matrix;
+ this.base(s);
+ this.angle = new svg.Property('angle', s);
+ }
+ this.Type.SkewBase.prototype = new this.Type.matrix;
+
+ this.Type.skewX = function(s) {
+ this.base = that.Type.SkewBase;
+ this.base(s);
+ this.m = [1, 0, Math.tan(this.angle.Angle.toRadians()), 1, 0, 0];
+ }
+ this.Type.skewX.prototype = new this.Type.SkewBase;
+
+ this.Type.skewY = function(s) {
+ this.base = that.Type.SkewBase;
+ this.base(s);
+ this.m = [1, Math.tan(this.angle.Angle.toRadians()), 0, 1, 0, 0];
+ }
+ this.Type.skewY.prototype = new this.Type.SkewBase;
+
+ this.transforms = [];
+
+ this.apply = function(ctx) {
+ for (var i=0; i<this.transforms.length; i++) {
+ this.transforms[i].apply(ctx);
+ }
+ }
+
+ this.applyToPoint = function(p) {
+ for (var i=0; i<this.transforms.length; i++) {
+ this.transforms[i].applyToPoint(p);
+ }
+ }
+
+ var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/);
+ for (var i=0; i<data.length; i++) {
+ var type = data[i].split('(')[0];
+ var s = data[i].split('(')[1].replace(')','');
+ var transform = new this.Type[type](s);
+ this.transforms.push(transform);
+ }
+ }
+
+ // aspect ratio
+ svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
+ // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
+ aspectRatio = svg.compressSpaces(aspectRatio);
+ aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer
+ var align = aspectRatio.split(' ')[0] || 'xMidYMid';
+ var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';
+
+ // calculate scale
+ var scaleX = width / desiredWidth;
+ var scaleY = height / desiredHeight;
+ var scaleMin = Math.min(scaleX, scaleY);
+ var scaleMax = Math.max(scaleX, scaleY);
+ if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
+ if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }
+
+ refX = new svg.Property('refX', refX);
+ refY = new svg.Property('refY', refY);
+ if (refX.hasValue() && refY.hasValue()) {
+ ctx.translate(-scaleMin * refX.Length.toPixels('x'), -scaleMin * refY.Length.toPixels('y'));
+ }
+ else {
+ // align
+ if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0);
+ if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0);
+ if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0);
+ if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight);
+ }
+
+ // scale
+ if (align == 'none') ctx.scale(scaleX, scaleY);
+ else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin);
+ else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax);
+
+ // translate
+ ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);
+ }
+
+ // elements
+ svg.Element = {}
+
+ svg.Element.ElementBase = function(node) {
+ this.attributes = {};
+ this.styles = {};
+ this.children = [];
+
+ // get or create attribute
+ this.attribute = function(name, createIfNotExists) {
+ var a = this.attributes[name];
+ if (a != null) return a;
+
+ a = new svg.Property(name, '');
+ if (createIfNotExists == true) this.attributes[name] = a;
+ return a;
+ }
+
+ // get or create style, crawls up node tree
+ this.style = function(name, createIfNotExists) {
+ var s = this.styles[name];
+ if (s != null) return s;
+
+ var a = this.attribute(name);
+ if (a != null && a.hasValue()) {
+ return a;
+ }
+
+ var p = this.parent;
+ if (p != null) {
+ var ps = p.style(name);
+ if (ps != null && ps.hasValue()) {
+ return ps;
+ }
+ }
+
+ s = new svg.Property(name, '');
+ if (createIfNotExists == true) this.styles[name] = s;
+ return s;
+ }
+
+ // base render
+ this.render = function(ctx) {
+ // don't render display=none
+ if (this.style('display').value == 'none') return;
+
+ // don't render visibility=hidden
+ if (this.attribute('visibility').value == 'hidden') return;
+
+ ctx.save();
+ this.setContext(ctx);
+ // mask
+ if (this.attribute('mask').hasValue()) {
+ var mask = this.attribute('mask').Definition.getDefinition();
+ if (mask != null) mask.apply(ctx, this);
+ }
+ else if (this.style('filter').hasValue()) {
+ var filter = this.style('filter').Definition.getDefinition();
+ if (filter != null) filter.apply(ctx, this);
+ }
+ else this.renderChildren(ctx);
+ this.clearContext(ctx);
+ ctx.restore();
+ }
+
+ // base set context
+ this.setContext = function(ctx) {
+ // OVERRIDE ME!
+ }
+
+ // base clear context
+ this.clearContext = function(ctx) {
+ // OVERRIDE ME!
+ }
+
+ // base render children
+ this.renderChildren = function(ctx) {
+ for (var i=0; i<this.children.length; i++) {
+ this.children[i].render(ctx);
+ }
+ }
+
+ this.addChild = function(childNode, create) {
+ var child = childNode;
+ if (create) child = svg.CreateElement(childNode);
+ child.parent = this;
+ this.children.push(child);
+ }
+
+ if (node != null && node.nodeType == 1) { //ELEMENT_NODE
+ // add children
+ for (var i=0; i<node.childNodes.length; i++) {
+ var childNode = node.childNodes[i];
+ if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
+ }
+
+ // add attributes
+ for (var i=0; i<node.attributes.length; i++) {
+ var attribute = node.attributes[i];
+ this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue);
+ }
+
+ // add tag styles
+ var styles = svg.Styles[node.nodeName];
+ if (styles != null) {
+ for (var name in styles) {
+ this.styles[name] = styles[name];
+ }
+ }
+
+ // add class styles
+ if (this.attribute('class').hasValue()) {
+ var classes = svg.compressSpaces(this.attribute('class').value).split(' ');
+ for (var j=0; j<classes.length; j++) {
+ styles = svg.Styles['.'+classes[j]];
+ if (styles != null) {
+ for (var name in styles) {
+ this.styles[name] = styles[name];
+ }
+ }
+ styles = svg.Styles[node.nodeName+'.'+classes[j]];
+ if (styles != null) {
+ for (var name in styles) {
+ this.styles[name] = styles[name];
+ }
+ }
+ }
+ }
+
+ // add inline styles
+ if (this.attribute('style').hasValue()) {
+ var styles = this.attribute('style').value.split(';');
+ for (var i=0; i<styles.length; i++) {
+ if (svg.trim(styles[i]) != '') {
+ var style = styles[i].split(':');
+ var name = svg.trim(style[0]);
+ var value = svg.trim(style[1]);
+ this.styles[name] = new svg.Property(name, value);
+ }
+ }
+ }
+
+ // add id
+ if (this.attribute('id').hasValue()) {
+ if (svg.Definitions[this.attribute('id').value] == null) {
+ svg.Definitions[this.attribute('id').value] = this;
+ }
+ }
+ }
+ }
+
+ svg.Element.RenderedElementBase = function(node) {
+ this.base = svg.Element.ElementBase;
+ this.base(node);
+
+ this.setContext = function(ctx) {
+ // fill
+ if (this.style('fill').Definition.isUrl()) {
+ var fs = this.style('fill').Definition.getFillStyle(this);
+ if (fs != null) ctx.fillStyle = fs;
+ }
+ else if (this.style('fill').hasValue()) {
+ var fillStyle = this.style('fill');
+ if (this.style('fill-opacity').hasValue()) fillStyle = fillStyle.Color.addOpacity(this.style('fill-opacity').value);
+ ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
+ }
+
+ // stroke
+ if (this.style('stroke').Definition.isUrl()) {
+ var fs = this.style('stroke').Definition.getFillStyle(this);
+ if (fs != null) ctx.strokeStyle = fs;
+ }
+ else if (this.style('stroke').hasValue()) {
+ var strokeStyle = this.style('stroke');
+ if (this.style('stroke-opacity').hasValue()) strokeStyle = strokeStyle.Color.addOpacity(this.style('stroke-opacity').value);
+ ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
+ }
+ if (this.style('stroke-width').hasValue()) ctx.lineWidth = this.style('stroke-width').Length.toPixels();
+ if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
+ if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
+ if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
+
+ // font
+ if (typeof(ctx.font) != 'undefined') {
+ ctx.font = svg.Font.CreateFont(
+ this.style('font-style').value,
+ this.style('font-variant').value,
+ this.style('font-weight').value,
+ this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' : '',
+ this.style('font-family').value).toString();
+ }
+
+ // transform
+ if (this.attribute('transform').hasValue()) {
+ var transform = new svg.Transform(this.attribute('transform').value);
+ transform.apply(ctx);
+ }
+
+ // clip
+ if (this.attribute('clip-path').hasValue()) {
+ var clip = this.attribute('clip-path').Definition.getDefinition();
+ if (clip != null) clip.apply(ctx);
+ }
+
+ // opacity
+ if (this.style('opacity').hasValue()) {
+ ctx.globalAlpha = this.style('opacity').numValue();
+ }
+ }
+ }
+ svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
+
+ svg.Element.PathElementBase = function(node) {
+ this.base = svg.Element.RenderedElementBase;
+ this.base(node);
+
+ this.path = function(ctx) {
+ if (ctx != null) ctx.beginPath();
+ return new svg.BoundingBox();
+ }
+
+ this.renderChildren = function(ctx) {
+ this.path(ctx);
+ svg.Mouse.checkPath(this, ctx);
+ if (ctx.fillStyle != '') ctx.fill();
+ if (ctx.strokeStyle != '') ctx.stroke();
+
+ var markers = this.getMarkers();
+ if (markers != null) {
+ if (this.style('marker-start').Definition.isUrl()) {
+ var marker = this.style('marker-start').Definition.getDefinition();
+ marker.render(ctx, markers[0][0], markers[0][1]);
+ }
+ if (this.style('marker-mid').Definition.isUrl()) {
+ var marker = this.style('marker-mid').Definition.getDefinition();
+ for (var i=1;i<markers.length-1;i++) {
+ marker.render(ctx, markers[i][0], markers[i][1]);
+ }
+ }
+ if (this.style('marker-end').Definition.isUrl()) {
+ var marker = this.style('marker-end').Definition.getDefinition();
+ marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]);
+ }
+ }
+ }
+
+ this.getBoundingBox = function() {
+ return this.path();
+ }
+
+ this.getMarkers = function() {
+ return null;
+ }
+ }
+ svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
+
+ // svg element
+ svg.Element.svg = function(node) {
+ this.base = svg.Element.RenderedElementBase;
+ this.base(node);
+
+ this.baseClearContext = this.clearContext;
+ this.clearContext = function(ctx) {
+ this.baseClearContext(ctx);
+ svg.ViewPort.RemoveCurrent();
+ }
+
+ this.baseSetContext = this.setContext;
+ this.setContext = function(ctx) {
+ // initial values
+ ctx.strokeStyle = 'rgba(0,0,0,0)';
+ ctx.lineCap = 'butt';
+ ctx.lineJoin = 'miter';
+ ctx.miterLimit = 4;
+
+ this.baseSetContext(ctx);
+
+ // create new view port
+ if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
+ ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y'));
+ }
+
+ var width = svg.ViewPort.width();
+ var height = svg.ViewPort.height();
+ if (typeof(this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
+ width = this.attribute('width').Length.toPixels('x');
+ height = this.attribute('height').Length.toPixels('y');
+
+ var x = 0;
+ var y = 0;
+ if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
+ x = -this.attribute('refX').Length.toPixels('x');
+ y = -this.attribute('refY').Length.toPixels('y');
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(width, y);
+ ctx.lineTo(width, height);
+ ctx.lineTo(x, height);
+ ctx.closePath();
+ ctx.clip();
+ }
+ svg.ViewPort.SetCurrent(width, height);
+
+ // viewbox
+ if (this.attribute('viewBox').hasValue()) {
+ var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
+ var minX = viewBox[0];
+ var minY = viewBox[1];
+ width = viewBox[2];
+ height = viewBox[3];
+
+ svg.AspectRatio(ctx,
+ this.attribute('preserveAspectRatio').value,
+ svg.ViewPort.width(),
+ width,
+ svg.ViewPort.height(),
+ height,
+ minX,
+ minY,
+ this.attribute('refX').value,
+ this.attribute('refY').value);
+
+ svg.ViewPort.RemoveCurrent();
+ svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
+ }
+ }
+ }
+ svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
+
+ // rect element
+ svg.Element.rect = function(node) {
+ this.base = svg.Element.PathElementBase;
+ this.base(node);
+
+ this.path = function(ctx) {
+ var x = this.attribute('x').Length.toPixels('x');
+ var y = this.attribute('y').Length.toPixels('y');
+ var width = this.attribute('width').Length.toPixels('x');
+ var height = this.attribute('height').Length.toPixels('y');
+ var rx = this.attribute('rx').Length.toPixels('x');
+ var ry = this.attribute('ry').Length.toPixels('y');
+ if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
+ if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
+
+ if (ctx != null) {
+ ctx.beginPath();
+ ctx.moveTo(x + rx, y);
+ ctx.lineTo(x + width - rx, y);
+ ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
+ ctx.lineTo(x + width, y + height - ry);
+ ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
+ ctx.lineTo(x + rx, y + height);
+ ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
+ ctx.lineTo(x, y + ry);
+ ctx.quadraticCurveTo(x, y, x + rx, y)
+ ctx.closePath();
+ }
+
+ return new svg.BoundingBox(x, y, x + width, y + height);
+ }
+ }
+ svg.Element.rect.prototype = new svg.Element.PathElementBase;
+
+ // circle element
+ svg.Element.circle = function(node) {
+ this.base = svg.Element.PathElementBase;
+ this.base(node);
+
+ this.path = function(ctx) {
+ var cx = this.attribute('cx').Length.toPixels('x');
+ var cy = this.attribute('cy').Length.toPixels('y');
+ var r = this.attribute('r').Length.toPixels();
+
+ if (ctx != null) {
+ ctx.beginPath();
+ ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
+ ctx.closePath();
+ }
+
+ return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
+ }
+ }
+ svg.Element.circle.prototype = new svg.Element.PathElementBase;
+
+ // ellipse element
+ svg.Element.ellipse = function(node) {
+ this.base = svg.Element.PathElementBase;
+ this.base(node);
+
+ this.path = function(ctx) {
+ var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
+ var rx = this.attribute('rx').Length.toPixels('x');
+ var ry = this.attribute('ry').Length.toPixels('y');
+ var cx = this.attribute('cx').Length.toPixels('x');
+ var cy = this.attribute('cy').Length.toPixels('y');
+
+ if (ctx != null) {
+ ctx.beginPath();
+ ctx.moveTo(cx, cy - ry);
+ ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy);
+ ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
+ ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
+ ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
+ ctx.closePath();
+ }
+
+ return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
+ }
+ }
+ svg.Element.ellipse.prototype = new svg.Element.PathElementBase;
+
+ // line element
+ svg.Element.line = function(node) {
+ this.base = svg.Element.PathElementBase;
+ this.base(node);
+
+ this.getPoints = function() {
+ return [
+ new svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')),
+ new svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))];
+ }
+
+ this.path = function(ctx) {
+ var points = this.getPoints();
+
+ if (ctx != null) {
+ ctx.beginPath();
+ ctx.moveTo(points[0].x, points[0].y);
+ ctx.lineTo(points[1].x, points[1].y);
+ }
+
+ return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
+ }
+
+ this.getMarkers = function() {
+ var points = this.getPoints();
+ var a = points[0].angleTo(points[1]);
+ return [[points[0], a], [points[1], a]];
+ }
+ }
+ svg.Element.line.prototype = new svg.Element.PathElementBase;
+
+ // polyline element
+ svg.Element.polyline = function(node) {
+ this.base = svg.Element.PathElementBase;
+ this.base(node);
+
+ this.points = svg.CreatePath(this.attribute('points').value);
+ this.path = function(ctx) {
+ var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
+ if (ctx != null) {
+ ctx.beginPath();
+ ctx.moveTo(this.points[0].x, this.points[0].y);
+ }
+ for (var i=1; i<this.points.length; i++) {
+ bb.addPoint(this.points[i].x, this.points[i].y);
+ if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
+ }
+ return bb;
+ }
+
+ this.getMarkers = function() {
+ var markers = [];
+ for (var i=0; i<this.points.length - 1; i++) {
+ markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
+ }
+ markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
+ return markers;
+ }
+ }
+ svg.Element.polyline.prototype = new svg.Element.PathElementBase;
+
+ // polygon element
+ svg.Element.polygon = function(node) {
+ this.base = svg.Element.polyline;
+ this.base(node);
+
+ this.basePath = this.path;
+ this.path = function(ctx) {
+ var bb = this.basePath(ctx);
+ if (ctx != null) {
+ ctx.lineTo(this.points[0].x, this.points[0].y);
+ ctx.closePath();
+ }
+ return bb;
+ }
+ }
+ svg.Element.polygon.prototype = new svg.Element.polyline;
+
+ // path element
+ svg.Element.path = function(node) {
+ this.base = svg.Element.PathElementBase;
+ this.base(node);
+
+ var d = this.attribute('d').value;
+ // TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
+ d = d.replace(/,/gm,' '); // get rid of all commas
+ d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
+ d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
+ d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points
+ d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
+ d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma
+ d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma
+ d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
+ d = svg.compressSpaces(d); // compress multiple spaces
+ d = svg.trim(d);
+ this.PathParser = new (function(d) {
+ this.tokens = d.split(' ');
+
+ this.reset = function() {
+ this.i = -1;
+ this.command = '';
+ this.previousCommand = '';
+ this.start = new svg.Point(0, 0);
+ this.control = new svg.Point(0, 0);
+ this.current = new svg.Point(0, 0);
+ this.points = [];
+ this.angles = [];
+ }
+
+ this.isEnd = function() {
+ return this.i >= this.tokens.length - 1;
+ }
+
+ this.isCommandOrEnd = function() {
+ if (this.isEnd()) return true;
+ return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
+ }
+
+ this.isRelativeCommand = function() {
+ return this.command == this.command.toLowerCase();
+ }
+
+ this.getToken = function() {
+ this.i = this.i + 1;
+ return this.tokens[this.i];
+ }
+
+ this.getScalar = function() {
+ return parseFloat(this.getToken());
+ }
+
+ this.nextCommand = function() {
+ this.previousCommand = this.command;
+ this.command = this.getToken();
+ }
+
+ this.getPoint = function() {
+ var p = new svg.Point(this.getScalar(), this.getScalar());
+ return this.makeAbsolute(p);
+ }
+
+ this.getAsControlPoint = function() {
+ var p = this.getPoint();
+ this.control = p;
+ return p;
+ }
+
+ this.getAsCurrentPoint = function() {
+ var p = this.getPoint();
+ this.current = p;
+ return p;
+ }
+
+ this.getReflectedControlPoint = function() {
+ if (this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') {
+ return this.current;
+ }
+
+ // reflect point
+ var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
+ return p;
+ }
+
+ this.makeAbsolute = function(p) {
+ if (this.isRelativeCommand()) {
+ p.x = this.current.x + p.x;
+ p.y = this.current.y + p.y;
+ }
+ return p;
+ }
+
+ this.addMarker = function(p, from, priorTo) {
+ // if the last angle isn't filled in because we didn't have this point yet ...
+ if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
+ this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
+ }
+ this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
+ }
+
+ this.addMarkerAngle = function(p, a) {
+ this.points.push(p);
+ this.angles.push(a);
+ }
+
+ this.getMarkerPoints = function() { return this.points; }
+ this.getMarkerAngles = function() {
+ for (var i=0; i<this.angles.length; i++) {
+ if (this.angles[i] == null) {
+ for (var j=i+1; j<this.angles.length; j++) {
+ if (this.angles[j] != null) {
+ this.angles[i] = this.angles[j];
+ break;
+ }
+ }
+ }
+ }
+ return this.angles;
+ }
+ })(d);
+
+ this.path = function(ctx) {
+ var pp = this.PathParser;
+ pp.reset();
+
+ var bb = new svg.BoundingBox();
+ if (ctx != null) ctx.beginPath();
+ while (!pp.isEnd()) {
+ pp.nextCommand();
+ switch (pp.command.toUpperCase()) {
+ case 'M':
+ var p = pp.getAsCurrentPoint();
+ pp.addMarker(p);
+ bb.addPoint(p.x, p.y);
+ if (ctx != null) ctx.moveTo(p.x, p.y);
+ pp.start = pp.current;
+ while (!pp.isCommandOrEnd()) {
+ var p = pp.getAsCurrentPoint();
+ pp.addMarker(p, pp.start);
+ bb.addPoint(p.x, p.y);
+ if (ctx != null) ctx.lineTo(p.x, p.y);
+ }
+ break;
+ case 'L':
+ while (!pp.isCommandOrEnd()) {
+ var c = pp.current;
+ var p = pp.getAsCurrentPoint();
+ pp.addMarker(p, c);
+ bb.addPoint(p.x, p.y);
+ if (ctx != null) ctx.lineTo(p.x, p.y);
+ }
+ break;
+ case 'H':
+ while (!pp.isCommandOrEnd()) {
+ var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
+ pp.addMarker(newP, pp.current);
+ pp.current = newP;
+ bb.addPoint(pp.current.x, pp.current.y);
+ if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
+ }
+ break;
+ case 'V':
+ while (!pp.isCommandOrEnd()) {
+ var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
+ pp.addMarker(newP, pp.current);
+ pp.current = newP;
+ bb.addPoint(pp.current.x, pp.current.y);
+ if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
+ }
+ break;
+ case 'C':
+ while (!pp.isCommandOrEnd()) {
+ var curr = pp.current;
+ var p1 = pp.getPoint();
+ var cntrl = pp.getAsControlPoint();
+ var cp = pp.getAsCurrentPoint();
+ pp.addMarker(cp, cntrl, p1);
+ bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
+ if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
+ }
+ break;
+ case 'S':
+ while (!pp.isCommandOrEnd()) {
+ var curr = pp.current;
+ var p1 = pp.getReflectedControlPoint();
+ var cntrl = pp.getAsControlPoint();
+ var cp = pp.getAsCurrentPoint();
+ pp.addMarker(cp, cntrl, p1);
+ bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
+ if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
+ }
+ break;
+ case 'Q':
+ while (!pp.isCommandOrEnd()) {
+ var curr = pp.current;
+ var cntrl = pp.getAsControlPoint();
+ var cp = pp.getAsCurrentPoint();
+ pp.addMarker(cp, cntrl, cntrl);
+ bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
+ if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
+ }
+ break;
+ case 'T':
+ while (!pp.isCommandOrEnd()) {
+ var curr = pp.current;
+ var cntrl = pp.getReflectedControlPoint();
+ pp.control = cntrl;
+ var cp = pp.getAsCurrentPoint();
+ pp.addMarker(cp, cntrl, cntrl);
+ bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
+ if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
+ }
+ break;
+ case 'A':
+ while (!pp.isCommandOrEnd()) {
+ var curr = pp.current;
+ var rx = pp.getScalar();
+ var ry = pp.getScalar();
+ var xAxisRotation = pp.getScalar() * (Math.PI / 180.0);
+ var largeArcFlag = pp.getScalar();
+ var sweepFlag = pp.getScalar();
+ var cp = pp.getAsCurrentPoint();
+
+ // Conversion from endpoint to center parameterization
+ // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+ // x1', y1'
+ var currp = new svg.Point(
+ Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0,
+ -Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0
+ );
+ // adjust radii
+ var l = Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2);
+ if (l > 1) {
+ rx *= Math.sqrt(l);
+ ry *= Math.sqrt(l);
+ }
+ // cx', cy'
+ var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
+ ((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))) /
+ (Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
+ );
+ if (isNaN(s)) s = 0;
+ var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
+ // cx, cy
+ var centp = new svg.Point(
+ (curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
+ (curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
+ );
+ // vector magnitude
+ var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
+ // ratio between two vectors
+ var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
+ // angle between two vectors
+ var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); }
+ // initial angle
+ var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
+ // angle delta
+ var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
+ var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
+ var ad = a(u, v);
+ if (r(u,v) <= -1) ad = Math.PI;
+ if (r(u,v) >= 1) ad = 0;
+
+ if (sweepFlag == 0 && ad > 0) ad = ad - 2 * Math.PI;
+ if (sweepFlag == 1 && ad < 0) ad = ad + 2 * Math.PI;
+
+ // for markers
+ var halfWay = new svg.Point(
+ centp.x - rx * Math.cos((a1 + ad) / 2),
+ centp.y - ry * Math.sin((a1 + ad) / 2)
+ );
+ pp.addMarkerAngle(halfWay, (a1 + ad) / 2 + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
+ pp.addMarkerAngle(cp, ad + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
+
+ bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
+ if (ctx != null) {
+ var r = rx > ry ? rx : ry;
+ var sx = rx > ry ? 1 : rx / ry;
+ var sy = rx > ry ? ry / rx : 1;
+
+ ctx.translate(centp.x, centp.y);
+ ctx.rotate(xAxisRotation);
+ ctx.scale(sx, sy);
+ ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
+ ctx.scale(1/sx, 1/sy);
+ ctx.rotate(-xAxisRotation);
+ ctx.translate(-centp.x, -centp.y);
+ }
+ }
+ break;
+ case 'Z':
+ if (ctx != null) ctx.closePath();
+ pp.current = pp.start;
+ }
+ }
+
+ return bb;
+ }
+
+ this.getMarkers = function() {
+ var points = this.PathParser.getMarkerPoints();
+ var angles = this.PathParser.getMarkerAngles();
+
+ var markers = [];
+ for (var i=0; i<points.length; i++) {
+ markers.push([points[i], angles[i]]);
+ }
+ return markers;
+ }
+ }
+ svg.Element.path.prototype = new svg.Element.PathElementBase;
+
+ // pattern element
+ svg.Element.pattern = function(node) {
+ this.base = svg.Element.ElementBase;
+ this.base(node);
+
+ this.createPattern = function(ctx, element) {
+ // render me using a temporary svg element
+ var tempSvg = new svg.Element.svg();
+ tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
+ tempSvg.attributes['x'] = new svg.Property('x', this.attribute('x').value);
+ tempSvg.attributes['y'] = new svg.Property('y', this.attribute('y').value);
+ tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value);
+ tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value);
+ tempSvg.children = this.children;
+
+ var c = document.createElement('canvas');
+ c.width = this.attribute('width').Length.toPixels('x');
+ c.height = this.attribute('height').Length.toPixels('y');
+ tempSvg.render(c.getContext('2d'));
+ return ctx.createPattern(c, 'repeat');
+ }
+ }
+ svg.Element.pattern.prototype = new svg.Element.ElementBase;
+
+ // marker element
+ svg.Element.marker = function(node) {
+ this.base = svg.Element.ElementBase;
+ this.base(node);
+
+ this.baseRender = this.render;
+ this.render = function(ctx, point, angle) {
+ ctx.translate(point.x, point.y);
+ if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle);
+ if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth);
+ ctx.save();
+
+ // render me using a temporary svg element
+ var tempSvg = new svg.Element.svg();
+ tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
+ tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value);
+ tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value);
+ tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value);
+ tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value);
+ tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black'));
+ tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
+ tempSvg.children = this.children;
+ tempSvg.render(ctx);
+
+ ctx.restore();
+ if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1/ctx.lineWidth, 1/ctx.lineWidth);
+ if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle);
+ ctx.translate(-point.x, -point.y);
+ }
+ }
+ svg.Element.marker.prototype = new svg.Element.ElementBase;
+
+ // definitions element
+ svg.Element.defs = function(node) {
+ this.base = svg.Element.ElementBase;
+ this.base(node);
+
+ this.render = function(ctx) {
+ // NOOP
+ }
+ }
+ svg.Element.defs.prototype = new svg.Element.ElementBase;
+
+ // base for gradients
+ svg.Element.GradientBase = function(node) {
+ this.base = svg.Element.ElementBase;
+ this.base(node);
+
+ this.gradientUnits = this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
+
+ this.stops = [];
+ for (var i=0; i<this.children.length; i++) {
+ var child = this.children[i];
+ this.stops.push(child);
+ }
+
+ this.getGradient = function() {
+ // OVERRIDE ME!
+ }
+
+ this.createGradient = function(ctx, element) {
+ var stopsContainer = this;
+ if (this.attribute('xlink:href').hasValue()) {
+ stopsContainer = this.attribute('xlink:href').Definition.getDefinition();
+ }
+
+ var g = this.getGradient(ctx, element);
+ for (var i=0; i<stopsContainer.stops.length; i++) {
+ g.addColorStop(stopsContainer.stops[i].offset, stopsContainer.stops[i].color);
+ }
+
+ if (this.attribute('gradientTransform').hasValue()) {
+ // render as transformed pattern on temporary canvas
+ var rootView = svg.ViewPort.viewPorts[0];
+
+ var rect = new svg.Element.rect();
+ rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0);
+ rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0);
+ rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS);
+ rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS);
+
+ var group = new svg.Element.g();
+ group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value);
+ group.children = [ rect ];
+
+ var tempSvg = new svg.Element.svg();
+ tempSvg.attributes['x'] = new svg.Property('x', 0);
+ tempSvg.attributes['y'] = new svg.Property('y', 0);
+ tempSvg.attributes['width'] = new svg.Property('width', rootView.width);
+ tempSvg.attributes['height'] = new svg.Property('height', rootView.height);
+ tempSvg.children = [ group ];
+
+ var c = document.createElement('canvas');
+ c.width = rootView.width;
+ c.height = rootView.height;
+ var tempCtx = c.getContext('2d');
+ tempCtx.fillStyle = g;
+ tempSvg.render(tempCtx);
+ return tempCtx.createPattern(c, 'no-repeat');
+ }
+
+ return g;
+ }
+ }
+ svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
+
+ // linear gradient element
+ svg.Element.linearGradient = function(node) {
+ this.base = svg.Element.GradientBase;
+ this.base(node);
+
+ this.getGradient = function(ctx, element) {
+ var bb = element.getBoundingBox();
+
+ var x1 = (this.gradientUnits == 'objectBoundingBox'
+ ? bb.x() + bb.width() * this.attribute('x1').numValue()
+ : this.attribute('x1').Length.toPixels('x'));
+ var y1 = (this.gradientUnits == 'objectBoundingBox'
+ ? bb.y() + bb.height() * this.attribute('y1').numValue()
+ : this.attribute('y1').Length.toPixels('y'));
+ var x2 = (this.gradientUnits == 'objectBoundingBox'
+ ? bb.x() + bb.width() * this.attribute('x2').numValue()
+ : this.attribute('x2').Length.toPixels('x'));
+ var y2 = (this.gradientUnits == 'objectBoundingBox'
+ ? bb.y() + bb.height() * this.attribute('y2').numValue()
+ : this.attribute('y2').Length.toPixels('y'));
+
+ return ctx.createLinearGradient(x1, y1, x2, y2);
+ }
+ }
+ svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
+
+ // radial gradient element
+ svg.Element.radialGradient =