canvg.js 97 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842
  1. /*
  2. * canvg.js - Javascript SVG parser and renderer on Canvas
  3. * MIT Licensed
  4. * Gabe Lerner (gabelerner@gmail.com)
  5. * http://code.google.com/p/canvg/
  6. *
  7. * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
  8. */
  9. (function(){
  10. // canvg(target, s)
  11. // empty parameters: replace all 'svg' elements on page with 'canvas' elements
  12. // target: canvas element or the id of a canvas element
  13. // s: svg string, url to svg file, or xml document
  14. // opts: optional hash of options
  15. // ignoreMouse: true => ignore mouse events
  16. // ignoreAnimation: true => ignore animations
  17. // ignoreDimensions: true => does not try to resize canvas
  18. // ignoreClear: true => does not clear canvas
  19. // offsetX: int => draws at a x offset
  20. // offsetY: int => draws at a y offset
  21. // scaleWidth: int => scales horizontally to width
  22. // scaleHeight: int => scales vertically to height
  23. // renderCallback: function => will call the function after the first render is completed
  24. // forceRedraw: function => will call the function on every frame, if it returns true, will redraw
  25. this.canvg = function (target, s, opts) {
  26. // no parameters
  27. if (target == null && s == null && opts == null) {
  28. var svgTags = document.getElementsByTagName('svg');
  29. for (var i=0; i<svgTags.length; i++) {
  30. var svgTag = svgTags[i];
  31. var c = document.createElement('canvas');
  32. c.width = svgTag.clientWidth;
  33. c.height = svgTag.clientHeight;
  34. svgTag.parentNode.insertBefore(c, svgTag);
  35. svgTag.parentNode.removeChild(svgTag);
  36. var div = document.createElement('div');
  37. div.appendChild(svgTag);
  38. canvg(c, div.innerHTML);
  39. }
  40. return;
  41. }
  42. opts = opts || {};
  43. if (typeof target == 'string') {
  44. target = document.getElementById(target);
  45. }
  46. // store class on canvas
  47. if (target.svg != null) target.svg.stop();
  48. var svg = build();
  49. // on i.e. 8 for flash canvas, we can't assign the property so check for it
  50. if (!(target.childNodes.length == 1 && target.childNodes[0].nodeName == 'OBJECT')) target.svg = svg;
  51. svg.opts = opts;
  52. var ctx = target.getContext('2d');
  53. if (typeof(s.documentElement) != 'undefined') {
  54. // load from xml doc
  55. svg.loadXmlDoc(ctx, s);
  56. }
  57. else if (s.substr(0,1) == '<') {
  58. // load from xml string
  59. svg.loadXml(ctx, s);
  60. }
  61. else {
  62. // load from url
  63. svg.load(ctx, s);
  64. }
  65. }
  66. function build() {
  67. var svg = { };
  68. svg.FRAMERATE = 30;
  69. svg.MAX_VIRTUAL_PIXELS = 30000;
  70. // globals
  71. svg.init = function(ctx) {
  72. var uniqueId = 0;
  73. svg.UniqueId = function () { uniqueId++; return 'canvg' + uniqueId; };
  74. svg.Definitions = {};
  75. svg.Styles = {};
  76. svg.Animations = [];
  77. svg.Images = [];
  78. svg.ctx = ctx;
  79. svg.ViewPort = new (function () {
  80. this.viewPorts = [];
  81. this.Clear = function() { this.viewPorts = []; }
  82. this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
  83. this.RemoveCurrent = function() { this.viewPorts.pop(); }
  84. this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }
  85. this.width = function() { return this.Current().width; }
  86. this.height = function() { return this.Current().height; }
  87. this.ComputeSize = function(d) {
  88. if (d != null && typeof(d) == 'number') return d;
  89. if (d == 'x') return this.width();
  90. if (d == 'y') return this.height();
  91. return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
  92. }
  93. });
  94. }
  95. svg.init();
  96. // images loaded
  97. svg.ImagesLoaded = function() {
  98. for (var i=0; i<svg.Images.length; i++) {
  99. if (!svg.Images[i].loaded) return false;
  100. }
  101. return true;
  102. }
  103. // trim
  104. svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); }
  105. // compress spaces
  106. svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm,' '); }
  107. // ajax
  108. svg.ajax = function(url) {
  109. var AJAX;
  110. if(window.XMLHttpRequest){AJAX=new XMLHttpRequest();}
  111. else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
  112. if(AJAX){
  113. AJAX.open('GET',url,false);
  114. AJAX.send(null);
  115. return AJAX.responseText;
  116. }
  117. return null;
  118. }
  119. // parse xml
  120. svg.parseXml = function(xml) {
  121. if (window.DOMParser)
  122. {
  123. var parser = new DOMParser();
  124. return parser.parseFromString(xml, 'text/xml');
  125. }
  126. else
  127. {
  128. xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
  129. var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
  130. xmlDoc.async = 'false';
  131. xmlDoc.loadXML(xml);
  132. return xmlDoc;
  133. }
  134. }
  135. svg.Property = function(name, value) {
  136. this.name = name;
  137. this.value = value;
  138. }
  139. svg.Property.prototype.getValue = function() {
  140. return this.value;
  141. }
  142. svg.Property.prototype.hasValue = function() {
  143. return (this.value != null && this.value !== '');
  144. }
  145. // return the numerical value of the property
  146. svg.Property.prototype.numValue = function() {
  147. if (!this.hasValue()) return 0;
  148. var n = parseFloat(this.value);
  149. if ((this.value + '').match(/%$/)) {
  150. n = n / 100.0;
  151. }
  152. return n;
  153. }
  154. svg.Property.prototype.valueOrDefault = function(def) {
  155. if (this.hasValue()) return this.value;
  156. return def;
  157. }
  158. svg.Property.prototype.numValueOrDefault = function(def) {
  159. if (this.hasValue()) return this.numValue();
  160. return def;
  161. }
  162. // color extensions
  163. // augment the current color value with the opacity
  164. svg.Property.prototype.addOpacity = function(opacity) {
  165. var newValue = this.value;
  166. if (opacity != null && opacity != '' && typeof(this.value)=='string') { // can only add opacity to colors, not patterns
  167. var color = new RGBColor(this.value);
  168. if (color.ok) {
  169. newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')';
  170. }
  171. }
  172. return new svg.Property(this.name, newValue);
  173. }
  174. // definition extensions
  175. // get the definition from the definitions table
  176. svg.Property.prototype.getDefinition = function() {
  177. var name = this.value.match(/#([^\)'"]+)/);
  178. if (name) { name = name[1]; }
  179. if (!name) { name = this.value; }
  180. return svg.Definitions[name];
  181. }
  182. svg.Property.prototype.isUrlDefinition = function() {
  183. return this.value.indexOf('url(') == 0
  184. }
  185. svg.Property.prototype.getFillStyleDefinition = function(e, opacityProp) {
  186. var def = this.getDefinition();
  187. // gradient
  188. if (def != null && def.createGradient) {
  189. return def.createGradient(svg.ctx, e, opacityProp);
  190. }
  191. // pattern
  192. if (def != null && def.createPattern) {
  193. if (def.getHrefAttribute().hasValue()) {
  194. var pt = def.attribute('patternTransform');
  195. def = def.getHrefAttribute().getDefinition();
  196. if (pt.hasValue()) { def.attribute('patternTransform', true).value = pt.value; }
  197. }
  198. return def.createPattern(svg.ctx, e);
  199. }
  200. return null;
  201. }
  202. // length extensions
  203. svg.Property.prototype.getDPI = function(viewPort) {
  204. return 96.0; // TODO: compute?
  205. }
  206. svg.Property.prototype.getEM = function(viewPort) {
  207. var em = 12;
  208. var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
  209. if (fontSize.hasValue()) em = fontSize.toPixels(viewPort);
  210. return em;
  211. }
  212. svg.Property.prototype.getUnits = function() {
  213. var s = this.value+'';
  214. return s.replace(/[0-9\.\-]/g,'');
  215. }
  216. // get the length as pixels
  217. svg.Property.prototype.toPixels = function(viewPort, processPercent) {
  218. if (!this.hasValue()) return 0;
  219. var s = this.value+'';
  220. if (s.match(/em$/)) return this.numValue() * this.getEM(viewPort);
  221. if (s.match(/ex$/)) return this.numValue() * this.getEM(viewPort) / 2.0;
  222. if (s.match(/px$/)) return this.numValue();
  223. if (s.match(/pt$/)) return this.numValue() * this.getDPI(viewPort) * (1.0 / 72.0);
  224. if (s.match(/pc$/)) return this.numValue() * 15;
  225. if (s.match(/cm$/)) return this.numValue() * this.getDPI(viewPort) / 2.54;
  226. if (s.match(/mm$/)) return this.numValue() * this.getDPI(viewPort) / 25.4;
  227. if (s.match(/in$/)) return this.numValue() * this.getDPI(viewPort);
  228. if (s.match(/%$/)) return this.numValue() * svg.ViewPort.ComputeSize(viewPort);
  229. var n = this.numValue();
  230. if (processPercent && n < 1.0) return n * svg.ViewPort.ComputeSize(viewPort);
  231. return n;
  232. }
  233. // time extensions
  234. // get the time as milliseconds
  235. svg.Property.prototype.toMilliseconds = function() {
  236. if (!this.hasValue()) return 0;
  237. var s = this.value+'';
  238. if (s.match(/s$/)) return this.numValue() * 1000;
  239. if (s.match(/ms$/)) return this.numValue();
  240. return this.numValue();
  241. }
  242. // angle extensions
  243. // get the angle as radians
  244. svg.Property.prototype.toRadians = function() {
  245. if (!this.hasValue()) return 0;
  246. var s = this.value+'';
  247. if (s.match(/deg$/)) return this.numValue() * (Math.PI / 180.0);
  248. if (s.match(/grad$/)) return this.numValue() * (Math.PI / 200.0);
  249. if (s.match(/rad$/)) return this.numValue();
  250. return this.numValue() * (Math.PI / 180.0);
  251. }
  252. // fonts
  253. svg.Font = new (function() {
  254. this.Styles = 'normal|italic|oblique|inherit';
  255. this.Variants = 'normal|small-caps|inherit';
  256. this.Weights = 'normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit';
  257. this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
  258. var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
  259. return {
  260. fontFamily: fontFamily || f.fontFamily,
  261. fontSize: fontSize || f.fontSize,
  262. fontStyle: fontStyle || f.fontStyle,
  263. fontWeight: fontWeight || f.fontWeight,
  264. fontVariant: fontVariant || f.fontVariant,
  265. toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
  266. }
  267. }
  268. var that = this;
  269. this.Parse = function(s) {
  270. var f = {};
  271. var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
  272. var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
  273. var ff = '';
  274. for (var i=0; i<d.length; i++) {
  275. if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
  276. else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; }
  277. 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; }
  278. else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
  279. else { if (d[i] != 'inherit') ff += d[i]; }
  280. } if (ff != '') f.fontFamily = ff;
  281. return f;
  282. }
  283. });
  284. // points and paths
  285. svg.ToNumberArray = function(s) {
  286. var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
  287. for (var i=0; i<a.length; i++) {
  288. a[i] = parseFloat(a[i]);
  289. }
  290. return a;
  291. }
  292. svg.Point = function(x, y) {
  293. this.x = x;
  294. this.y = y;
  295. }
  296. svg.Point.prototype.angleTo = function(p) {
  297. return Math.atan2(p.y - this.y, p.x - this.x);
  298. }
  299. svg.Point.prototype.applyTransform = function(v) {
  300. var xp = this.x * v[0] + this.y * v[2] + v[4];
  301. var yp = this.x * v[1] + this.y * v[3] + v[5];
  302. this.x = xp;
  303. this.y = yp;
  304. }
  305. svg.CreatePoint = function(s) {
  306. var a = svg.ToNumberArray(s);
  307. return new svg.Point(a[0], a[1]);
  308. }
  309. svg.CreatePath = function(s) {
  310. var a = svg.ToNumberArray(s);
  311. var path = [];
  312. for (var i=0; i<a.length; i+=2) {
  313. path.push(new svg.Point(a[i], a[i+1]));
  314. }
  315. return path;
  316. }
  317. // bounding box
  318. svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want
  319. this.x1 = Number.NaN;
  320. this.y1 = Number.NaN;
  321. this.x2 = Number.NaN;
  322. this.y2 = Number.NaN;
  323. this.x = function() { return this.x1; }
  324. this.y = function() { return this.y1; }
  325. this.width = function() { return this.x2 - this.x1; }
  326. this.height = function() { return this.y2 - this.y1; }
  327. this.addPoint = function(x, y) {
  328. if (x != null) {
  329. if (isNaN(this.x1) || isNaN(this.x2)) {
  330. this.x1 = x;
  331. this.x2 = x;
  332. }
  333. if (x < this.x1) this.x1 = x;
  334. if (x > this.x2) this.x2 = x;
  335. }
  336. if (y != null) {
  337. if (isNaN(this.y1) || isNaN(this.y2)) {
  338. this.y1 = y;
  339. this.y2 = y;
  340. }
  341. if (y < this.y1) this.y1 = y;
  342. if (y > this.y2) this.y2 = y;
  343. }
  344. }
  345. this.addX = function(x) { this.addPoint(x, null); }
  346. this.addY = function(y) { this.addPoint(null, y); }
  347. this.addBoundingBox = function(bb) {
  348. this.addPoint(bb.x1, bb.y1);
  349. this.addPoint(bb.x2, bb.y2);
  350. }
  351. this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
  352. var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
  353. var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
  354. var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
  355. var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
  356. this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
  357. }
  358. this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
  359. // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
  360. var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
  361. this.addPoint(p0[0], p0[1]);
  362. this.addPoint(p3[0], p3[1]);
  363. for (i=0; i<=1; i++) {
  364. var f = function(t) {
  365. return Math.pow(1-t, 3) * p0[i]
  366. + 3 * Math.pow(1-t, 2) * t * p1[i]
  367. + 3 * (1-t) * Math.pow(t, 2) * p2[i]
  368. + Math.pow(t, 3) * p3[i];
  369. }
  370. var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
  371. var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
  372. var c = 3 * p1[i] - 3 * p0[i];
  373. if (a == 0) {
  374. if (b == 0) continue;
  375. var t = -c / b;
  376. if (0 < t && t < 1) {
  377. if (i == 0) this.addX(f(t));
  378. if (i == 1) this.addY(f(t));
  379. }
  380. continue;
  381. }
  382. var b2ac = Math.pow(b, 2) - 4 * c * a;
  383. if (b2ac < 0) continue;
  384. var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
  385. if (0 < t1 && t1 < 1) {
  386. if (i == 0) this.addX(f(t1));
  387. if (i == 1) this.addY(f(t1));
  388. }
  389. var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
  390. if (0 < t2 && t2 < 1) {
  391. if (i == 0) this.addX(f(t2));
  392. if (i == 1) this.addY(f(t2));
  393. }
  394. }
  395. }
  396. this.isPointInBox = function(x, y) {
  397. return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
  398. }
  399. this.addPoint(x1, y1);
  400. this.addPoint(x2, y2);
  401. }
  402. // transforms
  403. svg.Transform = function(v) {
  404. var that = this;
  405. this.Type = {}
  406. // translate
  407. this.Type.translate = function(s) {
  408. this.p = svg.CreatePoint(s);
  409. this.apply = function(ctx) {
  410. ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
  411. }
  412. this.unapply = function(ctx) {
  413. ctx.translate(-1.0 * this.p.x || 0.0, -1.0 * this.p.y || 0.0);
  414. }
  415. this.applyToPoint = function(p) {
  416. p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
  417. }
  418. }
  419. // rotate
  420. this.Type.rotate = function(s) {
  421. var a = svg.ToNumberArray(s);
  422. this.angle = new svg.Property('angle', a[0]);
  423. this.cx = a[1] || 0;
  424. this.cy = a[2] || 0;
  425. this.apply = function(ctx) {
  426. ctx.translate(this.cx, this.cy);
  427. ctx.rotate(this.angle.toRadians());
  428. ctx.translate(-this.cx, -this.cy);
  429. }
  430. this.unapply = function(ctx) {
  431. ctx.translate(this.cx, this.cy);
  432. ctx.rotate(-1.0 * this.angle.toRadians());
  433. ctx.translate(-this.cx, -this.cy);
  434. }
  435. this.applyToPoint = function(p) {
  436. var a = this.angle.toRadians();
  437. p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
  438. p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
  439. p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
  440. }
  441. }
  442. this.Type.scale = function(s) {
  443. this.p = svg.CreatePoint(s);
  444. this.apply = function(ctx) {
  445. ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
  446. }
  447. this.unapply = function(ctx) {
  448. ctx.scale(1.0 / this.p.x || 1.0, 1.0 / this.p.y || this.p.x || 1.0);
  449. }
  450. this.applyToPoint = function(p) {
  451. p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
  452. }
  453. }
  454. this.Type.matrix = function(s) {
  455. this.m = svg.ToNumberArray(s);
  456. this.apply = function(ctx) {
  457. ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
  458. }
  459. this.applyToPoint = function(p) {
  460. p.applyTransform(this.m);
  461. }
  462. }
  463. this.Type.SkewBase = function(s) {
  464. this.base = that.Type.matrix;
  465. this.base(s);
  466. this.angle = new svg.Property('angle', s);
  467. }
  468. this.Type.SkewBase.prototype = new this.Type.matrix;
  469. this.Type.skewX = function(s) {
  470. this.base = that.Type.SkewBase;
  471. this.base(s);
  472. this.m = [1, 0, Math.tan(this.angle.toRadians()), 1, 0, 0];
  473. }
  474. this.Type.skewX.prototype = new this.Type.SkewBase;
  475. this.Type.skewY = function(s) {
  476. this.base = that.Type.SkewBase;
  477. this.base(s);
  478. this.m = [1, Math.tan(this.angle.toRadians()), 0, 1, 0, 0];
  479. }
  480. this.Type.skewY.prototype = new this.Type.SkewBase;
  481. this.transforms = [];
  482. this.apply = function(ctx) {
  483. for (var i=0; i<this.transforms.length; i++) {
  484. this.transforms[i].apply(ctx);
  485. }
  486. }
  487. this.unapply = function(ctx) {
  488. for (var i=this.transforms.length-1; i>=0; i--) {
  489. this.transforms[i].unapply(ctx);
  490. }
  491. }
  492. this.applyToPoint = function(p) {
  493. for (var i=0; i<this.transforms.length; i++) {
  494. this.transforms[i].applyToPoint(p);
  495. }
  496. }
  497. var data = svg.trim(svg.compressSpaces(v)).replace(/\)(\s?,\s?)/g,') ').split(/\s(?=[a-z])/);
  498. for (var i=0; i<data.length; i++) {
  499. var type = svg.trim(data[i].split('(')[0]);
  500. var s = data[i].split('(')[1].replace(')','');
  501. var transform = new this.Type[type](s);
  502. transform.type = type;
  503. this.transforms.push(transform);
  504. }
  505. }
  506. // aspect ratio
  507. svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
  508. // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
  509. aspectRatio = svg.compressSpaces(aspectRatio);
  510. aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer
  511. var align = aspectRatio.split(' ')[0] || 'xMidYMid';
  512. var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';
  513. // calculate scale
  514. var scaleX = width / desiredWidth;
  515. var scaleY = height / desiredHeight;
  516. var scaleMin = Math.min(scaleX, scaleY);
  517. var scaleMax = Math.max(scaleX, scaleY);
  518. if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
  519. if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }
  520. refX = new svg.Property('refX', refX);
  521. refY = new svg.Property('refY', refY);
  522. if (refX.hasValue() && refY.hasValue()) {
  523. ctx.translate(-scaleMin * refX.toPixels('x'), -scaleMin * refY.toPixels('y'));
  524. }
  525. else {
  526. // align
  527. if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0);
  528. if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0);
  529. if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0);
  530. if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight);
  531. }
  532. // scale
  533. if (align == 'none') ctx.scale(scaleX, scaleY);
  534. else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin);
  535. else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax);
  536. // translate
  537. ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);
  538. }
  539. // elements
  540. svg.Element = {}
  541. svg.EmptyProperty = new svg.Property('EMPTY', '');
  542. svg.Element.ElementBase = function(node) {
  543. this.attributes = {};
  544. this.styles = {};
  545. this.children = [];
  546. // get or create attribute
  547. this.attribute = function(name, createIfNotExists) {
  548. var a = this.attributes[name];
  549. if (a != null) return a;
  550. if (createIfNotExists == true) { a = new svg.Property(name, ''); this.attributes[name] = a; }
  551. return a || svg.EmptyProperty;
  552. }
  553. this.getHrefAttribute = function() {
  554. for (var a in this.attributes) {
  555. if (a.match(/:href$/)) {
  556. return this.attributes[a];
  557. }
  558. }
  559. return svg.EmptyProperty;
  560. }
  561. // get or create style, crawls up node tree
  562. this.style = function(name, createIfNotExists) {
  563. var s = this.styles[name];
  564. if (s != null) return s;
  565. var a = this.attribute(name);
  566. if (a != null && a.hasValue()) {
  567. this.styles[name] = a; // move up to me to cache
  568. return a;
  569. }
  570. var p = this.parent;
  571. if (p != null) {
  572. var ps = p.style(name);
  573. if (ps != null && ps.hasValue()) {
  574. return ps;
  575. }
  576. }
  577. if (createIfNotExists == true) { s = new svg.Property(name, ''); this.styles[name] = s; }
  578. return s || svg.EmptyProperty;
  579. }
  580. // base render
  581. this.render = function(ctx) {
  582. // don't render display=none
  583. if (this.style('display').value == 'none') return;
  584. // don't render visibility=hidden
  585. if (this.attribute('visibility').value == 'hidden') return;
  586. ctx.save();
  587. if (this.attribute('mask').hasValue()) { // mask
  588. var mask = this.attribute('mask').getDefinition();
  589. if (mask != null) mask.apply(ctx, this);
  590. }
  591. else if (this.style('filter').hasValue()) { // filter
  592. var filter = this.style('filter').getDefinition();
  593. if (filter != null) filter.apply(ctx, this);
  594. }
  595. else {
  596. this.setContext(ctx);
  597. this.renderChildren(ctx);
  598. this.clearContext(ctx);
  599. }
  600. ctx.restore();
  601. }
  602. // base set context
  603. this.setContext = function(ctx) {
  604. // OVERRIDE ME!
  605. }
  606. // base clear context
  607. this.clearContext = function(ctx) {
  608. // OVERRIDE ME!
  609. }
  610. // base render children
  611. this.renderChildren = function(ctx) {
  612. for (var i=0; i<this.children.length; i++) {
  613. this.children[i].render(ctx);
  614. }
  615. }
  616. this.addChild = function(childNode, create) {
  617. var child = childNode;
  618. if (create) child = svg.CreateElement(childNode);
  619. child.parent = this;
  620. this.children.push(child);
  621. }
  622. if (node != null && node.nodeType == 1) { //ELEMENT_NODE
  623. // add children
  624. for (var i=0; i<node.childNodes.length; i++) {
  625. var childNode = node.childNodes[i];
  626. if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
  627. if (this.captureTextNodes && childNode.nodeType == 3) {
  628. var text = childNode.nodeValue || childNode.text || '';
  629. if (svg.trim(svg.compressSpaces(text)) != '') {
  630. this.addChild(new svg.Element.tspan(childNode), false); // TEXT_NODE
  631. }
  632. }
  633. }
  634. // add attributes
  635. for (var i=0; i<node.attributes.length; i++) {
  636. var attribute = node.attributes[i];
  637. this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue);
  638. }
  639. // add tag styles
  640. var styles = svg.Styles[node.nodeName];
  641. if (styles != null) {
  642. for (var name in styles) {
  643. this.styles[name] = styles[name];
  644. }
  645. }
  646. // add class styles
  647. if (this.attribute('class').hasValue()) {
  648. var classes = svg.compressSpaces(this.attribute('class').value).split(' ');
  649. for (var j=0; j<classes.length; j++) {
  650. styles = svg.Styles['.'+classes[j]];
  651. if (styles != null) {
  652. for (var name in styles) {
  653. this.styles[name] = styles[name];
  654. }
  655. }
  656. styles = svg.Styles[node.nodeName+'.'+classes[j]];
  657. if (styles != null) {
  658. for (var name in styles) {
  659. this.styles[name] = styles[name];
  660. }
  661. }
  662. }
  663. }
  664. // add id styles
  665. if (this.attribute('id').hasValue()) {
  666. var styles = svg.Styles['#' + this.attribute('id').value];
  667. if (styles != null) {
  668. for (var name in styles) {
  669. this.styles[name] = styles[name];
  670. }
  671. }
  672. }
  673. // add inline styles
  674. if (this.attribute('style').hasValue()) {
  675. var styles = this.attribute('style').value.split(';');
  676. for (var i=0; i<styles.length; i++) {
  677. if (svg.trim(styles[i]) != '') {
  678. var style = styles[i].split(':');
  679. var name = svg.trim(style[0]);
  680. var value = svg.trim(style[1]);
  681. this.styles[name] = new svg.Property(name, value);
  682. }
  683. }
  684. }
  685. // add id
  686. if (this.attribute('id').hasValue()) {
  687. if (svg.Definitions[this.attribute('id').value] == null) {
  688. svg.Definitions[this.attribute('id').value] = this;
  689. }
  690. }
  691. }
  692. }
  693. svg.Element.RenderedElementBase = function(node) {
  694. this.base = svg.Element.ElementBase;
  695. this.base(node);
  696. this.setContext = function(ctx) {
  697. // fill
  698. if (this.style('fill').isUrlDefinition()) {
  699. var fs = this.style('fill').getFillStyleDefinition(this, this.style('fill-opacity'));
  700. if (fs != null) ctx.fillStyle = fs;
  701. }
  702. else if (this.style('fill').hasValue()) {
  703. var fillStyle = this.style('fill');
  704. if (fillStyle.value == 'currentColor') fillStyle.value = this.style('color').value;
  705. ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
  706. }
  707. if (this.style('fill-opacity').hasValue()) {
  708. var fillStyle = new svg.Property('fill', ctx.fillStyle);
  709. fillStyle = fillStyle.addOpacity(this.style('fill-opacity').value);
  710. ctx.fillStyle = fillStyle.value;
  711. }
  712. // stroke
  713. if (this.style('stroke').isUrlDefinition()) {
  714. var fs = this.style('stroke').getFillStyleDefinition(this, this.style('stroke-opacity'));
  715. if (fs != null) ctx.strokeStyle = fs;
  716. }
  717. else if (this.style('stroke').hasValue()) {
  718. var strokeStyle = this.style('stroke');
  719. if (strokeStyle.value == 'currentColor') strokeStyle.value = this.style('color').value;
  720. ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
  721. }
  722. if (this.style('stroke-opacity').hasValue()) {
  723. var strokeStyle = new svg.Property('stroke', ctx.strokeStyle);
  724. strokeStyle = strokeStyle.addOpacity(this.style('stroke-opacity').value);
  725. ctx.strokeStyle = strokeStyle.value;
  726. }
  727. if (this.style('stroke-width').hasValue()) {
  728. var newLineWidth = this.style('stroke-width').toPixels();
  729. ctx.lineWidth = newLineWidth == 0 ? 0.001 : newLineWidth; // browsers don't respect 0
  730. }
  731. if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
  732. if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
  733. if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
  734. if (this.style('stroke-dasharray').hasValue()) {
  735. var gaps = svg.ToNumberArray(this.style('stroke-dasharray').value);
  736. if (typeof(ctx.setLineDash) != 'undefined') { ctx.setLineDash(gaps); }
  737. else if (typeof(ctx.webkitLineDash) != 'undefined') { ctx.webkitLineDash = gaps; }
  738. else if (typeof(ctx.mozDash ) != 'undefined') { ctx.mozDash = gaps; }
  739. var offset = this.style('stroke-dashoffset').numValueOrDefault(1);
  740. if (typeof(ctx.lineDashOffset) != 'undefined') { ctx.lineDashOffset = offset; }
  741. else if (typeof(ctx.webkitLineDashOffset) != 'undefined') { ctx.webkitLineDashOffset = offset; }
  742. else if (typeof(ctx.mozDashOffset) != 'undefined') { ctx.mozDashOffset = offset; }
  743. }
  744. // font
  745. if (typeof(ctx.font) != 'undefined') {
  746. ctx.font = svg.Font.CreateFont(
  747. this.style('font-style').value,
  748. this.style('font-variant').value,
  749. this.style('font-weight').value,
  750. this.style('font-size').hasValue() ? this.style('font-size').toPixels() + 'px' : '',
  751. this.style('font-family').value).toString();
  752. }
  753. // transform
  754. if (this.attribute('transform').hasValue()) {
  755. var transform = new svg.Transform(this.attribute('transform').value);
  756. transform.apply(ctx);
  757. }
  758. // clip
  759. if (this.style('clip-path').hasValue()) {
  760. var clip = this.style('clip-path').getDefinition();
  761. if (clip != null) clip.apply(ctx);
  762. }
  763. // opacity
  764. if (this.style('opacity').hasValue()) {
  765. ctx.globalAlpha = this.style('opacity').numValue();
  766. }
  767. }
  768. }
  769. svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
  770. svg.Element.PathElementBase = function(node) {
  771. this.base = svg.Element.RenderedElementBase;
  772. this.base(node);
  773. this.path = function(ctx) {
  774. if (ctx != null) ctx.beginPath();
  775. return new svg.BoundingBox();
  776. }
  777. this.renderChildren = function(ctx) {
  778. this.path(ctx);
  779. svg.Mouse.checkPath(this, ctx);
  780. if (ctx.fillStyle != '') {
  781. if (this.attribute('fill-rule').hasValue()) { ctx.fill(this.attribute('fill-rule').value); }
  782. else { ctx.fill(); }
  783. }
  784. if (ctx.strokeStyle != '') ctx.stroke();
  785. var markers = this.getMarkers();
  786. if (markers != null) {
  787. if (this.style('marker-start').isUrlDefinition()) {
  788. var marker = this.style('marker-start').getDefinition();
  789. marker.render(ctx, markers[0][0], markers[0][1]);
  790. }
  791. if (this.style('marker-mid').isUrlDefinition()) {
  792. var marker = this.style('marker-mid').getDefinition();
  793. for (var i=1;i<markers.length-1;i++) {
  794. marker.render(ctx, markers[i][0], markers[i][1]);
  795. }
  796. }
  797. if (this.style('marker-end').isUrlDefinition()) {
  798. var marker = this.style('marker-end').getDefinition();
  799. marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]);
  800. }
  801. }
  802. }
  803. this.getBoundingBox = function() {
  804. return this.path();
  805. }
  806. this.getMarkers = function() {
  807. return null;
  808. }
  809. }
  810. svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
  811. // svg element
  812. svg.Element.svg = function(node) {
  813. this.base = svg.Element.RenderedElementBase;
  814. this.base(node);
  815. this.baseClearContext = this.clearContext;
  816. this.clearContext = function(ctx) {
  817. this.baseClearContext(ctx);
  818. svg.ViewPort.RemoveCurrent();
  819. }
  820. this.baseSetContext = this.setContext;
  821. this.setContext = function(ctx) {
  822. // initial values
  823. ctx.strokeStyle = 'rgba(0,0,0,0)';
  824. ctx.lineCap = 'butt';
  825. ctx.lineJoin = 'miter';
  826. ctx.miterLimit = 4;
  827. this.baseSetContext(ctx);
  828. // create new view port
  829. if (!this.attribute('x').hasValue()) this.attribute('x', true).value = 0;
  830. if (!this.attribute('y').hasValue()) this.attribute('y', true).value = 0;
  831. ctx.translate(this.attribute('x').toPixels('x'), this.attribute('y').toPixels('y'));
  832. var width = svg.ViewPort.width();
  833. var height = svg.ViewPort.height();
  834. if (!this.attribute('width').hasValue()) this.attribute('width', true).value = '100%';
  835. if (!this.attribute('height').hasValue()) this.attribute('height', true).value = '100%';
  836. if (typeof(this.root) == 'undefined') {
  837. width = this.attribute('width').toPixels('x');
  838. height = this.attribute('height').toPixels('y');
  839. var x = 0;
  840. var y = 0;
  841. if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
  842. x = -this.attribute('refX').toPixels('x');
  843. y = -this.attribute('refY').toPixels('y');
  844. }
  845. ctx.beginPath();
  846. ctx.moveTo(x, y);
  847. ctx.lineTo(width, y);
  848. ctx.lineTo(width, height);
  849. ctx.lineTo(x, height);
  850. ctx.closePath();
  851. ctx.clip();
  852. }
  853. svg.ViewPort.SetCurrent(width, height);
  854. // viewbox
  855. if (this.attribute('viewBox').hasValue()) {
  856. var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
  857. var minX = viewBox[0];
  858. var minY = viewBox[1];
  859. width = viewBox[2];
  860. height = viewBox[3];
  861. svg.AspectRatio(ctx,
  862. this.attribute('preserveAspectRatio').value,
  863. svg.ViewPort.width(),
  864. width,
  865. svg.ViewPort.height(),
  866. height,
  867. minX,
  868. minY,
  869. this.attribute('refX').value,
  870. this.attribute('refY').value);
  871. svg.ViewPort.RemoveCurrent();
  872. svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
  873. }
  874. }
  875. }
  876. svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
  877. // rect element
  878. svg.Element.rect = function(node) {
  879. this.base = svg.Element.PathElementBase;
  880. this.base(node);
  881. this.path = function(ctx) {
  882. var x = this.attribute('x').toPixels('x');
  883. var y = this.attribute('y').toPixels('y');
  884. var width = this.attribute('width').toPixels('x');
  885. var height = this.attribute('height').toPixels('y');
  886. var rx = this.attribute('rx').toPixels('x');
  887. var ry = this.attribute('ry').toPixels('y');
  888. if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
  889. if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
  890. rx = Math.min(rx, width / 2.0);
  891. ry = Math.min(ry, height / 2.0);
  892. if (ctx != null) {
  893. ctx.beginPath();
  894. ctx.moveTo(x + rx, y);
  895. ctx.lineTo(x + width - rx, y);
  896. ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
  897. ctx.lineTo(x + width, y + height - ry);
  898. ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
  899. ctx.lineTo(x + rx, y + height);
  900. ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
  901. ctx.lineTo(x, y + ry);
  902. ctx.quadraticCurveTo(x, y, x + rx, y)
  903. ctx.closePath();
  904. }
  905. return new svg.BoundingBox(x, y, x + width, y + height);
  906. }
  907. }
  908. svg.Element.rect.prototype = new svg.Element.PathElementBase;
  909. // circle element
  910. svg.Element.circle = function(node) {
  911. this.base = svg.Element.PathElementBase;
  912. this.base(node);
  913. this.path = function(ctx) {
  914. var cx = this.attribute('cx').toPixels('x');
  915. var cy = this.attribute('cy').toPixels('y');
  916. var r = this.attribute('r').toPixels();
  917. if (ctx != null) {
  918. ctx.beginPath();
  919. ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
  920. ctx.closePath();
  921. }
  922. return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
  923. }
  924. }
  925. svg.Element.circle.prototype = new svg.Element.PathElementBase;
  926. // ellipse element
  927. svg.Element.ellipse = function(node) {
  928. this.base = svg.Element.PathElementBase;
  929. this.base(node);
  930. this.path = function(ctx) {
  931. var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
  932. var rx = this.attribute('rx').toPixels('x');
  933. var ry = this.attribute('ry').toPixels('y');
  934. var cx = this.attribute('cx').toPixels('x');
  935. var cy = this.attribute('cy').toPixels('y');
  936. if (ctx != null) {
  937. ctx.beginPath();
  938. ctx.moveTo(cx, cy - ry);
  939. ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy);
  940. ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
  941. ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
  942. ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
  943. ctx.closePath();
  944. }
  945. return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
  946. }
  947. }
  948. svg.Element.ellipse.prototype = new svg.Element.PathElementBase;
  949. // line element
  950. svg.Element.line = function(node) {
  951. this.base = svg.Element.PathElementBase;
  952. this.base(node);
  953. this.getPoints = function() {
  954. return [
  955. new svg.Point(this.attribute('x1').toPixels('x'), this.attribute('y1').toPixels('y')),
  956. new svg.Point(this.attribute('x2').toPixels('x'), this.attribute('y2').toPixels('y'))];
  957. }
  958. this.path = function(ctx) {
  959. var points = this.getPoints();
  960. if (ctx != null) {
  961. ctx.beginPath();
  962. ctx.moveTo(points[0].x, points[0].y);
  963. ctx.lineTo(points[1].x, points[1].y);
  964. }
  965. return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
  966. }
  967. this.getMarkers = function() {
  968. var points = this.getPoints();
  969. var a = points[0].angleTo(points[1]);
  970. return [[points[0], a], [points[1], a]];
  971. }
  972. }
  973. svg.Element.line.prototype = new svg.Element.PathElementBase;
  974. // polyline element
  975. svg.Element.polyline = function(node) {
  976. this.base = svg.Element.PathElementBase;
  977. this.base(node);
  978. this.points = svg.CreatePath(this.attribute('points').value);
  979. this.path = function(ctx) {
  980. var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
  981. if (ctx != null) {
  982. ctx.beginPath();
  983. ctx.moveTo(this.points[0].x, this.points[0].y);
  984. }
  985. for (var i=1; i<this.points.length; i++) {
  986. bb.addPoint(this.points[i].x, this.points[i].y);
  987. if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
  988. }
  989. return bb;
  990. }
  991. this.getMarkers = function() {
  992. var markers = [];
  993. for (var i=0; i<this.points.length - 1; i++) {
  994. markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
  995. }
  996. markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
  997. return markers;
  998. }
  999. }
  1000. svg.Element.polyline.prototype = new svg.Element.PathElementBase;
  1001. // polygon element
  1002. svg.Element.polygon = function(node) {
  1003. this.base = svg.Element.polyline;
  1004. this.base(node);
  1005. this.basePath = this.path;
  1006. this.path = function(ctx) {
  1007. var bb = this.basePath(ctx);
  1008. if (ctx != null) {
  1009. ctx.lineTo(this.points[0].x, this.points[0].y);
  1010. ctx.closePath();
  1011. }
  1012. return bb;
  1013. }
  1014. }
  1015. svg.Element.polygon.prototype = new svg.Element.polyline;
  1016. // path element
  1017. svg.Element.path = function(node) {
  1018. this.base = svg.Element.PathElementBase;
  1019. this.base(node);
  1020. var d = this.attribute('d').value;
  1021. // TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
  1022. d = d.replace(/,/gm,' '); // get rid of all commas
  1023. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
  1024. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
  1025. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points
  1026. d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
  1027. d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma
  1028. d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma
  1029. d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
  1030. d = svg.compressSpaces(d); // compress multiple spaces
  1031. d = svg.trim(d);
  1032. this.PathParser = new (function(d) {
  1033. this.tokens = d.split(' ');
  1034. this.reset = function() {
  1035. this.i = -1;
  1036. this.command = '';
  1037. this.previousCommand = '';
  1038. this.start = new svg.Point(0, 0);
  1039. this.control = new svg.Point(0, 0);
  1040. this.current = new svg.Point(0, 0);
  1041. this.points = [];
  1042. this.angles = [];
  1043. }
  1044. this.isEnd = function() {
  1045. return this.i >= this.tokens.length - 1;
  1046. }
  1047. this.isCommandOrEnd = function() {
  1048. if (this.isEnd()) return true;
  1049. return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
  1050. }
  1051. this.isRelativeCommand = function() {
  1052. switch(this.command)
  1053. {
  1054. case 'm':
  1055. case 'l':
  1056. case 'h':
  1057. case 'v':
  1058. case 'c':
  1059. case 's':
  1060. case 'q':
  1061. case 't':
  1062. case 'a':
  1063. case 'z':
  1064. return true;
  1065. break;
  1066. }
  1067. return false;
  1068. }
  1069. this.getToken = function() {
  1070. this.i++;
  1071. return this.tokens[this.i];
  1072. }
  1073. this.getScalar = function() {
  1074. return parseFloat(this.getToken());
  1075. }
  1076. this.nextCommand = function() {
  1077. this.previousCommand = this.command;
  1078. this.command = this.getToken();
  1079. }
  1080. this.getPoint = function() {
  1081. var p = new svg.Point(this.getScalar(), this.getScalar());
  1082. return this.makeAbsolute(p);
  1083. }
  1084. this.getAsControlPoint = function() {
  1085. var p = this.getPoint();
  1086. this.control = p;
  1087. return p;
  1088. }
  1089. this.getAsCurrentPoint = function() {
  1090. var p = this.getPoint();
  1091. this.current = p;
  1092. return p;
  1093. }
  1094. this.getReflectedControlPoint = function() {
  1095. if (this.previousCommand.toLowerCase() != 'c' &&
  1096. this.previousCommand.toLowerCase() != 's' &&
  1097. this.previousCommand.toLowerCase() != 'q' &&
  1098. this.previousCommand.toLowerCase() != 't' ){
  1099. return this.current;
  1100. }
  1101. // reflect point
  1102. var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
  1103. return p;
  1104. }
  1105. this.makeAbsolute = function(p) {
  1106. if (this.isRelativeCommand()) {
  1107. p.x += this.current.x;
  1108. p.y += this.current.y;
  1109. }
  1110. return p;
  1111. }
  1112. this.addMarker = function(p, from, priorTo) {
  1113. // if the last angle isn't filled in because we didn't have this point yet ...
  1114. if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
  1115. this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
  1116. }
  1117. this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
  1118. }
  1119. this.addMarkerAngle = function(p, a) {
  1120. this.points.push(p);
  1121. this.angles.push(a);
  1122. }
  1123. this.getMarkerPoints = function() { return this.points; }
  1124. this.getMarkerAngles = function() {
  1125. for (var i=0; i<this.angles.length; i++) {
  1126. if (this.angles[i] == null) {
  1127. for (var j=i+1; j<this.angles.length; j++) {
  1128. if (this.angles[j] != null) {
  1129. this.angles[i] = this.angles[j];
  1130. break;
  1131. }
  1132. }
  1133. }
  1134. }
  1135. return this.angles;
  1136. }
  1137. })(d);
  1138. this.path = function(ctx) {
  1139. var pp = this.PathParser;
  1140. pp.reset();
  1141. var bb = new svg.BoundingBox();
  1142. if (ctx != null) ctx.beginPath();
  1143. while (!pp.isEnd()) {
  1144. pp.nextCommand();
  1145. switch (pp.command) {
  1146. case 'M':
  1147. case 'm':
  1148. var p = pp.getAsCurrentPoint();
  1149. pp.addMarker(p);
  1150. bb.addPoint(p.x, p.y);
  1151. if (ctx != null) ctx.moveTo(p.x, p.y);
  1152. pp.start = pp.current;
  1153. while (!pp.isCommandOrEnd()) {
  1154. var p = pp.getAsCurrentPoint();
  1155. pp.addMarker(p, pp.start);
  1156. bb.addPoint(p.x, p.y);
  1157. if (ctx != null) ctx.lineTo(p.x, p.y);
  1158. }
  1159. break;
  1160. case 'L':
  1161. case 'l':
  1162. while (!pp.isCommandOrEnd()) {
  1163. var c = pp.current;
  1164. var p = pp.getAsCurrentPoint();
  1165. pp.addMarker(p, c);
  1166. bb.addPoint(p.x, p.y);
  1167. if (ctx != null) ctx.lineTo(p.x, p.y);
  1168. }
  1169. break;
  1170. case 'H':
  1171. case 'h':
  1172. while (!pp.isCommandOrEnd()) {
  1173. var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
  1174. pp.addMarker(newP, pp.current);
  1175. pp.current = newP;
  1176. bb.addPoint(pp.current.x, pp.current.y);
  1177. if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
  1178. }
  1179. break;
  1180. case 'V':
  1181. case 'v':
  1182. while (!pp.isCommandOrEnd()) {
  1183. var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
  1184. pp.addMarker(newP, pp.current);
  1185. pp.current = newP;
  1186. bb.addPoint(pp.current.x, pp.current.y);
  1187. if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
  1188. }
  1189. break;
  1190. case 'C':
  1191. case 'c':
  1192. while (!pp.isCommandOrEnd()) {
  1193. var curr = pp.current;
  1194. var p1 = pp.getPoint();
  1195. var cntrl = pp.getAsControlPoint();
  1196. var cp = pp.getAsCurrentPoint();
  1197. pp.addMarker(cp, cntrl, p1);
  1198. bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1199. if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1200. }
  1201. break;
  1202. case 'S':
  1203. case 's':
  1204. while (!pp.isCommandOrEnd()) {
  1205. var curr = pp.current;
  1206. var p1 = pp.getReflectedControlPoint();
  1207. var cntrl = pp.getAsControlPoint();
  1208. var cp = pp.getAsCurrentPoint();
  1209. pp.addMarker(cp, cntrl, p1);
  1210. bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1211. if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1212. }
  1213. break;
  1214. case 'Q':
  1215. case 'q':
  1216. while (!pp.isCommandOrEnd()) {
  1217. var curr = pp.current;
  1218. var cntrl = pp.getAsControlPoint();
  1219. var cp = pp.getAsCurrentPoint();
  1220. pp.addMarker(cp, cntrl, cntrl);
  1221. bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1222. if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
  1223. }
  1224. break;
  1225. case 'T':
  1226. case 't':
  1227. while (!pp.isCommandOrEnd()) {
  1228. var curr = pp.current;
  1229. var cntrl = pp.getReflectedControlPoint();
  1230. pp.control = cntrl;
  1231. var cp = pp.getAsCurrentPoint();
  1232. pp.addMarker(cp, cntrl, cntrl);
  1233. bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1234. if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
  1235. }
  1236. break;
  1237. case 'A':
  1238. case 'a':
  1239. while (!pp.isCommandOrEnd()) {
  1240. var curr = pp.current;
  1241. var rx = pp.getScalar();
  1242. var ry = pp.getScalar();
  1243. var xAxisRotation = pp.getScalar() * (Math.PI / 180.0);
  1244. var largeArcFlag = pp.getScalar();
  1245. var sweepFlag = pp.getScalar();
  1246. var cp = pp.getAsCurrentPoint();
  1247. // Conversion from endpoint to center parameterization
  1248. // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
  1249. // x1', y1'
  1250. var currp = new svg.Point(
  1251. Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0,
  1252. -Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0
  1253. );
  1254. // adjust radii
  1255. var l = Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2);
  1256. if (l > 1) {
  1257. rx *= Math.sqrt(l);
  1258. ry *= Math.sqrt(l);
  1259. }
  1260. // cx', cy'
  1261. var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
  1262. ((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))) /
  1263. (Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
  1264. );
  1265. if (isNaN(s)) s = 0;
  1266. var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
  1267. // cx, cy
  1268. var centp = new svg.Point(
  1269. (curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
  1270. (curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
  1271. );
  1272. // vector magnitude
  1273. var m = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
  1274. // ratio between two vectors
  1275. var r = function(u, v) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
  1276. // angle between two vectors
  1277. var a = function(u, v) { return (u[0]*v[1] < u[1]*v[0] ? -1 : 1) * Math.acos(r(u,v)); }
  1278. // initial angle
  1279. var a1 = a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
  1280. // angle delta
  1281. var u = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
  1282. var v = [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
  1283. var ad = a(u, v);
  1284. if (r(u,v) <= -1) ad = Math.PI;
  1285. if (r(u,v) >= 1) ad = 0;
  1286. // for markers
  1287. var dir = 1 - sweepFlag ? 1.0 : -1.0;
  1288. var ah = a1 + dir * (ad / 2.0);
  1289. var halfWay = new svg.Point(
  1290. centp.x + rx * Math.cos(ah),
  1291. centp.y + ry * Math.sin(ah)
  1292. );
  1293. pp.addMarkerAngle(halfWay, ah - dir * Math.PI / 2);
  1294. pp.addMarkerAngle(cp, ah - dir * Math.PI);
  1295. bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
  1296. if (ctx != null) {
  1297. var r = rx > ry ? rx : ry;
  1298. var sx = rx > ry ? 1 : rx / ry;
  1299. var sy = rx > ry ? ry / rx : 1;
  1300. ctx.translate(centp.x, centp.y);
  1301. ctx.rotate(xAxisRotation);
  1302. ctx.scale(sx, sy);
  1303. ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
  1304. ctx.scale(1/sx, 1/sy);
  1305. ctx.rotate(-xAxisRotation);
  1306. ctx.translate(-centp.x, -centp.y);
  1307. }
  1308. }
  1309. break;
  1310. case 'Z':
  1311. case 'z':
  1312. if (ctx != null) ctx.closePath();
  1313. pp.current = pp.start;
  1314. }
  1315. }
  1316. return bb;
  1317. }
  1318. this.getMarkers = function() {
  1319. var points = this.PathParser.getMarkerPoints();
  1320. var angles = this.PathParser.getMarkerAngles();
  1321. var markers = [];
  1322. for (var i=0; i<points.length; i++) {
  1323. markers.push([points[i], angles[i]]);
  1324. }
  1325. return markers;
  1326. }
  1327. }
  1328. svg.Element.path.prototype = new svg.Element.PathElementBase;
  1329. // pattern element
  1330. svg.Element.pattern = function(node) {
  1331. this.base = svg.Element.ElementBase;
  1332. this.base(node);
  1333. this.createPattern = function(ctx, element) {
  1334. var width = this.attribute('width').toPixels('x', true);
  1335. var height = this.attribute('height').toPixels('y', true);
  1336. // render me using a temporary svg element
  1337. var tempSvg = new svg.Element.svg();
  1338. tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
  1339. tempSvg.attributes['width'] = new svg.Property('width', width + 'px');
  1340. tempSvg.attributes['height'] = new svg.Property('height', height + 'px');
  1341. tempSvg.attributes['transform'] = new svg.Property('transform', this.attribute('patternTransform').value);
  1342. tempSvg.children = this.children;
  1343. var c = document.createElement('canvas');
  1344. c.width = width;
  1345. c.height = height;
  1346. var cctx = c.getContext('2d');
  1347. if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
  1348. cctx.translate(this.attribute('x').toPixels('x', true), this.attribute('y').toPixels('y', true));
  1349. }
  1350. // render 3x3 grid so when we transform there's no white space on edges
  1351. for (var x=-1; x<=1; x++) {
  1352. for (var y=-1; y<=1; y++) {
  1353. cctx.save();
  1354. cctx.translate(x * c.width, y * c.height);
  1355. tempSvg.render(cctx);
  1356. cctx.restore();
  1357. }
  1358. }
  1359. var pattern = ctx.createPattern(c, 'repeat');
  1360. return pattern;
  1361. }
  1362. }
  1363. svg.Element.pattern.prototype = new svg.Element.ElementBase;
  1364. // marker element
  1365. svg.Element.marker = function(node) {
  1366. this.base = svg.Element.ElementBase;
  1367. this.base(node);
  1368. this.baseRender = this.render;
  1369. this.render = function(ctx, point, angle) {
  1370. ctx.translate(point.x, point.y);
  1371. if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle);
  1372. if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth);
  1373. ctx.save();
  1374. // render me using a temporary svg element
  1375. var tempSvg = new svg.Element.svg();
  1376. tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
  1377. tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value);
  1378. tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value);
  1379. tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value);
  1380. tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value);
  1381. tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black'));
  1382. tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
  1383. tempSvg.children = this.children;
  1384. tempSvg.render(ctx);
  1385. ctx.restore();
  1386. if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1/ctx.lineWidth, 1/ctx.lineWidth);
  1387. if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle);
  1388. ctx.translate(-point.x, -point.y);
  1389. }
  1390. }
  1391. svg.Element.marker.prototype = new svg.Element.ElementBase;
  1392. // definitions element
  1393. svg.Element.defs = function(node) {
  1394. this.base = svg.Element.ElementBase;
  1395. this.base(node);
  1396. this.render = function(ctx) {
  1397. // NOOP
  1398. }
  1399. }
  1400. svg.Element.defs.prototype = new svg.Element.ElementBase;
  1401. // base for gradients
  1402. svg.Element.GradientBase = function(node) {
  1403. this.base = svg.Element.ElementBase;
  1404. this.base(node);
  1405. this.gradientUnits = this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
  1406. this.stops = [];
  1407. for (var i=0; i<this.children.length; i++) {
  1408. var child = this.children[i];
  1409. if (child.type == 'stop') this.stops.push(child);
  1410. }
  1411. this.getGradient = function() {
  1412. // OVERRIDE ME!
  1413. }
  1414. this.createGradient = function(ctx, element, parentOpacityProp) {
  1415. var stopsContainer = this;
  1416. if (this.getHrefAttribute().hasValue()) {
  1417. stopsContainer = this.getHrefAttribute().getDefinition();
  1418. }
  1419. var addParentOpacity = function (color) {
  1420. if (parentOpacityProp.hasValue()) {
  1421. var p = new svg.Property('color', color);
  1422. return p.addOpacity(parentOpacityProp.value).value;
  1423. }
  1424. return color;
  1425. };
  1426. var g = this.getGradient(ctx, element);
  1427. if (g == null) return addParentOpacity(stopsContainer.stops[stopsContainer.stops.length - 1].color);
  1428. for (var i=0; i<stopsContainer.stops.length; i++) {
  1429. g.addColorStop(stopsContainer.stops[i].offset, addParentOpacity(stopsContainer.stops[i].color));
  1430. }
  1431. if (this.attribute('gradientTransform').hasValue()) {
  1432. // render as transformed pattern on temporary canvas
  1433. var rootView = svg.ViewPort.viewPorts[0];
  1434. var rect = new svg.Element.rect();
  1435. rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0);
  1436. rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0);
  1437. rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS);
  1438. rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS);
  1439. var group = new svg.Element.g();
  1440. group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value);
  1441. group.children = [ rect ];
  1442. var tempSvg = new svg.Element.svg();
  1443. tempSvg.attributes['x'] = new svg.Property('x', 0);
  1444. tempSvg.attributes['y'] = new svg.Property('y', 0);
  1445. tempSvg.attributes['width'] = new svg.Property('width', rootView.width);
  1446. tempSvg.attributes['height'] = new svg.Property('height', rootView.height);
  1447. tempSvg.children = [ group ];
  1448. var c = document.createElement('canvas');
  1449. c.width = rootView.width;
  1450. c.height = rootView.height;
  1451. var tempCtx = c.getContext('2d');
  1452. tempCtx.fillStyle = g;
  1453. tempSvg.render(tempCtx);
  1454. return tempCtx.createPattern(c, 'no-repeat');
  1455. }
  1456. return g;
  1457. }
  1458. }
  1459. svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
  1460. // linear gradient element
  1461. svg.Element.linearGradient = function(node) {
  1462. this.base = svg.Element.GradientBase;
  1463. this.base(node);
  1464. this.getGradient = function(ctx, element) {
  1465. var bb = element.getBoundingBox();
  1466. if (!this.attribute('x1').hasValue()
  1467. && !this.attribute('y1').hasValue()
  1468. && !this.attribute('x2').hasValue()
  1469. && !this.attribute('y2').hasValue()) {
  1470. this.attribute('x1', true).value = 0;
  1471. this.attribute('y1', true).value = 0;
  1472. this.attribute('x2', true).value = 1;
  1473. this.attribute('y2', true).value = 0;
  1474. }
  1475. var x1 = (this.gradientUnits == 'objectBoundingBox'
  1476. ? bb.x() + bb.width() * this.attribute('x1').numValue()
  1477. : this.attribute('x1').toPixels('x'));
  1478. var y1 = (this.gradientUnits == 'objectBoundingBox'
  1479. ? bb.y() + bb.height() * this.attribute('y1').numValue()
  1480. : this.attribute('y1').toPixels('y'));
  1481. var x2 = (this.gradientUnits == 'objectBoundingBox'
  1482. ? bb.x() + bb.width() * this.attribute('x2').numValue()
  1483. : this.attribute('x2').toPixels('x'));
  1484. var y2 = (this.gradientUnits == 'objectBoundingBox'
  1485. ? bb.y() + bb.height() * this.attribute('y2').numValue()
  1486. : this.attribute('y2').toPixels('y'));
  1487. if (x1 == x2 && y1 == y2) return null;
  1488. return ctx.createLinearGradient(x1, y1, x2, y2);
  1489. }
  1490. }
  1491. svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
  1492. // radial gradient element
  1493. svg.Element.radialGradient = function(node) {
  1494. this.base = svg.Element.GradientBase;
  1495. this.base(node);
  1496. this.getGradient = function(ctx, element) {
  1497. var bb = element.getBoundingBox();
  1498. if (!this.attribute('cx').hasValue()) this.attribute('cx', true).value = '50%';
  1499. if (!this.attribute('cy').hasValue()) this.attribute('cy', true).value = '50%';
  1500. if (!this.attribute('r').hasValue()) this.attribute('r', true).value = '50%';
  1501. var cx = (this.gradientUnits == 'objectBoundingBox'
  1502. ? bb.x() + bb.width() * this.attribute('cx').numValue()
  1503. : this.attribute('cx').toPixels('x'));
  1504. var cy = (this.gradientUnits == 'objectBoundingBox'
  1505. ? bb.y() + bb.height() * this.attribute('cy').numValue()
  1506. : this.attribute('cy').toPixels('y'));
  1507. var fx = cx;
  1508. var fy = cy;
  1509. if (this.attribute('fx').hasValue()) {
  1510. fx = (this.gradientUnits == 'objectBoundingBox'
  1511. ? bb.x() + bb.width() * this.attribute('fx').numValue()
  1512. : this.attribute('fx').toPixels('x'));
  1513. }
  1514. if (this.attribute('fy').hasValue()) {
  1515. fy = (this.gradientUnits == 'objectBoundingBox'
  1516. ? bb.y() + bb.height() * this.attribute('fy').numValue()
  1517. : this.attribute('fy').toPixels('y'));
  1518. }
  1519. var r = (this.gradientUnits == 'objectBoundingBox'
  1520. ? (bb.width() + bb.height()) / 2.0 * this.attribute('r').numValue()
  1521. : this.attribute('r').toPixels());
  1522. return ctx.createRadialGradient(fx, fy, 0, cx, cy, r);
  1523. }
  1524. }
  1525. svg.Element.radialGradient.prototype = new svg.Element.GradientBase;
  1526. // gradient stop element
  1527. svg.Element.stop = function(node) {
  1528. this.base = svg.Element.ElementBase;
  1529. this.base(node);
  1530. this.offset = this.attribute('offset').numValue();
  1531. if (this.offset < 0) this.offset = 0;
  1532. if (this.offset > 1) this.offset = 1;
  1533. var stopColor = this.style('stop-color');
  1534. if (this.style('stop-opacity').hasValue()) stopColor = stopColor.addOpacity(this.style('stop-opacity').value);
  1535. this.color = stopColor.value;
  1536. }
  1537. svg.Element.stop.prototype = new svg.Element.ElementBase;
  1538. // animation base element
  1539. svg.Element.AnimateBase = function(node) {
  1540. this.base = svg.Element.ElementBase;
  1541. this.base(node);
  1542. svg.Animations.push(this);
  1543. this.duration = 0.0;
  1544. this.begin = this.attribute('begin').toMilliseconds();
  1545. this.maxDuration = this.begin + this.attribute('dur').toMilliseconds();
  1546. this.getProperty = function() {
  1547. var attributeType = this.attribute('attributeType').value;
  1548. var attributeName = this.attribute('attributeName').value;
  1549. if (attributeType == 'CSS') {
  1550. return this.parent.style(attributeName, true);
  1551. }
  1552. return this.parent.attribute(attributeName, true);
  1553. };
  1554. this.initialValue = null;
  1555. this.initialUnits = '';
  1556. this.removed = false;
  1557. this.calcValue = function() {
  1558. // OVERRIDE ME!
  1559. return '';
  1560. }
  1561. this.update = function(delta) {
  1562. // set initial value
  1563. if (this.initialValue == null) {
  1564. this.initialValue = this.getProperty().value;
  1565. this.initialUnits = this.getProperty().getUnits();
  1566. }
  1567. // if we're past the end time
  1568. if (this.duration > this.maxDuration) {
  1569. // loop for indefinitely repeating animations
  1570. if (this.attribute('repeatCount').value == 'indefinite'
  1571. || this.attribute('repeatDur').value == 'indefinite') {
  1572. this.duration = 0.0
  1573. }
  1574. else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
  1575. this.removed = true;
  1576. this.getProperty().value = this.initialValue;
  1577. return true;
  1578. }
  1579. else {
  1580. return false; // no updates made
  1581. }
  1582. }
  1583. this.duration = this.duration + delta;
  1584. // if we're past the begin time
  1585. var updated = false;
  1586. if (this.begin < this.duration) {
  1587. var newValue = this.calcValue(); // tween
  1588. if (this.attribute('type').hasValue()) {
  1589. // for transform, etc.
  1590. var type = this.attribute('type').value;
  1591. newValue = type + '(' + newValue + ')';
  1592. }
  1593. this.getProperty().value = newValue;
  1594. updated = true;
  1595. }
  1596. return updated;
  1597. }
  1598. this.from = this.attribute('from');
  1599. this.to = this.attribute('to');
  1600. this.values = this.attribute('values');
  1601. if (this.values.hasValue()) this.values.value = this.values.value.split(';');
  1602. // fraction of duration we've covered
  1603. this.progress = function() {
  1604. var ret = { progress: (this.duration - this.begin) / (this.maxDuration - this.begin) };
  1605. if (this.values.hasValue()) {
  1606. var p = ret.progress * (this.values.value.length - 1);
  1607. var lb = Math.floor(p), ub = Math.ceil(p);
  1608. ret.from = new svg.Property('from', parseFloat(this.values.value[lb]));
  1609. ret.to = new svg.Property('to', parseFloat(this.values.value[ub]));
  1610. ret.progress = (p - lb) / (ub - lb);
  1611. }
  1612. else {
  1613. ret.from = this.from;
  1614. ret.to = this.to;
  1615. }
  1616. return ret;
  1617. }
  1618. }
  1619. svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
  1620. // animate element
  1621. svg.Element.animate = function(node) {
  1622. this.base = svg.Element.AnimateBase;
  1623. this.base(node);
  1624. this.calcValue = function() {
  1625. var p = this.progress();
  1626. // tween value linearly
  1627. var newValue = p.from.numValue() + (p.to.numValue() - p.from.numValue()) * p.progress;
  1628. return newValue + this.initialUnits;
  1629. };
  1630. }
  1631. svg.Element.animate.prototype = new svg.Element.AnimateBase;
  1632. // animate color element
  1633. svg.Element.animateColor = function(node) {
  1634. this.base = svg.Element.AnimateBase;
  1635. this.base(node);
  1636. this.calcValue = function() {
  1637. var p = this.progress();
  1638. var from = new RGBColor(p.from.value);
  1639. var to = new RGBColor(p.to.value);
  1640. if (from.ok && to.ok) {
  1641. // tween color linearly
  1642. var r = from.r + (to.r - from.r) * p.progress;
  1643. var g = from.g + (to.g - from.g) * p.progress;
  1644. var b = from.b + (to.b - from.b) * p.progress;
  1645. return 'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')';
  1646. }
  1647. return this.attribute('from').value;
  1648. };
  1649. }
  1650. svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
  1651. // animate transform element
  1652. svg.Element.animateTransform = function(node) {
  1653. this.base = svg.Element.AnimateBase;
  1654. this.base(node);
  1655. this.calcValue = function() {
  1656. var p = this.progress();
  1657. // tween value linearly
  1658. var from = svg.ToNumberArray(p.from.value);
  1659. var to = svg.ToNumberArray(p.to.value);
  1660. var newValue = '';
  1661. for (var i=0; i<from.length; i++) {
  1662. newValue += from[i] + (to[i] - from[i]) * p.progress + ' ';
  1663. }
  1664. return newValue;
  1665. };
  1666. }
  1667. svg.Element.animateTransform.prototype = new svg.Element.animate;
  1668. // font element
  1669. svg.Element.font = function(node) {
  1670. this.base = svg.Element.ElementBase;
  1671. this.base(node);
  1672. this.horizAdvX = this.attribute('horiz-adv-x').numValue();
  1673. this.isRTL = false;
  1674. this.isArabic = false;
  1675. this.fontFace = null;
  1676. this.missingGlyph = null;
  1677. this.glyphs = [];
  1678. for (var i=0; i<this.children.length; i++) {
  1679. var child = this.children[i];
  1680. if (child.type == 'font-face') {
  1681. this.fontFace = child;
  1682. if (child.style('font-family').hasValue()) {
  1683. svg.Definitions[child.style('font-family').value] = this;
  1684. }
  1685. }
  1686. else if (child.type == 'missing-glyph') this.missingGlyph = child;
  1687. else if (child.type == 'glyph') {
  1688. if (child.arabicForm != '') {
  1689. this.isRTL = true;
  1690. this.isArabic = true;
  1691. if (typeof(this.glyphs[child.unicode]) == 'undefined') this.glyphs[child.unicode] = [];
  1692. this.glyphs[child.unicode][child.arabicForm] = child;
  1693. }
  1694. else {
  1695. this.glyphs[child.unicode] = child;
  1696. }
  1697. }
  1698. }
  1699. }
  1700. svg.Element.font.prototype = new svg.Element.ElementBase;
  1701. // font-face element
  1702. svg.Element.fontface = function(node) {
  1703. this.base = svg.Element.ElementBase;
  1704. this.base(node);
  1705. this.ascent = this.attribute('ascent').value;
  1706. this.descent = this.attribute('descent').value;
  1707. this.unitsPerEm = this.attribute('units-per-em').numValue();
  1708. }
  1709. svg.Element.fontface.prototype = new svg.Element.ElementBase;
  1710. // missing-glyph element
  1711. svg.Element.missingglyph = function(node) {
  1712. this.base = svg.Element.path;
  1713. this.base(node);
  1714. this.horizAdvX = 0;
  1715. }
  1716. svg.Element.missingglyph.prototype = new svg.Element.path;
  1717. // glyph element
  1718. svg.Element.glyph = function(node) {
  1719. this.base = svg.Element.path;
  1720. this.base(node);
  1721. this.horizAdvX = this.attribute('horiz-adv-x').numValue();
  1722. this.unicode = this.attribute('unicode').value;
  1723. this.arabicForm = this.attribute('arabic-form').value;
  1724. }
  1725. svg.Element.glyph.prototype = new svg.Element.path;
  1726. // text element
  1727. svg.Element.text = function(node) {
  1728. this.captureTextNodes = true;
  1729. this.base = svg.Element.RenderedElementBase;
  1730. this.base(node);
  1731. this.baseSetContext = this.setContext;
  1732. this.setContext = function(ctx) {
  1733. this.baseSetContext(ctx);
  1734. if (this.style('dominant-baseline').hasValue()) ctx.textBaseline = this.style('dominant-baseline').value;
  1735. if (this.style('alignment-baseline').hasValue()) ctx.textBaseline = this.style('alignment-baseline').value;
  1736. }
  1737. this.getBoundingBox = function () {
  1738. // TODO: implement
  1739. return new svg.BoundingBox(this.attribute('x').toPixels('x'), this.attribute('y').toPixels('y'), 0, 0);
  1740. }
  1741. this.renderChildren = function(ctx) {
  1742. this.x = this.attribute('x').toPixels('x');
  1743. this.y = this.attribute('y').toPixels('y');
  1744. this.x += this.getAnchorDelta(ctx, this, 0);
  1745. for (var i=0; i<this.children.length; i++) {
  1746. this.renderChild(ctx, this, i);
  1747. }
  1748. }
  1749. this.getAnchorDelta = function (ctx, parent, startI) {
  1750. var textAnchor = this.style('text-anchor').valueOrDefault('start');
  1751. if (textAnchor != 'start') {
  1752. var width = 0;
  1753. for (var i=startI; i<parent.children.length; i++) {
  1754. var child = parent.children[i];
  1755. if (i > startI && child.attribute('x').hasValue()) break; // new group
  1756. width += child.measureTextRecursive(ctx);
  1757. }
  1758. return -1 * (textAnchor == 'end' ? width : width / 2.0);
  1759. }
  1760. return 0;
  1761. }
  1762. this.renderChild = function(ctx, parent, i) {
  1763. var child = parent.children[i];
  1764. if (child.attribute('x').hasValue()) {
  1765. child.x = child.attribute('x').toPixels('x') + this.getAnchorDelta(ctx, parent, i);
  1766. }
  1767. else {
  1768. if (this.attribute('dx').hasValue()) this.x += this.attribute('dx').toPixels('x');
  1769. if (child.attribute('dx').hasValue()) this.x += child.attribute('dx').toPixels('x');
  1770. child.x = this.x;
  1771. }
  1772. this.x = child.x + child.measureText(ctx);
  1773. if (child.attribute('y').hasValue()) {
  1774. child.y = child.attribute('y').toPixels('y');
  1775. }
  1776. else {
  1777. if (this.attribute('dy').hasValue()) this.y += this.attribute('dy').toPixels('y');
  1778. if (child.attribute('dy').hasValue()) this.y += child.attribute('dy').toPixels('y');
  1779. child.y = this.y;
  1780. }
  1781. this.y = child.y;
  1782. child.render(ctx);
  1783. for (var i=0; i<child.children.length; i++) {
  1784. this.renderChild(ctx, child, i);
  1785. }
  1786. }
  1787. }
  1788. svg.Element.text.prototype = new svg.Element.RenderedElementBase;
  1789. // text base
  1790. svg.Element.TextElementBase = function(node) {
  1791. this.base = svg.Element.RenderedElementBase;
  1792. this.base(node);
  1793. this.getGlyph = function(font, text, i) {
  1794. var c = text[i];
  1795. var glyph = null;
  1796. if (font.isArabic) {
  1797. var arabicForm = 'isolated';
  1798. if ((i==0 || text[i-1]==' ') && i<text.length-2 && text[i+1]!=' ') arabicForm = 'terminal';
  1799. if (i>0 && text[i-1]!=' ' && i<text.length-2 && text[i+1]!=' ') arabicForm = 'medial';
  1800. if (i>0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial';
  1801. if (typeof(font.glyphs[c]) != 'undefined') {
  1802. glyph = font.glyphs[c][arabicForm];
  1803. if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c];
  1804. }
  1805. }
  1806. else {
  1807. glyph = font.glyphs[c];
  1808. }
  1809. if (glyph == null) glyph = font.missingGlyph;
  1810. return glyph;
  1811. }
  1812. this.renderChildren = function(ctx) {
  1813. var customFont = this.parent.style('font-family').getDefinition();
  1814. if (customFont != null) {
  1815. var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
  1816. var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
  1817. var text = this.getText();
  1818. if (customFont.isRTL) text = text.split("").reverse().join("");
  1819. var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
  1820. for (var i=0; i<text.length; i++) {
  1821. var glyph = this.getGlyph(customFont, text, i);
  1822. var scale = fontSize / customFont.fontFace.unitsPerEm;
  1823. ctx.translate(this.x, this.y);
  1824. ctx.scale(scale, -scale);
  1825. var lw = ctx.lineWidth;
  1826. ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize;
  1827. if (fontStyle == 'italic') ctx.transform(1, 0, .4, 1, 0, 0);
  1828. glyph.render(ctx);
  1829. if (fontStyle == 'italic') ctx.transform(1, 0, -.4, 1, 0, 0);
  1830. ctx.lineWidth = lw;
  1831. ctx.scale(1/scale, -1/scale);
  1832. ctx.translate(-this.x, -this.y);
  1833. this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm;
  1834. if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
  1835. this.x += dx[i];
  1836. }
  1837. }
  1838. return;
  1839. }
  1840. if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y);
  1841. if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y);
  1842. }
  1843. this.getText = function() {
  1844. // OVERRIDE ME
  1845. }
  1846. this.measureTextRecursive = function(ctx) {
  1847. var width = this.measureText(ctx);
  1848. for (var i=0; i<this.children.length; i++) {
  1849. width += this.children[i].measureTextRecursive(ctx);
  1850. }
  1851. return width;
  1852. }
  1853. this.measureText = function(ctx) {
  1854. var customFont = this.parent.style('font-family').getDefinition();
  1855. if (customFont != null) {
  1856. var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
  1857. var measure = 0;
  1858. var text = this.getText();
  1859. if (customFont.isRTL) text = text.split("").reverse().join("");
  1860. var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
  1861. for (var i=0; i<text.length; i++) {
  1862. var glyph = this.getGlyph(customFont, text, i);
  1863. measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm;
  1864. if (typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
  1865. measure += dx[i];
  1866. }
  1867. }
  1868. return measure;
  1869. }
  1870. var textToMeasure = svg.compressSpaces(this.getText());
  1871. if (!ctx.measureText) return textToMeasure.length * 10;
  1872. ctx.save();
  1873. this.setContext(ctx);
  1874. var width = ctx.measureText(textToMeasure).width;
  1875. ctx.restore();
  1876. return width;
  1877. }
  1878. }
  1879. svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase;
  1880. // tspan
  1881. svg.Element.tspan = function(node) {
  1882. this.captureTextNodes = true;
  1883. this.base = svg.Element.TextElementBase;
  1884. this.base(node);
  1885. this.text = node.nodeValue || node.text || '';
  1886. this.getText = function() {
  1887. return this.text;
  1888. }
  1889. }
  1890. svg.Element.tspan.prototype = new svg.Element.TextElementBase;
  1891. // tref
  1892. svg.Element.tref = function(node) {
  1893. this.base = svg.Element.TextElementBase;
  1894. this.base(node);
  1895. this.getText = function() {
  1896. var element = this.getHrefAttribute().getDefinition();
  1897. if (element != null) return element.children[0].getText();
  1898. }
  1899. }
  1900. svg.Element.tref.prototype = new svg.Element.TextElementBase;
  1901. // a element
  1902. svg.Element.a = function(node) {
  1903. this.base = svg.Element.TextElementBase;
  1904. this.base(node);
  1905. this.hasText = true;
  1906. for (var i=0; i<node.childNodes.length; i++) {
  1907. if (node.childNodes[i].nodeType != 3) this.hasText = false;
  1908. }
  1909. // this might contain text
  1910. this.text = this.hasText ? node.childNodes[0].nodeValue : '';
  1911. this.getText = function() {
  1912. return this.text;
  1913. }
  1914. this.baseRenderChildren = this.renderChildren;
  1915. this.renderChildren = function(ctx) {
  1916. if (this.hasText) {
  1917. // render as text element
  1918. this.baseRenderChildren(ctx);
  1919. var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
  1920. svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.toPixels('y'), this.x + this.measureText(ctx), this.y));
  1921. }
  1922. else {
  1923. // render as temporary group
  1924. var g = new svg.Element.g();
  1925. g.children = this.children;
  1926. g.parent = this;
  1927. g.render(ctx);
  1928. }
  1929. }
  1930. this.onclick = function() {
  1931. window.open(this.getHrefAttribute().value);
  1932. }
  1933. this.onmousemove = function() {
  1934. svg.ctx.canvas.style.cursor = 'pointer';
  1935. }
  1936. }
  1937. svg.Element.a.prototype = new svg.Element.TextElementBase;
  1938. // image element
  1939. svg.Element.image = function(node) {
  1940. this.base = svg.Element.RenderedElementBase;
  1941. this.base(node);
  1942. var href = this.getHrefAttribute().value;
  1943. var isSvg = href.match(/\.svg$/)
  1944. svg.Images.push(this);
  1945. this.loaded = false;
  1946. if (!isSvg) {
  1947. this.img = document.createElement('img');
  1948. var self = this;
  1949. this.img.onload = function() { self.loaded = true; }
  1950. this.img.onerror = function() { if (typeof(console) != 'undefined') { console.log('ERROR: image "' + href + '" not found'); self.loaded = true; } }
  1951. this.img.src = href;
  1952. }
  1953. else {
  1954. this.img = svg.ajax(href);
  1955. this.loaded = true;
  1956. }
  1957. this.renderChildren = function(ctx) {
  1958. var x = this.attribute('x').toPixels('x');
  1959. var y = this.attribute('y').toPixels('y');
  1960. var width = this.attribute('width').toPixels('x');
  1961. var height = this.attribute('height').toPixels('y');
  1962. if (width == 0 || height == 0) return;
  1963. ctx.save();
  1964. if (isSvg) {
  1965. ctx.drawSvg(this.img, x, y, width, height);
  1966. }
  1967. else {
  1968. ctx.translate(x, y);
  1969. svg.AspectRatio(ctx,
  1970. this.attribute('preserveAspectRatio').value,
  1971. width,
  1972. this.img.width,
  1973. height,
  1974. this.img.height,
  1975. 0,
  1976. 0);
  1977. ctx.drawImage(this.img, 0, 0);
  1978. }
  1979. ctx.restore();
  1980. }
  1981. this.getBoundingBox = function() {
  1982. var x = this.attribute('x').toPixels('x');
  1983. var y = this.attribute('y').toPixels('y');
  1984. var width = this.attribute('width').toPixels('x');
  1985. var height = this.attribute('height').toPixels('y');
  1986. return new svg.BoundingBox(x, y, x + width, y + height);
  1987. }
  1988. }
  1989. svg.Element.image.prototype = new svg.Element.RenderedElementBase;
  1990. // group element
  1991. svg.Element.g = function(node) {
  1992. this.base = svg.Element.RenderedElementBase;
  1993. this.base(node);
  1994. this.getBoundingBox = function() {
  1995. var bb = new svg.BoundingBox();
  1996. for (var i=0; i<this.children.length; i++) {
  1997. bb.addBoundingBox(this.children[i].getBoundingBox());
  1998. }
  1999. return bb;
  2000. };
  2001. }
  2002. svg.Element.g.prototype = new svg.Element.RenderedElementBase;
  2003. // symbol element
  2004. svg.Element.symbol = function(node) {
  2005. this.base = svg.Element.RenderedElementBase;
  2006. this.base(node);
  2007. this.baseSetContext = this.setContext;
  2008. this.setContext = function(ctx) {
  2009. this.baseSetContext(ctx);
  2010. // viewbox
  2011. if (this.attribute('viewBox').hasValue()) {
  2012. var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
  2013. var minX = viewBox[0];
  2014. var minY = viewBox[1];
  2015. width = viewBox[2];
  2016. height = viewBox[3];
  2017. svg.AspectRatio(ctx,
  2018. this.attribute('preserveAspectRatio').value,
  2019. this.attribute('width').toPixels('x'),
  2020. width,
  2021. this.attribute('height').toPixels('y'),
  2022. height,
  2023. minX,
  2024. minY);
  2025. svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
  2026. }
  2027. }
  2028. }
  2029. svg.Element.symbol.prototype = new svg.Element.RenderedElementBase;
  2030. // style element
  2031. svg.Element.style = function(node) {
  2032. this.base = svg.Element.ElementBase;
  2033. this.base(node);
  2034. // text, or spaces then CDATA
  2035. var css = ''
  2036. for (var i=0; i<node.childNodes.length; i++) {
  2037. css += node.childNodes[i].nodeValue;
  2038. }
  2039. css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments
  2040. css = svg.compressSpaces(css); // replace whitespace
  2041. var cssDefs = css.split('}');
  2042. for (var i=0; i<cssDefs.length; i++) {
  2043. if (svg.trim(cssDefs[i]) != '') {
  2044. var cssDef = cssDefs[i].split('{');
  2045. var cssClasses = cssDef[0].split(',');
  2046. var cssProps = cssDef[1].split(';');
  2047. for (var j=0; j<cssClasses.length; j++) {
  2048. var cssClass = svg.trim(cssClasses[j]);
  2049. if (cssClass != '') {
  2050. var props = {};
  2051. for (var k=0; k<cssProps.length; k++) {
  2052. var prop = cssProps[k].indexOf(':');
  2053. var name = cssProps[k].substr(0, prop);
  2054. var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop);
  2055. if (name != null && value != null) {
  2056. props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value));
  2057. }
  2058. }
  2059. svg.Styles[cssClass] = props;
  2060. if (cssClass == '@font-face') {
  2061. var fontFamily = props['font-family'].value.replace(/"/g,'');
  2062. var srcs = props['src'].value.split(',');
  2063. for (var s=0; s<srcs.length; s++) {
  2064. if (srcs[s].indexOf('format("svg")') > 0) {
  2065. var urlStart = srcs[s].indexOf('url');
  2066. var urlEnd = srcs[s].indexOf(')', urlStart);
  2067. var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
  2068. var doc = svg.parseXml(svg.ajax(url));
  2069. var fonts = doc.getElementsByTagName('font');
  2070. for (var f=0; f<fonts.length; f++) {
  2071. var font = svg.CreateElement(fonts[f]);
  2072. svg.Definitions[fontFamily] = font;
  2073. }
  2074. }
  2075. }
  2076. }
  2077. }
  2078. }
  2079. }
  2080. }
  2081. }
  2082. svg.Element.style.prototype = new svg.Element.ElementBase;
  2083. // use element
  2084. svg.Element.use = function(node) {
  2085. this.base = svg.Element.RenderedElementBase;
  2086. this.base(node);
  2087. this.baseSetContext = this.setContext;
  2088. this.setContext = function(ctx) {
  2089. this.baseSetContext(ctx);
  2090. if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').toPixels('x'), 0);
  2091. if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').toPixels('y'));
  2092. }
  2093. this.getDefinition = function() {
  2094. var element = this.getHrefAttribute().getDefinition();
  2095. if (this.attribute('width').hasValue()) element.attribute('width', true).value = this.attribute('width').value;
  2096. if (this.attribute('height').hasValue()) element.attribute('height', true).value = this.attribute('height').value;
  2097. return element;
  2098. }
  2099. this.path = function(ctx) {
  2100. var element = this.getDefinition();
  2101. if (element != null) element.path(ctx);
  2102. }
  2103. this.getBoundingBox = function() {
  2104. var element = this.getDefinition();
  2105. if (element != null) return element.getBoundingBox();
  2106. }
  2107. this.renderChildren = function(ctx) {
  2108. var element = this.getDefinition();
  2109. if (element != null) {
  2110. // temporarily detach from parent and render
  2111. var oldParent = element.parent;
  2112. element.parent = null;
  2113. element.render(ctx);
  2114. element.parent = oldParent;
  2115. }
  2116. }
  2117. }
  2118. svg.Element.use.prototype = new svg.Element.RenderedElementBase;
  2119. // mask element
  2120. svg.Element.mask = function(node) {
  2121. this.base = svg.Element.ElementBase;
  2122. this.base(node);
  2123. this.apply = function(ctx, element) {
  2124. // render as temp svg
  2125. var x = this.attribute('x').toPixels('x');
  2126. var y = this.attribute('y').toPixels('y');
  2127. var width = this.attribute('width').toPixels('x');
  2128. var height = this.attribute('height').toPixels('y');
  2129. if (width == 0 && height == 0) {
  2130. var bb = new svg.BoundingBox();
  2131. for (var i=0; i<this.children.length; i++) {
  2132. bb.addBoundingBox(this.children[i].getBoundingBox());
  2133. }
  2134. var x = Math.floor(bb.x1);
  2135. var y = Math.floor(bb.y1);
  2136. var width = Math.floor(bb.width());
  2137. var height = Math.floor(bb.height());
  2138. }
  2139. // temporarily remove mask to avoid recursion
  2140. var mask = element.attribute('mask').value;
  2141. element.attribute('mask').value = '';
  2142. var cMask = document.createElement('canvas');
  2143. cMask.width = x + width;
  2144. cMask.height = y + height;
  2145. var maskCtx = cMask.getContext('2d');
  2146. this.renderChildren(maskCtx);
  2147. var c = document.createElement('canvas');
  2148. c.width = x + width;
  2149. c.height = y + height;
  2150. var tempCtx = c.getContext('2d');
  2151. element.render(tempCtx);
  2152. tempCtx.globalCompositeOperation = 'destination-in';
  2153. tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat');
  2154. tempCtx.fillRect(0, 0, x + width, y + height);
  2155. ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat');
  2156. ctx.fillRect(0, 0, x + width, y + height);
  2157. // reassign mask
  2158. element.attribute('mask').value = mask;
  2159. }
  2160. this.render = function(ctx) {
  2161. // NO RENDER
  2162. }
  2163. }
  2164. svg.Element.mask.prototype = new svg.Element.ElementBase;
  2165. // clip element
  2166. svg.Element.clipPath = function(node) {
  2167. this.base = svg.Element.ElementBase;
  2168. this.base(node);
  2169. this.apply = function(ctx) {
  2170. for (var i=0; i<this.children.length; i++) {
  2171. var child = this.children[i];
  2172. if (typeof(child.path) != 'undefined') {
  2173. var transform = null;
  2174. if (child.attribute('transform').hasValue()) {
  2175. transform = new svg.Transform(child.attribute('transform').value);
  2176. transform.apply(ctx);
  2177. }
  2178. child.path(ctx);
  2179. ctx.clip();
  2180. if (transform) { transform.unapply(ctx); }
  2181. }
  2182. }
  2183. }
  2184. this.render = function(ctx) {
  2185. // NO RENDER
  2186. }
  2187. }
  2188. svg.Element.clipPath.prototype = new svg.Element.ElementBase;
  2189. // filters
  2190. svg.Element.filter = function(node) {
  2191. this.base = svg.Element.ElementBase;
  2192. this.base(node);
  2193. this.apply = function(ctx, element) {
  2194. // render as temp svg
  2195. var bb = element.getBoundingBox();
  2196. var x = Math.floor(bb.x1);
  2197. var y = Math.floor(bb.y1);
  2198. var width = Math.floor(bb.width());
  2199. var height = Math.floor(bb.height());
  2200. // temporarily remove filter to avoid recursion
  2201. var filter = element.style('filter').value;
  2202. element.style('filter').value = '';
  2203. var px = 0, py = 0;
  2204. for (var i=0; i<this.children.length; i++) {
  2205. var efd = this.children[i].extraFilterDistance || 0;
  2206. px = Math.max(px, efd);
  2207. py = Math.max(py, efd);
  2208. }
  2209. var c = document.createElement('canvas');
  2210. c.width = width + 2*px;
  2211. c.height = height + 2*py;
  2212. var tempCtx = c.getContext('2d');
  2213. tempCtx.translate(-x + px, -y + py);
  2214. element.render(tempCtx);
  2215. // apply filters
  2216. for (var i=0; i<this.children.length; i++) {
  2217. this.children[i].apply(tempCtx, 0, 0, width + 2*px, height + 2*py);
  2218. }
  2219. // render on me
  2220. ctx.drawImage(c, 0, 0, width + 2*px, height + 2*py, x - px, y - py, width + 2*px, height + 2*py);
  2221. // reassign filter
  2222. element.style('filter', true).value = filter;
  2223. }
  2224. this.render = function(ctx) {
  2225. // NO RENDER
  2226. }
  2227. }
  2228. svg.Element.filter.prototype = new svg.Element.ElementBase;
  2229. svg.Element.feMorphology = function(node) {
  2230. this.base = svg.Element.ElementBase;
  2231. this.base(node);
  2232. this.apply = function(ctx, x, y, width, height) {
  2233. // TODO: implement
  2234. }
  2235. }
  2236. svg.Element.feMorphology.prototype = new svg.Element.ElementBase;
  2237. svg.Element.feColorMatrix = function(node) {
  2238. this.base = svg.Element.ElementBase;
  2239. this.base(node);
  2240. function imGet(img, x, y, width, height, rgba) {
  2241. return img[y*width*4 + x*4 + rgba];
  2242. }
  2243. function imSet(img, x, y, width, height, rgba, val) {
  2244. img[y*width*4 + x*4 + rgba] = val;
  2245. }
  2246. this.apply = function(ctx, x, y, width, height) {
  2247. // only supporting grayscale for now per Issue 195, need to extend to all matrix
  2248. // assuming x==0 && y==0 for now
  2249. var srcData = ctx.getImageData(0, 0, width, height);
  2250. for (var y = 0; y < height; y++) {
  2251. for (var x = 0; x < width; x++) {
  2252. var r = imGet(srcData.data, x, y, width, height, 0);
  2253. var g = imGet(srcData.data, x, y, width, height, 1);
  2254. var b = imGet(srcData.data, x, y, width, height, 2);
  2255. var gray = (r + g + b) / 3;
  2256. imSet(srcData.data, x, y, width, height, 0, gray);
  2257. imSet(srcData.data, x, y, width, height, 1, gray);
  2258. imSet(srcData.data, x, y, width, height, 2, gray);
  2259. }
  2260. }
  2261. ctx.clearRect(0, 0, width, height);
  2262. ctx.putImageData(srcData, 0, 0);
  2263. }
  2264. }
  2265. svg.Element.feColorMatrix.prototype = new svg.Element.ElementBase;
  2266. svg.Element.feGaussianBlur = function(node) {
  2267. this.base = svg.Element.ElementBase;
  2268. this.base(node);
  2269. this.blurRadius = Math.floor(this.attribute('stdDeviation').numValue());
  2270. this.extraFilterDistance = this.blurRadius;
  2271. this.apply = function(ctx, x, y, width, height) {
  2272. if (typeof(stackBlurCanvasRGBA) == 'undefined') {
  2273. if (typeof(console) != 'undefined') { console.log('ERROR: StackBlur.js must be included for blur to work'); }
  2274. return;
  2275. }
  2276. // StackBlur requires canvas be on document
  2277. ctx.canvas.id = svg.UniqueId();
  2278. ctx.canvas.style.display = 'none';
  2279. document.body.appendChild(ctx.canvas);
  2280. stackBlurCanvasRGBA(ctx.canvas.id, x, y, width, height, this.blurRadius);
  2281. document.body.removeChild(ctx.canvas);
  2282. }
  2283. }
  2284. svg.Element.feGaussianBlur.prototype = new svg.Element.ElementBase;
  2285. // title element, do nothing
  2286. svg.Element.title = function(node) {
  2287. }
  2288. svg.Element.title.prototype = new svg.Element.ElementBase;
  2289. // desc element, do nothing
  2290. svg.Element.desc = function(node) {
  2291. }
  2292. svg.Element.desc.prototype = new svg.Element.ElementBase;
  2293. svg.Element.MISSING = function(node) {
  2294. if (typeof(console) != 'undefined') { console.log('ERROR: Element \'' + node.nodeName + '\' not yet implemented.'); }
  2295. }
  2296. svg.Element.MISSING.prototype = new svg.Element.ElementBase;
  2297. // element factory
  2298. svg.CreateElement = function(node) {
  2299. var className = node.nodeName.replace(/^[^:]+:/,''); // remove namespace
  2300. className = className.replace(/\-/g,''); // remove dashes
  2301. var e = null;
  2302. if (typeof(svg.Element[className]) != 'undefined') {
  2303. e = new svg.Element[className](node);
  2304. }
  2305. else {
  2306. e = new svg.Element.MISSING(node);
  2307. }
  2308. e.type = node.nodeName;
  2309. return e;
  2310. }
  2311. // load from url
  2312. svg.load = function(ctx, url) {
  2313. svg.loadXml(ctx, svg.ajax(url));
  2314. }
  2315. // load from xml
  2316. svg.loadXml = function(ctx, xml) {
  2317. svg.loadXmlDoc(ctx, svg.parseXml(xml));
  2318. }
  2319. svg.loadXmlDoc = function(ctx, dom) {
  2320. svg.init(ctx);
  2321. var mapXY = function(p) {
  2322. var e = ctx.canvas;
  2323. while (e) {
  2324. p.x -= e.offsetLeft;
  2325. p.y -= e.offsetTop;
  2326. e = e.offsetParent;
  2327. }
  2328. if (window.scrollX) p.x += window.scrollX;
  2329. if (window.scrollY) p.y += window.scrollY;
  2330. return p;
  2331. }
  2332. // bind mouse
  2333. if (svg.opts['ignoreMouse'] != true) {
  2334. ctx.canvas.onclick = function(e) {
  2335. var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
  2336. svg.Mouse.onclick(p.x, p.y);
  2337. };
  2338. ctx.canvas.onmousemove = function(e) {
  2339. var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
  2340. svg.Mouse.onmousemove(p.x, p.y);
  2341. };
  2342. }
  2343. var e = svg.CreateElement(dom.documentElement);
  2344. e.root = true;
  2345. // render loop
  2346. var isFirstRender = true;
  2347. var draw = function() {
  2348. svg.ViewPort.Clear();
  2349. if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight);
  2350. if (svg.opts['ignoreDimensions'] != true) {
  2351. // set canvas size
  2352. if (e.style('width').hasValue()) {
  2353. ctx.canvas.width = e.style('width').toPixels('x');
  2354. ctx.canvas.style.width = ctx.canvas.width + 'px';
  2355. }
  2356. if (e.style('height').hasValue()) {
  2357. ctx.canvas.height = e.style('height').toPixels('y');
  2358. ctx.canvas.style.height = ctx.canvas.height + 'px';
  2359. }
  2360. }
  2361. var cWidth = ctx.canvas.clientWidth || ctx.canvas.width;
  2362. var cHeight = ctx.canvas.clientHeight || ctx.canvas.height;
  2363. if (svg.opts['ignoreDimensions'] == true && e.style('width').hasValue() && e.style('height').hasValue()) {
  2364. cWidth = e.style('width').toPixels('x');
  2365. cHeight = e.style('height').toPixels('y');
  2366. }
  2367. svg.ViewPort.SetCurrent(cWidth, cHeight);
  2368. if (svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX'];
  2369. if (svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY'];
  2370. if (svg.opts['scaleWidth'] != null && svg.opts['scaleHeight'] != null) {
  2371. var xRatio = 1, yRatio = 1, viewBox = svg.ToNumberArray(e.attribute('viewBox').value);
  2372. if (e.attribute('width').hasValue()) xRatio = e.attribute('width').toPixels('x') / svg.opts['scaleWidth'];
  2373. else if (!isNaN(viewBox[2])) xRatio = viewBox[2] / svg.opts['scaleWidth'];
  2374. if (e.attribute('height').hasValue()) yRatio = e.attribute('height').toPixels('y') / svg.opts['scaleHeight'];
  2375. else if (!isNaN(viewBox[3])) yRatio = viewBox[3] / svg.opts['scaleHeight'];
  2376. e.attribute('width', true).value = svg.opts['scaleWidth'];
  2377. e.attribute('height', true).value = svg.opts['scaleHeight'];
  2378. e.attribute('viewBox', true).value = '0 0 ' + (cWidth * xRatio) + ' ' + (cHeight * yRatio);
  2379. e.attribute('preserveAspectRatio', true).value = 'none';
  2380. }
  2381. // clear and render
  2382. if (svg.opts['ignoreClear'] != true) {
  2383. ctx.clearRect(0, 0, cWidth, cHeight);
  2384. }
  2385. e.render(ctx);
  2386. if (isFirstRender) {
  2387. isFirstRender = false;
  2388. if (typeof(svg.opts['renderCallback']) == 'function') svg.opts['renderCallback'](dom);
  2389. }
  2390. }
  2391. var waitingForImages = true;
  2392. if (svg.ImagesLoaded()) {
  2393. waitingForImages = false;
  2394. draw();
  2395. }
  2396. svg.intervalID = setInterval(function() {
  2397. var needUpdate = false;
  2398. if (waitingForImages && svg.ImagesLoaded()) {
  2399. waitingForImages = false;
  2400. needUpdate = true;
  2401. }
  2402. // need update from mouse events?
  2403. if (svg.opts['ignoreMouse'] != true) {
  2404. needUpdate = needUpdate | svg.Mouse.hasEvents();
  2405. }
  2406. // need update from animations?
  2407. if (svg.opts['ignoreAnimation'] != true) {
  2408. for (var i=0; i<svg.Animations.length; i++) {
  2409. needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE);
  2410. }
  2411. }
  2412. // need update from redraw?
  2413. if (typeof(svg.opts['forceRedraw']) == 'function') {
  2414. if (svg.opts['forceRedraw']() == true) needUpdate = true;
  2415. }
  2416. // render if needed
  2417. if (needUpdate) {
  2418. draw();
  2419. svg.Mouse.runEvents(); // run and clear our events
  2420. }
  2421. }, 1000 / svg.FRAMERATE);
  2422. }
  2423. svg.stop = function() {
  2424. if (svg.intervalID) {
  2425. clearInterval(svg.intervalID);
  2426. }
  2427. }
  2428. svg.Mouse = new (function() {
  2429. this.events = [];
  2430. this.hasEvents = function() { return this.events.length != 0; }
  2431. this.onclick = function(x, y) {
  2432. this.events.push({ type: 'onclick', x: x, y: y,
  2433. run: function(e) { if (e.onclick) e.onclick(); }
  2434. });
  2435. }
  2436. this.onmousemove = function(x, y) {
  2437. this.events.push({ type: 'onmousemove', x: x, y: y,
  2438. run: function(e) { if (e.onmousemove) e.onmousemove(); }
  2439. });
  2440. }
  2441. this.eventElements = [];
  2442. this.checkPath = function(element, ctx) {
  2443. for (var i=0; i<this.events.length; i++) {
  2444. var e = this.events[i];
  2445. if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element;
  2446. }
  2447. }
  2448. this.checkBoundingBox = function(element, bb) {
  2449. for (var i=0; i<this.events.length; i++) {
  2450. var e = this.events[i];
  2451. if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element;
  2452. }
  2453. }
  2454. this.runEvents = function() {
  2455. svg.ctx.canvas.style.cursor = '';
  2456. for (var i=0; i<this.events.length; i++) {
  2457. var e = this.events[i];
  2458. var element = this.eventElements[i];
  2459. while (element) {
  2460. e.run(element);
  2461. element = element.parent;
  2462. }
  2463. }
  2464. // done running, clear
  2465. this.events = [];
  2466. this.eventElements = [];
  2467. }
  2468. });
  2469. return svg;
  2470. }
  2471. })();
  2472. if (typeof(CanvasRenderingContext2D) != 'undefined') {
  2473. CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh) {
  2474. canvg(this.canvas, s, {
  2475. ignoreMouse: true,
  2476. ignoreAnimation: true,
  2477. ignoreDimensions: true,
  2478. ignoreClear: true,
  2479. offsetX: dx,
  2480. offsetY: dy,
  2481. scaleWidth: dw,
  2482. scaleHeight: dh
  2483. });
  2484. }
  2485. }