highcharts-rails 5.0.14 → 6.0.0

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