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