corrade-nucleus-nucleons – Blame information for rev 24

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