2 * canvg.js - Javascript SVG parser and renderer on Canvas
4 * Gabe Lerner (gabelerner@gmail.com)
5 * http://code.google.com/p/canvg/
7 * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
11 window
.console
.log = function(str
) {};
12 window
.console
.dir = function(str
) {};
17 Array
.prototype.indexOf = function(obj
){
18 for(var i
=0; i
<this.length
; i
++){
29 // empty parameters: replace all 'svg' elements on page with 'canvas' elements
30 // target: canvas element or the id of a canvas element
31 // s: svg string or url to svg file
32 // opts: optional hash of options
33 // ignoreMouse: true => ignore mouse events
34 // ignoreAnimation: true => ignore animations
35 // ignoreDimensions: true => does not try to resize canvas
36 // ignoreClear: true => does not clear canvas
37 // offsetX: int => draws at a x offset
38 // offsetY: int => draws at a y offset
39 // scaleWidth: int => scales horizontally to width
40 // scaleHeight: int => scales vertically to height
41 // renderCallback: function => will call the function after the first render is completed
42 // forceRedraw: function => will call the function on every frame, if it returns true, will redraw
43 this.canvg = function (target
, s
, opts
) {
45 if (target
== null && s
== null && opts
== null) {
46 var svgTags
= document
.getElementsByTagName('svg');
47 for (var i
=0; i
<svgTags
.length
; i
++) {
48 var svgTag
= svgTags
[i
];
49 var c
= document
.createElement('canvas');
50 c
.width
= svgTag
.clientWidth
;
51 c
.height
= svgTag
.clientHeight
;
52 svgTag
.parentNode
.insertBefore(c
, svgTag
);
53 svgTag
.parentNode
.removeChild(svgTag
);
54 var div
= document
.createElement('div');
55 div
.appendChild(svgTag
);
56 canvg(c
, div
.innerHTML
);
61 if (typeof target
== 'string') {
62 target
= document
.getElementById(target
);
65 // reuse class per canvas
67 if (target
.svg
== null) {
77 var ctx
= target
.getContext('2d');
78 if (s
.substr(0,1) == '<') {
79 // load from xml string
94 svg
.init = function(ctx
) {
100 svg
.ViewPort
= new (function () {
102 this.SetCurrent = function(width
, height
) { this.viewPorts
.push({ width
: width
, height
: height
}); }
103 this.RemoveCurrent = function() { this.viewPorts
.pop(); }
104 this.Current = function() { return this.viewPorts
[this.viewPorts
.length
- 1]; }
105 this.width = function() { return this.Current().width
; }
106 this.height = function() { return this.Current().height
; }
107 this.ComputeSize = function(d
) {
108 if (d
!= null && typeof(d
) == 'number') return d
;
109 if (d
== 'x') return this.width();
110 if (d
== 'y') return this.height();
111 return Math
.sqrt(Math
.pow(this.width(), 2) + Math
.pow(this.height(), 2)) / Math
.sqrt(2);
118 svg
.ImagesLoaded = function() {
119 for (var i
=0; i
<svg
.Images
.length
; i
++) {
120 if (!svg
.Images
[i
].loaded
) return false;
126 svg
.trim = function(s
) { return s
.replace(/^\s+|\s+$/g, ''); }
129 svg
.compressSpaces = function(s
) { return s
.replace(/[\s\r\t\n]+/gm,' '); }
132 svg
.ajax = function(url
) {
134 if(window
.XMLHttpRequest
){AJAX
=new XMLHttpRequest();}
135 else{AJAX
=new ActiveXObject('Microsoft.XMLHTTP');}
137 AJAX
.open('GET',url
,false);
139 return AJAX
.responseText
;
145 svg
.parseXml = function(xml
) {
146 if (window
.DOMParser
)
148 var parser
= new DOMParser();
149 return parser
.parseFromString(xml
, 'text/xml');
153 xml
= xml
.replace(/<!DOCTYPE svg[^>]*>/, '');
154 var xmlDoc
= new ActiveXObject('Microsoft.XMLDOM');
155 xmlDoc
.async
= 'false';
161 svg
.Property = function(name
, value
) {
165 this.hasValue = function() {
166 return (this.value
!= null && this.value
!= '');
169 // return the numerical value of the property
170 this.numValue = function() {
171 if (!this.hasValue()) return 0;
173 var n
= parseFloat(this.value
);
174 if ((this.value
+ '').match(/%$/)) {
180 this.valueOrDefault = function(def
) {
181 if (this.hasValue()) return this.value
;
185 this.numValueOrDefault = function(def
) {
186 if (this.hasValue()) return this.numValue();
195 // augment the current color value with the opacity
196 addOpacity: function(opacity
) {
197 var newValue
= that
.value
;
198 if (opacity
!= null && opacity
!= '') {
199 var color
= new RGBColor(that
.value
);
201 newValue
= 'rgba(' + color
.r
+ ', ' + color
.g
+ ', ' + color
.b
+ ', ' + opacity
+ ')';
204 return new svg
.Property(that
.name
, newValue
);
208 // definition extensions
210 // get the definition from the definitions table
211 getDefinition: function() {
212 var name
= that
.value
.replace(/^(url\()?#([^\)]+)\)?$/, '$2');
213 return svg
.Definitions
[name
];
217 return that
.value
.indexOf('url(') == 0
220 getFillStyle: function(e
) {
221 var def
= this.getDefinition();
224 if (def
!= null && def
.createGradient
) {
225 return def
.createGradient(svg
.ctx
, e
);
229 if (def
!= null && def
.createPattern
) {
230 return def
.createPattern(svg
.ctx
, e
);
239 DPI: function(viewPort
) {
240 return 96.0; // TODO: compute?
243 EM: function(viewPort
) {
246 var fontSize
= new svg
.Property('fontSize', svg
.Font
.Parse(svg
.ctx
.font
).fontSize
);
247 if (fontSize
.hasValue()) em
= fontSize
.Length
.toPixels(viewPort
);
252 // get the length as pixels
253 toPixels: function(viewPort
) {
254 if (!that
.hasValue()) return 0;
255 var s
= that
.value
+'';
256 if (s
.match(/em$/)) return that
.numValue() * this.EM(viewPort
);
257 if (s
.match(/ex$/)) return that
.numValue() * this.EM(viewPort
) / 2.0;
258 if (s
.match(/px$/)) return that
.numValue();
259 if (s
.match(/pt$/)) return that
.numValue() * 1.25;
260 if (s
.match(/pc$/)) return that
.numValue() * 15;
261 if (s
.match(/cm$/)) return that
.numValue() * this.DPI(viewPort
) / 2.54;
262 if (s
.match(/mm$/)) return that
.numValue() * this.DPI(viewPort
) / 25.4;
263 if (s
.match(/in$/)) return that
.numValue() * this.DPI(viewPort
);
264 if (s
.match(/%$/)) return that
.numValue() * svg
.ViewPort
.ComputeSize(viewPort
);
265 return that
.numValue();
271 // get the time as milliseconds
272 toMilliseconds: function() {
273 if (!that
.hasValue()) return 0;
274 var s
= that
.value
+'';
275 if (s
.match(/s$/)) return that
.numValue() * 1000;
276 if (s
.match(/ms$/)) return that
.numValue();
277 return that
.numValue();
283 // get the angle as radians
284 toRadians: function() {
285 if (!that
.hasValue()) return 0;
286 var s
= that
.value
+'';
287 if (s
.match(/deg$/)) return that
.numValue() * (Math
.PI
/ 180.0);
288 if (s
.match(/grad$/)) return that
.numValue() * (Math
.PI
/ 200.0);
289 if (s
.match(/rad$/)) return that
.numValue();
290 return that
.numValue() * (Math
.PI
/ 180.0);
296 svg
.Font
= new (function() {
297 this.Styles
= ['normal','italic','oblique','inherit'];
298 this.Variants
= ['normal','small-caps','inherit'];
299 this.Weights
= ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit'];
301 this.CreateFont = function(fontStyle
, fontVariant
, fontWeight
, fontSize
, fontFamily
, inherit
) {
302 var f
= inherit
!= null ? this.Parse(inherit
) : this.CreateFont('', '', '', '', '', svg
.ctx
.font
);
304 fontFamily
: fontFamily
|| f
.fontFamily
,
305 fontSize
: fontSize
|| f
.fontSize
,
306 fontStyle
: fontStyle
|| f
.fontStyle
,
307 fontWeight
: fontWeight
|| f
.fontWeight
,
308 fontVariant
: fontVariant
|| f
.fontVariant
,
309 toString: function () { return [this.fontStyle
, this.fontVariant
, this.fontWeight
, this.fontSize
, this.fontFamily
].join(' ') }
314 this.Parse = function(s
) {
316 var d
= svg
.trim(svg
.compressSpaces(s
|| '')).split(' ');
317 var set = { fontSize
: false, fontStyle
: false, fontWeight
: false, fontVariant
: false }
319 for (var i
=0; i
<d
.length
; i
++) {
320 if (!set.fontStyle
&& that
.Styles
.indexOf(d
[i
]) != -1) { if (d
[i
] != 'inherit') f
.fontStyle
= d
[i
]; set.fontStyle
= true; }
321 else if (!set.fontVariant
&& that
.Variants
.indexOf(d
[i
]) != -1) { if (d
[i
] != 'inherit') f
.fontVariant
= d
[i
]; set.fontStyle
= set.fontVariant
= true; }
322 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; }
323 else if (!set.fontSize
) { if (d
[i
] != 'inherit') f
.fontSize
= d
[i
].split('/')[0]; set.fontStyle
= set.fontVariant
= set.fontWeight
= set.fontSize
= true; }
324 else { if (d
[i
] != 'inherit') ff
+= d
[i
]; }
325 } if (ff
!= '') f
.fontFamily
= ff
;
331 svg
.ToNumberArray = function(s
) {
332 var a
= svg
.trim(svg
.compressSpaces((s
|| '').replace(/,/g
, ' '))).split(' ');
333 for (var i
=0; i
<a
.length
; i
++) {
334 a
[i
] = parseFloat(a
[i
]);
338 svg
.Point = function(x
, y
) {
342 this.angleTo = function(p
) {
343 return Math
.atan2(p
.y
- this.y
, p
.x
- this.x
);
346 this.applyTransform = function(v
) {
347 var xp
= this.x
* v
[0] + this.y
* v
[2] + v
[4];
348 var yp
= this.x
* v
[1] + this.y
* v
[3] + v
[5];
353 svg
.CreatePoint = function(s
) {
354 var a
= svg
.ToNumberArray(s
);
355 return new svg
.Point(a
[0], a
[1]);
357 svg
.CreatePath = function(s
) {
358 var a
= svg
.ToNumberArray(s
);
360 for (var i
=0; i
<a
.length
; i
+=2) {
361 path
.push(new svg
.Point(a
[i
], a
[i
+1]));
367 svg
.BoundingBox = function(x1
, y1
, x2
, y2
) { // pass in initial points if you want
368 this.x1
= Number
.NaN
;
369 this.y1
= Number
.NaN
;
370 this.x2
= Number
.NaN
;
371 this.y2
= Number
.NaN
;
373 this.x = function() { return this.x1
; }
374 this.y = function() { return this.y1
; }
375 this.width = function() { return this.x2
- this.x1
; }
376 this.height = function() { return this.y2
- this.y1
; }
378 this.addPoint = function(x
, y
) {
380 if (isNaN(this.x1
) || isNaN(this.x2
)) {
384 if (x
< this.x1
) this.x1
= x
;
385 if (x
> this.x2
) this.x2
= x
;
389 if (isNaN(this.y1
) || isNaN(this.y2
)) {
393 if (y
< this.y1
) this.y1
= y
;
394 if (y
> this.y2
) this.y2
= y
;
397 this.addX = function(x
) { this.addPoint(x
, null); }
398 this.addY = function(y
) { this.addPoint(null, y
); }
400 this.addBoundingBox = function(bb
) {
401 this.addPoint(bb
.x1
, bb
.y1
);
402 this.addPoint(bb
.x2
, bb
.y2
);
405 this.addQuadraticCurve = function(p0x
, p0y
, p1x
, p1y
, p2x
, p2y
) {
406 var cp1x
= p0x
+ 2/3 * (p1x - p0x); // CP1
= QP0
+ 2/3 *(QP1
-QP0
)
407 var cp1y
= p0y
+ 2/3 * (p1y - p0y); // CP1
= QP0
+ 2/3 *(QP1
-QP0
)
408 var cp2x
= cp1x
+ 1/3 * (p2x - p0x); // CP2
= CP1
+ 1/3 *(QP2
-QP0
)
409 var cp2y
= cp1y
+ 1/3 * (p2y - p0y); // CP2
= CP1
+ 1/3 *(QP2
-QP0
)
410 this.addBezierCurve(p0x
, p0y
, cp1x
, cp2x
, cp1y
, cp2y
, p2x
, p2y
);
413 this.addBezierCurve = function(p0x
, p0y
, p1x
, p1y
, p2x
, p2y
, p3x
, p3y
) {
414 // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
415 var p0
= [p0x
, p0y
], p1
= [p1x
, p1y
], p2
= [p2x
, p2y
], p3
= [p3x
, p3y
];
416 this.addPoint(p0
[0], p0
[1]);
417 this.addPoint(p3
[0], p3
[1]);
419 for (i
=0; i
<=1; i
++) {
420 var f = function(t
) {
421 return Math
.pow(1-t
, 3) * p0
[i
]
422 + 3 * Math
.pow(1-t
, 2) * t
* p1
[i
]
423 + 3 * (1-t
) * Math
.pow(t
, 2) * p2
[i
]
424 + Math
.pow(t
, 3) * p3
[i
];
427 var b
= 6 * p0
[i
] - 12 * p1
[i
] + 6 * p2
[i
];
428 var a
= -3 * p0
[i
] + 9 * p1
[i
] - 9 * p2
[i
] + 3 * p3
[i
];
429 var c
= 3 * p1
[i
] - 3 * p0
[i
];
432 if (b
== 0) continue;
434 if (0 < t
&& t
< 1) {
435 if (i
== 0) this.addX(f(t
));
436 if (i
== 1) this.addY(f(t
));
441 var b2ac
= Math
.pow(b
, 2) - 4 * c
* a
;
442 if (b2ac
< 0) continue;
443 var t1
= (-b
+ Math
.sqrt(b2ac
)) / (2 * a
);
444 if (0 < t1
&& t1
< 1) {
445 if (i
== 0) this.addX(f(t1
));
446 if (i
== 1) this.addY(f(t1
));
448 var t2
= (-b
- Math
.sqrt(b2ac
)) / (2 * a
);
449 if (0 < t2
&& t2
< 1) {
450 if (i
== 0) this.addX(f(t2
));
451 if (i
== 1) this.addY(f(t2
));
456 this.isPointInBox = function(x
, y
) {
457 return (this.x1
<= x
&& x
<= this.x2
&& this.y1
<= y
&& y
<= this.y2
);
460 this.addPoint(x1
, y1
);
461 this.addPoint(x2
, y2
);
465 svg
.Transform = function(v
) {
470 this.Type
.translate = function(s
) {
471 this.p
= svg
.CreatePoint(s
);
472 this.apply = function(ctx
) {
473 ctx
.translate(this.p
.x
|| 0.0, this.p
.y
|| 0.0);
475 this.applyToPoint = function(p
) {
476 p
.applyTransform([1, 0, 0, 1, this.p
.x
|| 0.0, this.p
.y
|| 0.0]);
481 this.Type
.rotate = function(s
) {
482 var a
= svg
.ToNumberArray(s
);
483 this.angle
= new svg
.Property('angle', a
[0]);
486 this.apply = function(ctx
) {
487 ctx
.translate(this.cx
, this.cy
);
488 ctx
.rotate(this.angle
.Angle
.toRadians());
489 ctx
.translate(-this.cx
, -this.cy
);
491 this.applyToPoint = function(p
) {
492 var a
= this.angle
.Angle
.toRadians();
493 p
.applyTransform([1, 0, 0, 1, this.p
.x
|| 0.0, this.p
.y
|| 0.0]);
494 p
.applyTransform([Math
.cos(a
), Math
.sin(a
), -Math
.sin(a
), Math
.cos(a
), 0, 0]);
495 p
.applyTransform([1, 0, 0, 1, -this.p
.x
|| 0.0, -this.p
.y
|| 0.0]);
499 this.Type
.scale = function(s
) {
500 this.p
= svg
.CreatePoint(s
);
501 this.apply = function(ctx
) {
502 ctx
.scale(this.p
.x
|| 1.0, this.p
.y
|| this.p
.x
|| 1.0);
504 this.applyToPoint = function(p
) {
505 p
.applyTransform([this.p
.x
|| 0.0, 0, 0, this.p
.y
|| 0.0, 0, 0]);
509 this.Type
.matrix = function(s
) {
510 this.m
= svg
.ToNumberArray(s
);
511 this.apply = function(ctx
) {
512 ctx
.transform(this.m
[0], this.m
[1], this.m
[2], this.m
[3], this.m
[4], this.m
[5]);
514 this.applyToPoint = function(p
) {
515 p
.applyTransform(this.m
);
519 this.Type
.SkewBase = function(s
) {
520 this.base
= that
.Type
.matrix
;
522 this.angle
= new svg
.Property('angle', s
);
524 this.Type
.SkewBase
.prototype = new this.Type
.matrix
;
526 this.Type
.skewX = function(s
) {
527 this.base
= that
.Type
.SkewBase
;
529 this.m
= [1, 0, Math
.tan(this.angle
.Angle
.toRadians()), 1, 0, 0];
531 this.Type
.skewX
.prototype = new this.Type
.SkewBase
;
533 this.Type
.skewY = function(s
) {
534 this.base
= that
.Type
.SkewBase
;
536 this.m
= [1, Math
.tan(this.angle
.Angle
.toRadians()), 0, 1, 0, 0];
538 this.Type
.skewY
.prototype = new this.Type
.SkewBase
;
540 this.transforms
= [];
542 this.apply = function(ctx
) {
543 for (var i
=0; i
<this.transforms
.length
; i
++) {
544 this.transforms
[i
].apply(ctx
);
548 this.applyToPoint = function(p
) {
549 for (var i
=0; i
<this.transforms
.length
; i
++) {
550 this.transforms
[i
].applyToPoint(p
);
554 var data
= v
.split(/\s(?=[a-z])/);
555 for (var i
=0; i
<data
.length
; i
++) {
556 var type
= data
[i
].split('(')[0];
557 var s
= data
[i
].split('(')[1].replace(')','');
558 var transform
= new this.Type
[type
](s
);
559 this.transforms
.push(transform
);
564 svg
.AspectRatio = function(ctx
, aspectRatio
, width
, desiredWidth
, height
, desiredHeight
, minX
, minY
, refX
, refY
) {
565 // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
566 aspectRatio
= svg
.compressSpaces(aspectRatio
);
567 aspectRatio
= aspectRatio
.replace(/^defer\s/,''); // ignore defer
568 var align
= aspectRatio
.split(' ')[0] || 'xMidYMid';
569 var meetOrSlice
= aspectRatio
.split(' ')[1] || 'meet';
572 var scaleX
= width
/ desiredWidth
;
573 var scaleY
= height
/ desiredHeight
;
574 var scaleMin
= Math
.min(scaleX
, scaleY
);
575 var scaleMax
= Math
.max(scaleX
, scaleY
);
576 if (meetOrSlice
== 'meet') { desiredWidth
*= scaleMin
; desiredHeight
*= scaleMin
; }
577 if (meetOrSlice
== 'slice') { desiredWidth
*= scaleMax
; desiredHeight
*= scaleMax
; }
579 refX
= new svg
.Property('refX', refX
);
580 refY
= new svg
.Property('refY', refY
);
581 if (refX
.hasValue() && refY
.hasValue()) {
582 ctx
.translate(-scaleMin
* refX
.Length
.toPixels('x'), -scaleMin
* refY
.Length
.toPixels('y'));
586 if (align
.match(/^xMid/) && ((meetOrSlice
== 'meet' && scaleMin
== scaleY
) || (meetOrSlice
== 'slice' && scaleMax
== scaleY
))) ctx
.translate(width
/ 2.0 - desiredWidth
/ 2.0, 0);
587 if (align
.match(/YMid$/) && ((meetOrSlice
== 'meet' && scaleMin
== scaleX
) || (meetOrSlice
== 'slice' && scaleMax
== scaleX
))) ctx
.translate(0, height
/ 2.0 - desiredHeight
/ 2.0);
588 if (align
.match(/^xMax/) && ((meetOrSlice
== 'meet' && scaleMin
== scaleY
) || (meetOrSlice
== 'slice' && scaleMax
== scaleY
))) ctx
.translate(width
- desiredWidth
, 0);
589 if (align
.match(/YMax$/) && ((meetOrSlice
== 'meet' && scaleMin
== scaleX
) || (meetOrSlice
== 'slice' && scaleMax
== scaleX
))) ctx
.translate(0, height
- desiredHeight
);
593 if (align
== 'none') ctx
.scale(scaleX
, scaleY
);
594 else if (meetOrSlice
== 'meet') ctx
.scale(scaleMin
, scaleMin
);
595 else if (meetOrSlice
== 'slice') ctx
.scale(scaleMax
, scaleMax
);
598 ctx
.translate(minX
== null ? 0 : -minX
, minY
== null ? 0 : -minY
);
604 svg
.Element
.ElementBase = function(node
) {
605 this.attributes
= {};
609 // get or create attribute
610 this.attribute = function(name
, createIfNotExists
) {
611 var a
= this.attributes
[name
];
612 if (a
!= null) return a
;
614 a
= new svg
.Property(name
, '');
615 if (createIfNotExists
== true) this.attributes
[name
] = a
;
619 // get or create style
620 this.style = function(name
, createIfNotExists
) {
621 var s
= this.styles
[name
];
622 if (s
!= null) return s
;
624 var a
= this.attribute(name
);
625 if (a
!= null && a
.hasValue()) {
629 s
= new svg
.Property(name
, '');
630 if (createIfNotExists
== true) this.styles
[name
] = s
;
635 this.render = function(ctx
) {
636 // don't render display=none
637 if (this.attribute('display').value
== 'none') return;
640 this.setContext(ctx
);
641 this.renderChildren(ctx
);
642 this.clearContext(ctx
);
647 this.setContext = function(ctx
) {
651 // base clear context
652 this.clearContext = function(ctx
) {
656 // base render children
657 this.renderChildren = function(ctx
) {
658 for (var i
=0; i
<this.children
.length
; i
++) {
659 this.children
[i
].render(ctx
);
663 this.addChild = function(childNode
, create
) {
664 var child
= childNode
;
665 if (create
) child
= svg
.CreateElement(childNode
);
667 this.children
.push(child
);
670 if (node
!= null && node
.nodeType
== 1) { //ELEMENT_NODE
672 for (var i
=0; i
<node
.childNodes
.length
; i
++) {
673 var childNode
= node
.childNodes
[i
];
674 if (childNode
.nodeType
== 1) this.addChild(childNode
, true); //ELEMENT_NODE
678 for (var i
=0; i
<node
.attributes
.length
; i
++) {
679 var attribute
= node
.attributes
[i
];
680 this.attributes
[attribute
.nodeName
] = new svg
.Property(attribute
.nodeName
, attribute
.nodeValue
);
684 var styles
= svg
.Styles
[this.type
];
685 if (styles
!= null) {
686 for (var name
in styles
) {
687 this.styles
[name
] = styles
[name
];
692 if (this.attribute('class').hasValue()) {
693 var classes
= svg
.compressSpaces(this.attribute('class').value
).split(' ');
694 for (var j
=0; j
<classes
.length
; j
++) {
695 styles
= svg
.Styles
['.'+classes
[j
]];
696 if (styles
!= null) {
697 for (var name
in styles
) {
698 this.styles
[name
] = styles
[name
];
705 if (this.attribute('style').hasValue()) {
706 var styles
= this.attribute('style').value
.split(';');
707 for (var i
=0; i
<styles
.length
; i
++) {
708 if (svg
.trim(styles
[i
]) != '') {
709 var style
= styles
[i
].split(':');
710 var name
= svg
.trim(style
[0]);
711 var value
= svg
.trim(style
[1]);
712 this.styles
[name
] = new svg
.Property(name
, value
);
718 if (this.attribute('id').hasValue()) {
719 if (svg
.Definitions
[this.attribute('id').value
] == null) {
720 svg
.Definitions
[this.attribute('id').value
] = this;
726 svg
.Element
.RenderedElementBase = function(node
) {
727 this.base
= svg
.Element
.ElementBase
;
730 this.setContext = function(ctx
) {
732 if (this.style('fill').Definition
.isUrl()) {
733 var fs
= this.style('fill').Definition
.getFillStyle(this);
734 if (fs
!= null) ctx
.fillStyle
= fs
;
736 else if (this.style('fill').hasValue()) {
737 var fillStyle
= this.style('fill');
738 if (this.style('fill-opacity').hasValue()) fillStyle
= fillStyle
.Color
.addOpacity(this.style('fill-opacity').value
);
739 ctx
.fillStyle
= (fillStyle
.value
== 'none' ? 'rgba(0,0,0,0)' : fillStyle
.value
);
743 if (this.style('stroke').Definition
.isUrl()) {
744 var fs
= this.style('stroke').Definition
.getFillStyle(this);
745 if (fs
!= null) ctx
.strokeStyle
= fs
;
747 else if (this.style('stroke').hasValue()) {
748 var strokeStyle
= this.style('stroke');
749 if (this.style('stroke-opacity').hasValue()) strokeStyle
= strokeStyle
.Color
.addOpacity(this.style('stroke-opacity').value
);
750 ctx
.strokeStyle
= (strokeStyle
.value
== 'none' ? 'rgba(0,0,0,0)' : strokeStyle
.value
);
752 if (this.style('stroke-width').hasValue()) ctx
.lineWidth
= this.style('stroke-width').Length
.toPixels();
753 if (this.style('stroke-linecap').hasValue()) ctx
.lineCap
= this.style('stroke-linecap').value
;
754 if (this.style('stroke-linejoin').hasValue()) ctx
.lineJoin
= this.style('stroke-linejoin').value
;
755 if (this.style('stroke-miterlimit').hasValue()) ctx
.miterLimit
= this.style('stroke-miterlimit').value
;
758 if (typeof(ctx
.font
) != 'undefined') {
759 ctx
.font
= svg
.Font
.CreateFont(
760 this.style('font-style').value
,
761 this.style('font-variant').value
,
762 this.style('font-weight').value
,
763 this.style('font-size').hasValue() ? this.style('font-size').Length
.toPixels() + 'px' : '',
764 this.style('font-family').value
).toString();
768 if (this.attribute('transform').hasValue()) {
769 var transform
= new svg
.Transform(this.attribute('transform').value
);
770 transform
.apply(ctx
);
774 if (this.attribute('clip-path').hasValue()) {
775 var clip
= this.attribute('clip-path').Definition
.getDefinition();
776 if (clip
!= null) clip
.apply(ctx
);
780 if (this.style('opacity').hasValue()) {
781 ctx
.globalAlpha
= this.style('opacity').numValue();
785 svg
.Element
.RenderedElementBase
.prototype = new svg
.Element
.ElementBase
;
787 svg
.Element
.PathElementBase = function(node
) {
788 this.base
= svg
.Element
.RenderedElementBase
;
791 this.path = function(ctx
) {
792 if (ctx
!= null) ctx
.beginPath();
793 return new svg
.BoundingBox();
796 this.renderChildren = function(ctx
) {
798 svg
.Mouse
.checkPath(this, ctx
);
799 if (ctx
.fillStyle
!= '') ctx
.fill();
800 if (ctx
.strokeStyle
!= '') ctx
.stroke();
802 var markers
= this.getMarkers();
803 if (markers
!= null) {
804 if (this.attribute('marker-start').Definition
.isUrl()) {
805 var marker
= this.attribute('marker-start').Definition
.getDefinition();
806 marker
.render(ctx
, markers
[0][0], markers
[0][1]);
808 if (this.attribute('marker-mid').Definition
.isUrl()) {
809 var marker
= this.attribute('marker-mid').Definition
.getDefinition();
810 for (var i
=1;i
<markers
.length
-1;i
++) {
811 marker
.render(ctx
, markers
[i
][0], markers
[i
][1]);
814 if (this.attribute('marker-end').Definition
.isUrl()) {
815 var marker
= this.attribute('marker-end').Definition
.getDefinition();
816 marker
.render(ctx
, markers
[markers
.length
-1][0], markers
[markers
.length
-1][1]);
821 this.getBoundingBox = function() {
825 this.getMarkers = function() {
829 svg
.Element
.PathElementBase
.prototype = new svg
.Element
.RenderedElementBase
;
832 svg
.Element
.svg = function(node
) {
833 this.base
= svg
.Element
.RenderedElementBase
;
836 this.baseClearContext
= this.clearContext
;
837 this.clearContext = function(ctx
) {
838 this.baseClearContext(ctx
);
839 svg
.ViewPort
.RemoveCurrent();
842 this.baseSetContext
= this.setContext
;
843 this.setContext = function(ctx
) {
845 ctx
.strokeStyle
= 'rgba(0,0,0,0)';
846 ctx
.lineCap
= 'butt';
847 ctx
.lineJoin
= 'miter';
850 this.baseSetContext(ctx
);
852 // create new view port
853 if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
854 ctx
.translate(this.attribute('x').Length
.toPixels('x'), this.attribute('y').Length
.toPixels('y'));
857 var width
= svg
.ViewPort
.width();
858 var height
= svg
.ViewPort
.height();
859 if (this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
860 width
= this.attribute('width').Length
.toPixels('x');
861 height
= this.attribute('height').Length
.toPixels('y');
865 if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
866 x
= -this.attribute('refX').Length
.toPixels('x');
867 y
= -this.attribute('refY').Length
.toPixels('y');
872 ctx
.lineTo(width
, y
);
873 ctx
.lineTo(width
, height
);
874 ctx
.lineTo(x
, height
);
878 svg
.ViewPort
.SetCurrent(width
, height
);
881 if (this.attribute('viewBox').hasValue()) {
882 var viewBox
= svg
.ToNumberArray(this.attribute('viewBox').value
);
883 var minX
= viewBox
[0];
884 var minY
= viewBox
[1];
889 this.attribute('preserveAspectRatio').value
,
890 svg
.ViewPort
.width(),
892 svg
.ViewPort
.height(),
896 this.attribute('refX').value
,
897 this.attribute('refY').value
);
899 svg
.ViewPort
.RemoveCurrent();
900 svg
.ViewPort
.SetCurrent(viewBox
[2], viewBox
[3]);
904 svg
.Element
.svg
.prototype = new svg
.Element
.RenderedElementBase
;
907 svg
.Element
.rect = function(node
) {
908 this.base
= svg
.Element
.PathElementBase
;
911 this.path = function(ctx
) {
912 var x
= this.attribute('x').Length
.toPixels('x');
913 var y
= this.attribute('y').Length
.toPixels('y');
914 var width
= this.attribute('width').Length
.toPixels('x');
915 var height
= this.attribute('height').Length
.toPixels('y');
916 var rx
= this.attribute('rx').Length
.toPixels('x');
917 var ry
= this.attribute('ry').Length
.toPixels('y');
918 if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry
= rx
;
919 if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx
= ry
;
923 ctx
.moveTo(x
+ rx
, y
);
924 ctx
.lineTo(x
+ width
- rx
, y
);
925 ctx
.quadraticCurveTo(x
+ width
, y
, x
+ width
, y
+ ry
)
926 ctx
.lineTo(x
+ width
, y
+ height
- ry
);
927 ctx
.quadraticCurveTo(x
+ width
, y
+ height
, x
+ width
- rx
, y
+ height
)
928 ctx
.lineTo(x
+ rx
, y
+ height
);
929 ctx
.quadraticCurveTo(x
, y
+ height
, x
, y
+ height
- ry
)
930 ctx
.lineTo(x
, y
+ ry
);
931 ctx
.quadraticCurveTo(x
, y
, x
+ rx
, y
)
935 return new svg
.BoundingBox(x
, y
, x
+ width
, y
+ height
);
938 svg
.Element
.rect
.prototype = new svg
.Element
.PathElementBase
;
941 svg
.Element
.circle = function(node
) {
942 this.base
= svg
.Element
.PathElementBase
;
945 this.path = function(ctx
) {
946 var cx
= this.attribute('cx').Length
.toPixels('x');
947 var cy
= this.attribute('cy').Length
.toPixels('y');
948 var r
= this.attribute('r').Length
.toPixels();
952 ctx
.arc(cx
, cy
, r
, 0, Math
.PI
* 2, true);
956 return new svg
.BoundingBox(cx
- r
, cy
- r
, cx
+ r
, cy
+ r
);
959 svg
.Element
.circle
.prototype = new svg
.Element
.PathElementBase
;
962 svg
.Element
.ellipse = function(node
) {
963 this.base
= svg
.Element
.PathElementBase
;
966 this.path = function(ctx
) {
967 var KAPPA
= 4 * ((Math
.sqrt(2) - 1) / 3);
968 var rx
= this.attribute('rx').Length
.toPixels('x');
969 var ry
= this.attribute('ry').Length
.toPixels('y');
970 var cx
= this.attribute('cx').Length
.toPixels('x');
971 var cy
= this.attribute('cy').Length
.toPixels('y');
975 ctx
.moveTo(cx
, cy
- ry
);
976 ctx
.bezierCurveTo(cx
+ (KAPPA
* rx
), cy
- ry
, cx
+ rx
, cy
- (KAPPA
* ry
), cx
+ rx
, cy
);
977 ctx
.bezierCurveTo(cx
+ rx
, cy
+ (KAPPA
* ry
), cx
+ (KAPPA
* rx
), cy
+ ry
, cx
, cy
+ ry
);
978 ctx
.bezierCurveTo(cx
- (KAPPA
* rx
), cy
+ ry
, cx
- rx
, cy
+ (KAPPA
* ry
), cx
- rx
, cy
);
979 ctx
.bezierCurveTo(cx
- rx
, cy
- (KAPPA
* ry
), cx
- (KAPPA
* rx
), cy
- ry
, cx
, cy
- ry
);
983 return new svg
.BoundingBox(cx
- rx
, cy
- ry
, cx
+ rx
, cy
+ ry
);
986 svg
.Element
.ellipse
.prototype = new svg
.Element
.PathElementBase
;
989 svg
.Element
.line = function(node
) {
990 this.base
= svg
.Element
.PathElementBase
;
993 this.getPoints = function() {
995 new svg
.Point(this.attribute('x1').Length
.toPixels('x'), this.attribute('y1').Length
.toPixels('y')),
996 new svg
.Point(this.attribute('x2').Length
.toPixels('x'), this.attribute('y2').Length
.toPixels('y'))];
999 this.path = function(ctx
) {
1000 var points
= this.getPoints();
1004 ctx
.moveTo(points
[0].x
, points
[0].y
);
1005 ctx
.lineTo(points
[1].x
, points
[1].y
);
1008 return new svg
.BoundingBox(points
[0].x
, points
[0].y
, points
[1].x
, points
[1].y
);
1011 this.getMarkers = function() {
1012 var points
= this.getPoints();
1013 var a
= points
[0].angleTo(points
[1]);
1014 return [[points
[0], a
], [points
[1], a
]];
1017 svg
.Element
.line
.prototype = new svg
.Element
.PathElementBase
;
1020 svg
.Element
.polyline = function(node
) {
1021 this.base
= svg
.Element
.PathElementBase
;
1024 this.points
= svg
.CreatePath(this.attribute('points').value
);
1025 this.path = function(ctx
) {
1026 var bb
= new svg
.BoundingBox(this.points
[0].x
, this.points
[0].y
);
1029 ctx
.moveTo(this.points
[0].x
, this.points
[0].y
);
1031 for (var i
=1; i
<this.points
.length
; i
++) {
1032 bb
.addPoint(this.points
[i
].x
, this.points
[i
].y
);
1033 if (ctx
!= null) ctx
.lineTo(this.points
[i
].x
, this.points
[i
].y
);
1038 this.getMarkers = function() {
1040 for (var i
=0; i
<this.points
.length
- 1; i
++) {
1041 markers
.push([this.points
[i
], this.points
[i
].angleTo(this.points
[i
+1])]);
1043 markers
.push([this.points
[this.points
.length
-1], markers
[markers
.length
-1][1]]);
1047 svg
.Element
.polyline
.prototype = new svg
.Element
.PathElementBase
;
1050 svg
.Element
.polygon = function(node
) {
1051 this.base
= svg
.Element
.polyline
;
1054 this.basePath
= this.path
;
1055 this.path = function(ctx
) {
1056 var bb
= this.basePath(ctx
);
1058 ctx
.lineTo(this.points
[0].x
, this.points
[0].y
);
1064 svg
.Element
.polygon
.prototype = new svg
.Element
.polyline
;
1067 svg
.Element
.path = function(node
) {
1068 this.base
= svg
.Element
.PathElementBase
;
1071 var d
= this.attribute('d').value
;
1072 // TODO: floating points, convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
1073 d
= d
.replace(/,/gm,' '); // get rid
of all commas
1074 d
= d
.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
1075 d
= d
.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
1076 d
= d
.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points
1077 d
= d
.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
1078 d
= d
.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma
1079 d
= d
.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma
1080 d
= d
.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
1081 d
= svg
.compressSpaces(d
); // compress multiple spaces
1083 this.PathParser
= new (function(d
) {
1084 this.tokens
= d
.split(' ');
1086 this.reset = function() {
1089 this.previousCommand
= '';
1090 this.start
= new svg
.Point(0, 0);
1091 this.control
= new svg
.Point(0, 0);
1092 this.current
= new svg
.Point(0, 0);
1097 this.isEnd = function() {
1098 return this.i
>= this.tokens
.length
- 1;
1101 this.isCommandOrEnd = function() {
1102 if (this.isEnd()) return true;
1103 return this.tokens
[this.i
+ 1].match(/[A-Za-z]/) != null;
1106 this.isRelativeCommand = function() {
1107 return this.command
== this.command
.toLowerCase();
1110 this.getToken = function() {
1111 this.i
= this.i
+ 1;
1112 return this.tokens
[this.i
];
1115 this.getScalar = function() {
1116 return parseFloat(this.getToken());
1119 this.nextCommand = function() {
1120 this.previousCommand
= this.command
;
1121 this.command
= this.getToken();
1124 this.getPoint = function() {
1125 var p
= new svg
.Point(this.getScalar(), this.getScalar());
1126 return this.makeAbsolute(p
);
1129 this.getAsControlPoint = function() {
1130 var p
= this.getPoint();
1135 this.getAsCurrentPoint = function() {
1136 var p
= this.getPoint();
1141 this.getReflectedControlPoint = function() {
1142 if (this.previousCommand
.toLowerCase() != 'c' && this.previousCommand
.toLowerCase() != 's') {
1143 return this.current
;
1147 var p
= new svg
.Point(2 * this.current
.x
- this.control
.x
, 2 * this.current
.y
- this.control
.y
);
1151 this.makeAbsolute = function(p
) {
1152 if (this.isRelativeCommand()) {
1153 p
.x
= this.current
.x
+ p
.x
;
1154 p
.y
= this.current
.y
+ p
.y
;
1159 this.addMarker = function(p
, from) {
1160 this.addMarkerAngle(p
, from == null ? null : from.angleTo(p
));
1163 this.addMarkerAngle = function(p
, a
) {
1164 this.points
.push(p
);
1165 this.angles
.push(a
);
1168 this.getMarkerPoints = function() { return this.points
; }
1169 this.getMarkerAngles = function() {
1170 for (var i
=0; i
<this.angles
.length
; i
++) {
1171 if (this.angles
[i
] == null) {
1172 for (var j
=i
+1; j
<this.angles
.length
; j
++) {
1173 if (this.angles
[j
] != null) {
1174 this.angles
[i
] = this.angles
[j
];
1184 this.path = function(ctx
) {
1185 var pp
= this.PathParser
;
1188 var bb
= new svg
.BoundingBox();
1190 if(this.attribute('visibility').value
=='hidden') return;
1192 if (ctx
!= null) ctx
.beginPath();
1193 while (!pp
.isEnd()) {
1195 switch (pp
.command
.toUpperCase()) {
1197 var p
= pp
.getAsCurrentPoint();
1199 bb
.addPoint(p
.x
, p
.y
);
1200 if (ctx
!= null) ctx
.moveTo(p
.x
, p
.y
);
1201 pp
.start
= pp
.current
;
1202 while (!pp
.isCommandOrEnd()) {
1203 var p
= pp
.getAsCurrentPoint();
1205 bb
.addPoint(p
.x
, p
.y
);
1206 if (ctx
!= null) ctx
.lineTo(p
.x
, p
.y
);
1210 while (!pp
.isCommandOrEnd()) {
1212 var p
= pp
.getAsCurrentPoint();
1214 bb
.addPoint(p
.x
, p
.y
);
1215 if (ctx
!= null) ctx
.lineTo(p
.x
, p
.y
);
1219 while (!pp
.isCommandOrEnd()) {
1220 var newP
= new svg
.Point((pp
.isRelativeCommand() ? pp
.current
.x
: 0) + pp
.getScalar(), pp
.current
.y
);
1221 pp
.addMarker(newP
, pp
.current
);
1223 bb
.addPoint(pp
.current
.x
, pp
.current
.y
);
1224 if (ctx
!= null) ctx
.lineTo(pp
.current
.x
, pp
.current
.y
);
1228 while (!pp
.isCommandOrEnd()) {
1229 var newP
= new svg
.Point(pp
.current
.x
, (pp
.isRelativeCommand() ? pp
.current
.y
: 0) + pp
.getScalar());
1230 pp
.addMarker(newP
, pp
.current
);
1232 bb
.addPoint(pp
.current
.x
, pp
.current
.y
);
1233 if (ctx
!= null) ctx
.lineTo(pp
.current
.x
, pp
.current
.y
);
1237 while (!pp
.isCommandOrEnd()) {
1238 var curr
= pp
.current
;
1239 var p1
= pp
.getPoint();
1240 var cntrl
= pp
.getAsControlPoint();
1241 var cp
= pp
.getAsCurrentPoint();
1242 pp
.addMarker(cp
, cntrl
);
1243 bb
.addBezierCurve(curr
.x
, curr
.y
, p1
.x
, p1
.y
, cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1244 if (ctx
!= null) ctx
.bezierCurveTo(p1
.x
, p1
.y
, cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1248 while (!pp
.isCommandOrEnd()) {
1249 var curr
= pp
.current
;
1250 var p1
= pp
.getReflectedControlPoint();
1251 var cntrl
= pp
.getAsControlPoint();
1252 var cp
= pp
.getAsCurrentPoint();
1253 pp
.addMarker(cp
, cntrl
);
1254 bb
.addBezierCurve(curr
.x
, curr
.y
, p1
.x
, p1
.y
, cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1255 if (ctx
!= null) ctx
.bezierCurveTo(p1
.x
, p1
.y
, cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1259 while (!pp
.isCommandOrEnd()) {
1260 var curr
= pp
.current
;
1261 var cntrl
= pp
.getAsControlPoint();
1262 var cp
= pp
.getAsCurrentPoint();
1263 pp
.addMarker(cp
, cntrl
);
1264 bb
.addQuadraticCurve(curr
.x
, curr
.y
, cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1265 if (ctx
!= null) ctx
.quadraticCurveTo(cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1269 while (!pp
.isCommandOrEnd()) {
1270 var curr
= pp
.current
;
1271 var cntrl
= pp
.getReflectedControlPoint();
1273 var cp
= pp
.getAsCurrentPoint();
1274 pp
.addMarker(cp
, cntrl
);
1275 bb
.addQuadraticCurve(curr
.x
, curr
.y
, cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1276 if (ctx
!= null) ctx
.quadraticCurveTo(cntrl
.x
, cntrl
.y
, cp
.x
, cp
.y
);
1280 while (!pp
.isCommandOrEnd()) {
1281 var curr
= pp
.current
;
1282 var rx
= pp
.getScalar();
1283 var ry
= pp
.getScalar();
1284 var xAxisRotation
= pp
.getScalar() * (Math
.PI
/ 180.0);
1285 var largeArcFlag
= pp
.getScalar();
1286 var sweepFlag
= pp
.getScalar();
1287 var cp
= pp
.getAsCurrentPoint();
1289 // Conversion from endpoint to center parameterization
1290 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1292 var currp
= new svg
.Point(
1293 Math
.cos(xAxisRotation
) * (curr
.x
- cp
.x
) / 2.0 + Math
.sin(xAxisRotation
) * (curr
.y
- cp
.y
) / 2.0,
1294 -Math
.sin(xAxisRotation
) * (curr
.x
- cp
.x
) / 2.0 + Math
.cos(xAxisRotation
) * (curr
.y
- cp
.y
) / 2.0
1297 var l
= Math
.pow(currp
.x
,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math
.pow(ry
,2);
1303 var s
= (largeArcFlag
== sweepFlag
? -1 : 1) * Math
.sqrt(
1304 ((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))) /
1305 (Math
.pow(rx
,2)*Math
.pow(currp
.y
,2)+Math
.pow(ry
,2)*Math
.pow(currp
.x
,2))
1307 if (isNaN(s
)) s
= 0;
1308 var cpp
= new svg
.Point(s
* rx
* currp
.y
/ ry
, s
* -ry
* currp
.x
/ rx
);
1310 var centp
= new svg
.Point(
1311 (curr
.x
+ cp
.x
) / 2.0 + Math
.cos(xAxisRotation
) * cpp
.x
- Math
.sin(xAxisRotation
) * cpp
.y
,
1312 (curr
.y
+ cp
.y
) / 2.0 + Math
.sin(xAxisRotation
) * cpp
.x
+ Math
.cos(xAxisRotation
) * cpp
.y
1315 var m = function(v
) { return Math
.sqrt(Math
.pow(v
[0],2) + Math
.pow(v
[1],2)); }
1316 // ratio between two vectors
1317 var r = function(u
, v
) { return (u
[0]*v
[0]+u
[1]*v
[1]) / (m(u
)*m(v
)) }
1318 // angle between two vectors
1319 var a = function(u
, v
) { return (u
[0]*v
[1] < u
[1]*v
[0] ? -1 : 1) * Math
.acos(r(u
,v
)); }
1321 var a1
= a([1,0], [(currp
.x
-cpp
.x
)/rx,(currp.y-cpp.y)/ry
]);
1323 var u
= [(currp
.x
-cpp
.x
)/rx,(currp.y-cpp.y)/ry
];
1324 var v
= [(-currp
.x
-cpp
.x
)/rx,(-currp.y-cpp.y)/ry
];
1326 if (r(u
,v
) <= -1) ad
= Math
.PI
;
1327 if (r(u
,v
) >= 1) ad
= 0;
1329 if (sweepFlag
== 0 && ad
> 0) ad
= ad
- 2 * Math
.PI
;
1330 if (sweepFlag
== 1 && ad
< 0) ad
= ad
+ 2 * Math
.PI
;
1333 var halfWay
= new svg
.Point(
1334 centp
.x
- rx
* Math
.cos((a1
+ ad
) / 2),
1335 centp
.y
- ry
* Math
.sin((a1
+ ad
) / 2)
1337 pp
.addMarkerAngle(halfWay
, (a1
+ ad
) / 2 + (sweepFlag
== 0 ? 1 : -1) * Math
.PI
/ 2);
1338 pp
.addMarkerAngle(cp
, ad
+ (sweepFlag
== 0 ? 1 : -1) * Math
.PI
/ 2);
1340 bb
.addPoint(cp
.x
, cp
.y
); // TODO: this is too naive, make it better
1342 var r
= rx
> ry
? rx
: ry
;
1343 var sx
= rx
> ry
? 1 : rx
/ ry
;
1344 var sy
= rx
> ry
? ry
/ rx
: 1;
1346 ctx
.translate(centp
.x
, centp
.y
);
1347 ctx
.rotate(xAxisRotation
);
1349 ctx
.arc(0, 0, r
, a1
, a1
+ ad
, 1 - sweepFlag
);
1350 ctx
.scale(1/sx, 1/sy);
1351 ctx
.rotate(-xAxisRotation
);
1352 ctx
.translate(-centp
.x
, -centp
.y
);
1357 if (ctx
!= null) ctx
.closePath();
1358 pp
.current
= pp
.start
;
1365 this.getMarkers = function() {
1366 var points
= this.PathParser
.getMarkerPoints();
1367 var angles
= this.PathParser
.getMarkerAngles();
1370 for (var i
=0; i
<points
.length
; i
++) {
1371 markers
.push([points
[i
], angles
[i
]]);
1376 svg
.Element
.path
.prototype = new svg
.Element
.PathElementBase
;
1379 svg
.Element
.pattern = function(node
) {
1380 this.base
= svg
.Element
.ElementBase
;
1383 this.createPattern = function(ctx
, element
) {
1384 // render me using a temporary svg element
1385 var tempSvg
= new svg
.Element
.svg();
1386 tempSvg
.attributes
['viewBox'] = new svg
.Property('viewBox', this.attribute('viewBox').value
);
1387 tempSvg
.attributes
['x'] = new svg
.Property('x', this.attribute('x').value
);
1388 tempSvg
.attributes
['y'] = new svg
.Property('y', this.attribute('y').value
);
1389 tempSvg
.attributes
['width'] = new svg
.Property('width', this.attribute('width').value
);
1390 tempSvg
.attributes
['height'] = new svg
.Property('height', this.attribute('height').value
);
1391 tempSvg
.children
= this.children
;
1393 var c
= document
.createElement('canvas');
1394 c
.width
= this.attribute('width').Length
.toPixels();
1395 c
.height
= this.attribute('height').Length
.toPixels();
1396 tempSvg
.render(c
.getContext('2d'));
1397 return ctx
.createPattern(c
, 'repeat');
1400 svg
.Element
.pattern
.prototype = new svg
.Element
.ElementBase
;
1403 svg
.Element
.marker = function(node
) {
1404 this.base
= svg
.Element
.ElementBase
;
1407 this.baseRender
= this.render
;
1408 this.render = function(ctx
, point
, angle
) {
1409 ctx
.translate(point
.x
, point
.y
);
1410 if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx
.rotate(angle
);
1411 if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx
.scale(ctx
.lineWidth
, ctx
.lineWidth
);
1414 // render me using a temporary svg element
1415 var tempSvg
= new svg
.Element
.svg();
1416 tempSvg
.attributes
['viewBox'] = new svg
.Property('viewBox', this.attribute('viewBox').value
);
1417 tempSvg
.attributes
['refX'] = new svg
.Property('refX', this.attribute('refX').value
);
1418 tempSvg
.attributes
['refY'] = new svg
.Property('refY', this.attribute('refY').value
);
1419 tempSvg
.attributes
['width'] = new svg
.Property('width', this.attribute('markerWidth').value
);
1420 tempSvg
.attributes
['height'] = new svg
.Property('height', this.attribute('markerHeight').value
);
1421 tempSvg
.attributes
['fill'] = new svg
.Property('fill', this.attribute('fill').valueOrDefault('black'));
1422 tempSvg
.attributes
['stroke'] = new svg
.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
1423 tempSvg
.children
= this.children
;
1424 tempSvg
.render(ctx
);
1427 if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx
.scale(1/ctx.lineWidth, 1/ctx.lineWidth
);
1428 if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx
.rotate(-angle
);
1429 ctx
.translate(-point
.x
, -point
.y
);
1432 svg
.Element
.marker
.prototype = new svg
.Element
.ElementBase
;
1434 // definitions element
1435 svg
.Element
.defs = function(node
) {
1436 this.base
= svg
.Element
.ElementBase
;
1439 this.render = function(ctx
) {
1443 svg
.Element
.defs
.prototype = new svg
.Element
.ElementBase
;
1445 // base for gradients
1446 svg
.Element
.GradientBase = function(node
) {
1447 this.base
= svg
.Element
.ElementBase
;
1450 this.gradientUnits
= this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
1453 for (var i
=0; i
<this.children
.length
; i
++) {
1454 var child
= this.children
[i
];
1455 this.stops
.push(child
);
1458 this.getGradient = function() {
1462 this.createGradient = function(ctx
, element
) {
1463 var stopsContainer
= this;
1464 if (this.attribute('xlink:href').hasValue()) {
1465 stopsContainer
= this.attribute('xlink:href').Definition
.getDefinition();
1468 var g
= this.getGradient(ctx
, element
);
1469 for (var i
=0; i
<stopsContainer
.stops
.length
; i
++) {
1470 g
.addColorStop(stopsContainer
.stops
[i
].offset
, stopsContainer
.stops
[i
].color
);
1475 svg
.Element
.GradientBase
.prototype = new svg
.Element
.ElementBase
;
1477 // linear gradient element
1478 svg
.Element
.linearGradient = function(node
) {
1479 this.base
= svg
.Element
.GradientBase
;
1482 this.getGradient = function(ctx
, element
) {
1483 var bb
= element
.getBoundingBox();
1485 var x1
= (this.gradientUnits
== 'objectBoundingBox'
1486 ? bb
.x() + bb
.width() * this.attribute('x1').numValue()
1487 : this.attribute('x1').Length
.toPixels('x'));
1488 var y1
= (this.gradientUnits
== 'objectBoundingBox'
1489 ? bb
.y() + bb
.height() * this.attribute('y1').numValue()
1490 : this.attribute('y1').Length
.toPixels('y'));
1491 var x2
= (this.gradientUnits
== 'objectBoundingBox'
1492 ? bb
.x() + bb
.width() * this.attribute('x2').numValue()
1493 : this.attribute('x2').Length
.toPixels('x'));
1494 var y2
= (this.gradientUnits
== 'objectBoundingBox'
1495 ? bb
.y() + bb
.height() * this.attribute('y2').numValue()
1496 : this.attribute('y2').Length
.toPixels('y'));
1498 var p1
= new svg
.Point(x1
, y1
);
1499 var p2
= new svg
.Point(x2
, y2
);
1500 if (this.attribute('gradientTransform').hasValue()) {
1501 var transform
= new svg
.Transform(this.attribute('gradientTransform').value
);
1502 transform
.applyToPoint(p1
);
1503 transform
.applyToPoint(p2
);
1506 return ctx
.createLinearGradient(p1
.x
, p1
.y
, p2
.x
, p2
.y
);
1509 svg
.Element
.linearGradient
.prototype = new svg
.Element
.GradientBase
;
1511 // radial gradient element
1512 svg
.Element
.radialGradient = function(node
) {
1513 this.base
= svg
.Element
.GradientBase
;
1516 this.getGradient = function(ctx
, element
) {
1517 var bb
= element
.getBoundingBox();
1519 var cx
= (this.gradientUnits
== 'objectBoundingBox'
1520 ? bb
.x() + bb
.width() * this.attribute('cx').numValue()
1521 : this.attribute('cx').Length
.toPixels('x'));
1522 var cy
= (this.gradientUnits
== 'objectBoundingBox'
1523 ? bb
.y() + bb
.height() * this.attribute('cy').numValue()
1524 : this.attribute('cy').Length
.toPixels('y'));
1528 if (this.attribute('fx').hasValue()) {
1529 fx
= (this.gradientUnits
== 'objectBoundingBox'
1530 ? bb
.x() + bb
.width() * this.attribute('fx').numValue()
1531 : this.attribute('fx').Length
.toPixels('x'));
1533 if (this.attribute('fy').hasValue()) {
1534 fy
= (this.gradientUnits
== 'objectBoundingBox'
1535 ? bb
.y() + bb
.height() * this.attribute('fy').numValue()
1536 : this.attribute('fy').Length
.toPixels('y'));
1539 var r
= (this.gradientUnits
== 'objectBoundingBox'
1540 ? (bb
.width() + bb
.height()) / 2.0 * this.attribute('r').numValue()
1541 : this.attribute('r').Length
.toPixels());
1543 var c
= new svg
.Point(cx
, cy
);
1544 var f
= new svg
.Point(fx
, fy
);
1545 if (this.attribute('gradientTransform').hasValue()) {
1546 var transform
= new svg
.Transform(this.attribute('gradientTransform').value
);
1547 transform
.applyToPoint(c
);
1548 transform
.applyToPoint(f
);
1550 for (var i
=0; i
<transform
.transforms
.length
; i
++) {
1551 // average the scaling part of the transform, apply to radius
1552 var scale1
= transform
.transforms
[i
].m
[0];
1553 var scale2
= transform
.transforms
[i
].m
[3];
1554 r
= r
* ((scale1
+ scale2
) / 2.0);
1558 return ctx
.createRadialGradient(f
.x
, f
.y
, 0, c
.x
, c
.y
, r
);
1561 svg
.Element
.radialGradient
.prototype = new svg
.Element
.GradientBase
;
1563 // gradient stop element
1564 svg
.Element
.stop = function(node
) {
1565 this.base
= svg
.Element
.ElementBase
;
1568 this.offset
= this.attribute('offset').numValue();
1570 var stopColor
= this.style('stop-color');
1571 if (this.style('stop-opacity').hasValue()) stopColor
= stopColor
.Color
.addOpacity(this.style('stop-opacity').value
);
1572 this.color
= stopColor
.value
;
1574 svg
.Element
.stop
.prototype = new svg
.Element
.ElementBase
;
1576 // animation base element
1577 svg
.Element
.AnimateBase = function(node
) {
1578 this.base
= svg
.Element
.ElementBase
;
1581 svg
.Animations
.push(this);
1583 this.duration
= 0.0;
1584 this.begin
= this.attribute('begin').Time
.toMilliseconds();
1585 this.maxDuration
= this.begin
+ this.attribute('dur').Time
.toMilliseconds();
1587 this.getProperty = function() {
1588 var attributeType
= this.attribute('attributeType').value
;
1589 var attributeName
= this.attribute('attributeName').value
;
1591 if (attributeType
== 'CSS') {
1592 return this.parent
.style(attributeName
, true);
1594 return this.parent
.attribute(attributeName
, true);
1597 this.initialValue
= null;
1598 this.removed
= false;
1600 this.calcValue = function() {
1605 this.update = function(delta
) {
1606 // set initial value
1607 if (this.initialValue
== null) {
1608 this.initialValue
= this.getProperty().value
;
1611 // if we're past the end time
1612 if (this.duration
> this.maxDuration
) {
1613 // loop for indefinitely repeating animations
1614 if (this.attribute('repeatCount').value
== 'indefinite') {
1617 else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed
) {
1618 this.removed
= true;
1619 this.getProperty().value
= this.initialValue
;
1623 return false; // no updates made
1626 this.duration
= this.duration
+ delta
;
1628 // if we're past the begin time
1629 var updated
= false;
1630 if (this.begin
< this.duration
) {
1631 var newValue
= this.calcValue(); // tween
1633 if (this.attribute('type').hasValue()) {
1634 // for transform, etc.
1635 var type
= this.attribute('type').value
;
1636 newValue
= type
+ '(' + newValue
+ ')';
1639 this.getProperty().value
= newValue
;
1646 // fraction of duration we've covered
1647 this.progress = function() {
1648 return ((this.duration
- this.begin
) / (this.maxDuration
- this.begin
));
1651 svg
.Element
.AnimateBase
.prototype = new svg
.Element
.ElementBase
;
1654 svg
.Element
.animate = function(node
) {
1655 this.base
= svg
.Element
.AnimateBase
;
1658 this.calcValue = function() {
1659 var from = this.attribute('from').numValue();
1660 var to
= this.attribute('to').numValue();
1662 // tween value linearly
1663 return from + (to
- from) * this.progress();
1666 svg
.Element
.animate
.prototype = new svg
.Element
.AnimateBase
;
1668 // animate color element
1669 svg
.Element
.animateColor = function(node
) {
1670 this.base
= svg
.Element
.AnimateBase
;
1673 this.calcValue = function() {
1674 var from = new RGBColor(this.attribute('from').value
);
1675 var to
= new RGBColor(this.attribute('to').value
);
1677 if (from.ok
&& to
.ok
) {
1678 // tween color linearly
1679 var r
= from.r
+ (to
.r
- from.r
) * this.progress();
1680 var g
= from.g
+ (to
.g
- from.g
) * this.progress();
1681 var b
= from.b
+ (to
.b
- from.b
) * this.progress();
1682 return 'rgb('+parseInt(r
,10)+','+parseInt(g
,10)+','+parseInt(b
,10)+')';
1684 return this.attribute('from').value
;
1687 svg
.Element
.animateColor
.prototype = new svg
.Element
.AnimateBase
;
1689 // animate transform element
1690 svg
.Element
.animateTransform = function(node
) {
1691 this.base
= svg
.Element
.animate
;
1694 svg
.Element
.animateTransform
.prototype = new svg
.Element
.animate
;
1697 svg
.Element
.text = function(node
) {
1698 this.base
= svg
.Element
.RenderedElementBase
;
1704 for (var i
=0; i
<node
.childNodes
.length
; i
++) {
1705 var childNode
= node
.childNodes
[i
];
1706 if (childNode
.nodeType
== 1) { // capture tspan and tref nodes
1707 this.addChild(childNode
, true);
1709 else if (childNode
.nodeType
== 3) { // capture text
1710 this.addChild(new svg
.Element
.tspan(childNode
), false);
1715 this.baseSetContext
= this.setContext
;
1716 this.setContext = function(ctx
) {
1717 this.baseSetContext(ctx
);
1718 if (this.attribute('text-anchor').hasValue()) {
1719 var textAnchor
= this.attribute('text-anchor').value
;
1720 ctx
.textAlign
= textAnchor
== 'middle' ? 'center' : textAnchor
;
1722 if (this.attribute('alignment-baseline').hasValue()) ctx
.textBaseline
= this.attribute('alignment-baseline').value
;
1725 this.renderChildren = function(ctx
) {
1726 if(this.attribute('visibility').value
=='hidden') return;
1728 var x
= this.attribute('x').Length
.toPixels('x');
1729 var y
= this.attribute('y').Length
.toPixels('y');
1731 for (var i
=0; i
<this.children
.length
; i
++) {
1732 var child
= this.children
[i
];
1734 if (child
.attribute('x').hasValue()) {
1735 child
.x
= child
.attribute('x').Length
.toPixels('x');
1738 if (child
.attribute('dx').hasValue()) x
+= child
.attribute('dx').Length
.toPixels('x');
1740 x
+= child
.measureText(ctx
);
1743 if (child
.attribute('y').hasValue()) {
1744 child
.y
= child
.attribute('y').Length
.toPixels('y');
1747 if (child
.attribute('dy').hasValue()) y
+= child
.attribute('dy').Length
.toPixels('y');
1755 svg
.Element
.text
.prototype = new svg
.Element
.RenderedElementBase
;
1758 svg
.Element
.TextElementBase = function(node
) {
1759 this.base
= svg
.Element
.RenderedElementBase
;
1762 this.renderChildren = function(ctx
) {
1763 ctx
.fillText(svg
.compressSpaces(this.getText()), this.x
, this.y
);
1766 this.getText = function() {
1770 this.measureText = function(ctx
) {
1771 var textToMeasure
= svg
.compressSpaces(this.getText());
1772 if (!ctx
.measureText
) return textToMeasure
.length
* 10;
1773 return ctx
.measureText(textToMeasure
).width
;
1776 svg
.Element
.TextElementBase
.prototype = new svg
.Element
.RenderedElementBase
;
1779 svg
.Element
.tspan = function(node
) {
1780 this.base
= svg
.Element
.TextElementBase
;
1784 this.text
= node
.nodeType
== 3 ? node
.nodeValue
: node
.childNodes
[0].nodeValue
;
1785 this.getText = function() {
1789 svg
.Element
.tspan
.prototype = new svg
.Element
.TextElementBase
;
1792 svg
.Element
.tref = function(node
) {
1793 this.base
= svg
.Element
.TextElementBase
;
1796 this.getText = function() {
1797 var element
= this.attribute('xlink:href').Definition
.getDefinition();
1798 if (element
!= null) return element
.children
[0].getText();
1801 svg
.Element
.tref
.prototype = new svg
.Element
.TextElementBase
;
1804 svg
.Element
.a = function(node
) {
1805 this.base
= svg
.Element
.TextElementBase
;
1808 this.hasText
= true;
1809 for (var i
=0; i
<node
.childNodes
.length
; i
++) {
1810 if (node
.childNodes
[i
].nodeType
!= 3) this.hasText
= false;
1813 // this might contain text
1814 this.text
= this.hasText
? node
.childNodes
[0].nodeValue
: '';
1815 this.getText = function() {
1819 this.baseRenderChildren
= this.renderChildren
;
1820 this.renderChildren = function(ctx
) {
1822 // render as text element
1823 this.baseRenderChildren(ctx
);
1824 var fontSize
= new svg
.Property('fontSize', svg
.Font
.Parse(svg
.ctx
.font
).fontSize
);
1825 svg
.Mouse
.checkBoundingBox(this, new svg
.BoundingBox(this.x
, this.y
- fontSize
.Length
.toPixels('y'), this.x
+ this.measureText(ctx
), this.y
));
1828 // render as temporary group
1829 var g
= new svg
.Element
.g();
1830 g
.children
= this.children
;
1836 this.onclick = function() {
1837 window
.open(this.attribute('xlink:href').value
);
1840 this.onmousemove = function() {
1841 svg
.ctx
.canvas
.style
.cursor
= 'pointer';
1844 svg
.Element
.a
.prototype = new svg
.Element
.TextElementBase
;
1847 svg
.Element
.image = function(node
) {
1848 this.base
= svg
.Element
.RenderedElementBase
;
1851 svg
.Images
.push(this);
1852 this.img
= document
.createElement('img');
1853 this.loaded
= false;
1855 this.img
.onload = function() { that
.loaded
= true; }
1856 this.img
.src
= this.attribute('xlink:href').value
;
1858 this.renderChildren = function(ctx
) {
1859 var x
= this.attribute('x').Length
.toPixels('x');
1860 var y
= this.attribute('y').Length
.toPixels('y');
1862 var width
= this.attribute('width').Length
.toPixels('x');
1863 var height
= this.attribute('height').Length
.toPixels('y');
1864 if (width
== 0 || height
== 0) return;
1867 ctx
.translate(x
, y
);
1868 svg
.AspectRatio(ctx
,
1869 this.attribute('preserveAspectRatio').value
,
1876 ctx
.drawImage(this.img
, 0, 0);
1880 svg
.Element
.image
.prototype = new svg
.Element
.RenderedElementBase
;
1883 svg
.Element
.g = function(node
) {
1884 this.base
= svg
.Element
.RenderedElementBase
;
1887 this.getBoundingBox = function() {
1888 var bb
= new svg
.BoundingBox();
1889 for (var i
=0; i
<this.children
.length
; i
++) {
1890 bb
.addBoundingBox(this.children
[i
].getBoundingBox());
1895 svg
.Element
.g
.prototype = new svg
.Element
.RenderedElementBase
;
1898 svg
.Element
.symbol = function(node
) {
1899 this.base
= svg
.Element
.RenderedElementBase
;
1902 this.baseSetContext
= this.setContext
;
1903 this.setContext = function(ctx
) {
1904 this.baseSetContext(ctx
);
1907 if (this.attribute('viewBox').hasValue()) {
1908 var viewBox
= svg
.ToNumberArray(this.attribute('viewBox').value
);
1909 var minX
= viewBox
[0];
1910 var minY
= viewBox
[1];
1912 height
= viewBox
[3];
1914 svg
.AspectRatio(ctx
,
1915 this.attribute('preserveAspectRatio').value
,
1916 this.attribute('width').Length
.toPixels('x'),
1918 this.attribute('height').Length
.toPixels('y'),
1923 svg
.ViewPort
.SetCurrent(viewBox
[2], viewBox
[3]);
1927 svg
.Element
.symbol
.prototype = new svg
.Element
.RenderedElementBase
;
1930 svg
.Element
.style = function(node
) {
1931 this.base
= svg
.Element
.ElementBase
;
1934 var css
= node
.childNodes
[0].nodeValue
;
1935 css
= css
.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(\/\/.*)/gm, ''); // remove comments
1936 css
= svg
.compressSpaces(css
); // replace whitespace
1937 var cssDefs
= css
.split('}');
1938 for (var i
=0; i
<cssDefs
.length
; i
++) {
1939 if (svg
.trim(cssDefs
[i
]) != '') {
1940 var cssDef
= cssDefs
[i
].split('{');
1941 var cssClasses
= cssDef
[0].split(',');
1942 var cssProps
= cssDef
[1].split(';');
1943 for (var j
=0; j
<cssClasses
.length
; j
++) {
1944 var cssClass
= svg
.trim(cssClasses
[j
]);
1945 if (cssClass
!= '') {
1947 for (var k
=0; k
<cssProps
.length
; k
++) {
1948 var prop
= cssProps
[k
].split(':');
1950 var value
= prop
[1];
1951 if (name
!= null && value
!= null) {
1952 props
[svg
.trim(prop
[0])] = new svg
.Property(svg
.trim(prop
[0]), svg
.trim(prop
[1]));
1955 svg
.Styles
[cssClass
] = props
;
1961 svg
.Element
.style
.prototype = new svg
.Element
.ElementBase
;
1964 svg
.Element
.use = function(node
) {
1965 this.base
= svg
.Element
.RenderedElementBase
;
1968 this.baseSetContext
= this.setContext
;
1969 this.setContext = function(ctx
) {
1970 this.baseSetContext(ctx
);
1971 if (this.attribute('x').hasValue()) ctx
.translate(this.attribute('x').Length
.toPixels('x'), 0);
1972 if (this.attribute('y').hasValue()) ctx
.translate(0, this.attribute('y').Length
.toPixels('y'));
1975 this.getDefinition = function() {
1976 var element
= this.attribute('xlink:href').Definition
.getDefinition();
1977 if (this.attribute('width').hasValue()) element
.attribute('width', true).value
= this.attribute('width').value
;
1978 if (this.attribute('height').hasValue()) element
.attribute('height', true).value
= this.attribute('height').value
;
1982 this.path = function(ctx
) {
1983 var element
= this.getDefinition();
1984 if (element
!= null) element
.path(ctx
);
1987 this.renderChildren = function(ctx
) {
1988 var element
= this.getDefinition();
1989 if (element
!= null) element
.render(ctx
);
1992 svg
.Element
.use.prototype = new svg
.Element
.RenderedElementBase
;
1995 svg
.Element
.clipPath = function(node
) {
1996 this.base
= svg
.Element
.ElementBase
;
1999 this.apply = function(ctx
) {
2000 for (var i
=0; i
<this.children
.length
; i
++) {
2001 if (this.children
[i
].path
) {
2002 this.children
[i
].path(ctx
);
2008 svg
.Element
.clipPath
.prototype = new svg
.Element
.ElementBase
;
2010 // title element, do nothing
2011 svg
.Element
.title = function(node
) {
2013 svg
.Element
.title
.prototype = new svg
.Element
.ElementBase
;
2015 // desc element, do nothing
2016 svg
.Element
.desc = function(node
) {
2018 svg
.Element
.desc
.prototype = new svg
.Element
.ElementBase
;
2020 svg
.Element
.MISSING = function(node
) {
2021 console
.log('ERROR: Element \'' + node
.nodeName
+ '\' not yet implemented.');
2023 svg
.Element
.MISSING
.prototype = new svg
.Element
.ElementBase
;
2026 svg
.CreateElement = function(node
) {
2027 var className
= node
.nodeName
.replace(/^[^:]+:/,'');
2029 if (typeof(svg
.Element
[className
]) != 'undefined') {
2030 e
= new svg
.Element
[className
](node
);
2033 e
= new svg
.Element
.MISSING(node
);
2036 e
.type
= node
.nodeName
;
2041 svg
.load = function(ctx
, url
) {
2042 svg
.loadXml(ctx
, svg
.ajax(url
));
2046 svg
.loadXml = function(ctx
, xml
) {
2049 var mapXY = function(p
) {
2052 p
.x
-= e
.offsetLeft
;
2056 if (window
.scrollX
) p
.x
+= window
.scrollX
;
2057 if (window
.scrollY
) p
.y
+= window
.scrollY
;
2062 if (svg
.opts
== null || svg
.opts
['ignoreMouse'] != true) {
2063 ctx
.canvas
.onclick = function(e
) {
2064 var p
= mapXY(new svg
.Point(e
!= null ? e
.clientX
: event
.clientX
, e
!= null ? e
.clientY
: event
.clientY
));
2065 svg
.Mouse
.onclick(p
.x
, p
.y
);
2067 ctx
.canvas
.onmousemove = function(e
) {
2068 var p
= mapXY(new svg
.Point(e
!= null ? e
.clientX
: event
.clientX
, e
!= null ? e
.clientY
: event
.clientY
));
2069 svg
.Mouse
.onmousemove(p
.x
, p
.y
);
2073 var dom
= svg
.parseXml(xml
);
2074 var e
= svg
.CreateElement(dom
.documentElement
);
2077 var isFirstRender
= true;
2078 var draw = function() {
2079 if (svg
.opts
== null || svg
.opts
['ignoreDimensions'] != true) {
2081 if (e
.style('width').hasValue()) {
2082 ctx
.canvas
.width
= e
.style('width').Length
.toPixels(ctx
.canvas
.parentNode
.clientWidth
);
2084 if (e
.style('height').hasValue()) {
2085 ctx
.canvas
.height
= e
.style('height').Length
.toPixels(ctx
.canvas
.parentNode
.clientHeight
);
2088 svg
.ViewPort
.SetCurrent(ctx
.canvas
.clientWidth
, ctx
.canvas
.clientHeight
);
2090 if (svg
.opts
!= null && svg
.opts
['offsetX'] != null) e
.attribute('x', true).value
= svg
.opts
['offsetX'];
2091 if (svg
.opts
!= null && svg
.opts
['offsetY'] != null) e
.attribute('y', true).value
= svg
.opts
['offsetY'];
2092 if (svg
.opts
!= null && svg
.opts
['scaleWidth'] != null && svg
.opts
['scaleHeight'] != null) {
2093 e
.attribute('width', true).value
= svg
.opts
['scaleWidth'];
2094 e
.attribute('height', true).value
= svg
.opts
['scaleHeight'];
2095 e
.attribute('viewBox', true).value
= '0 0 ' + ctx
.canvas
.clientWidth
+ ' ' + ctx
.canvas
.clientHeight
;
2096 e
.attribute('preserveAspectRatio', true).value
= 'none';
2100 if (svg
.opts
== null || svg
.opts
['ignoreClear'] != true) {
2101 ctx
.clearRect(0, 0, ctx
.canvas
.clientWidth
, ctx
.canvas
.clientHeight
);
2104 if (isFirstRender
) {
2105 isFirstRender
= false;
2106 if (svg
.opts
!= null && typeof(svg
.opts
['renderCallback']) == 'function') svg
.opts
['renderCallback']();
2110 var waitingForImages
= true;
2111 if (svg
.ImagesLoaded()) {
2112 waitingForImages
= false;
2115 svg
.intervalID
= setInterval(function() {
2116 var needUpdate
= false;
2118 if (waitingForImages
&& svg
.ImagesLoaded()) {
2119 waitingForImages
= false;
2123 // need update from mouse events?
2124 if (svg
.opts
== null || svg
.opts
['ignoreMouse'] != true) {
2125 needUpdate
= needUpdate
| svg
.Mouse
.hasEvents();
2128 // need update from animations?
2129 if (svg
.opts
== null || svg
.opts
['ignoreAnimation'] != true) {
2130 for (var i
=0; i
<svg
.Animations
.length
; i
++) {
2131 needUpdate
= needUpdate
| svg
.Animations
[i
].update(1000 / svg
.FRAMERATE
);
2135 // need update from redraw?
2136 if (svg
.opts
!= null && typeof(svg
.opts
['forceRedraw']) == 'function') {
2137 if (svg
.opts
['forceRedraw']() == true) needUpdate
= true;
2143 svg
.Mouse
.runEvents(); // run and clear our events
2145 }, 1000 / svg
.FRAMERATE
);
2148 svg
.stop = function() {
2149 if (svg
.intervalID
) {
2150 clearInterval(svg
.intervalID
);
2154 svg
.Mouse
= new (function() {
2156 this.hasEvents = function() { return this.events
.length
!= 0; }
2158 this.onclick = function(x
, y
) {
2159 this.events
.push({ type
: 'onclick', x
: x
, y
: y
,
2160 run: function(e
) { if (e
.onclick
) e
.onclick(); }
2164 this.onmousemove = function(x
, y
) {
2165 this.events
.push({ type
: 'onmousemove', x
: x
, y
: y
,
2166 run: function(e
) { if (e
.onmousemove
) e
.onmousemove(); }
2170 this.eventElements
= [];
2172 this.checkPath = function(element
, ctx
) {
2173 for (var i
=0; i
<this.events
.length
; i
++) {
2174 var e
= this.events
[i
];
2175 if (ctx
.isPointInPath
&& ctx
.isPointInPath(e
.x
, e
.y
)) this.eventElements
[i
] = element
;
2179 this.checkBoundingBox = function(element
, bb
) {
2180 for (var i
=0; i
<this.events
.length
; i
++) {
2181 var e
= this.events
[i
];
2182 if (bb
.isPointInBox(e
.x
, e
.y
)) this.eventElements
[i
] = element
;
2186 this.runEvents = function() {
2187 svg
.ctx
.canvas
.style
.cursor
= '';
2189 for (var i
=0; i
<this.events
.length
; i
++) {
2190 var e
= this.events
[i
];
2191 var element
= this.eventElements
[i
];
2194 element
= element
.parent
;
2198 // done running, clear
2200 this.eventElements
= [];
2208 if (CanvasRenderingContext2D
) {
2209 CanvasRenderingContext2D
.prototype.drawSvg = function(s
, dx
, dy
, dw
, dh
) {
2210 canvg(this.canvas
, s
, {
2212 ignoreAnimation
: true,
2213 ignoreDimensions
: true,
2224 * A class to parse color values
2225 * @author Stoyan Stefanov <sstoo@gmail.com>
2226 * @link http://www.phpied.com/rgb-color-parser-in-javascript/
2227 * @license Use it if you like it
2229 function RGBColor(color_string
)
2233 // strip any leading #
2234 if (color_string
.charAt(0) == '#') { // remove # if any
2235 color_string
= color_string
.substr(1,6);
2238 color_string
= color_string
.replace(/ /g
,'');
2239 color_string
= color_string
.toLowerCase();
2241 // before getting into regexps, try simple matches
2242 // and overwrite the input
2243 var simple_colors
= {
2244 aliceblue
: 'f0f8ff',
2245 antiquewhite
: 'faebd7',
2247 aquamarine
: '7fffd4',
2252 blanchedalmond
: 'ffebcd',
2254 blueviolet
: '8a2be2',
2256 burlywood
: 'deb887',
2257 cadetblue
: '5f9ea0',
2258 chartreuse
: '7fff00',
2259 chocolate
: 'd2691e',
2261 cornflowerblue
: '6495ed',
2267 darkgoldenrod
: 'b8860b',
2269 darkgreen
: '006400',
2270 darkkhaki
: 'bdb76b',
2271 darkmagenta
: '8b008b',
2272 darkolivegreen
: '556b2f',
2273 darkorange
: 'ff8c00',
2274 darkorchid
: '9932cc',
2276 darksalmon
: 'e9967a',
2277 darkseagreen
: '8fbc8f',
2278 darkslateblue
: '483d8b',
2279 darkslategray
: '2f4f4f',
2280 darkturquoise
: '00ced1',
2281 darkviolet
: '9400d3',
2283 deepskyblue
: '00bfff',
2285 dodgerblue
: '1e90ff',
2287 firebrick
: 'b22222',
2288 floralwhite
: 'fffaf0',
2289 forestgreen
: '228b22',
2291 gainsboro
: 'dcdcdc',
2292 ghostwhite
: 'f8f8ff',
2294 goldenrod
: 'daa520',
2297 greenyellow
: 'adff2f',
2300 indianred
: 'cd5c5c',
2305 lavenderblush
: 'fff0f5',
2306 lawngreen
: '7cfc00',
2307 lemonchiffon
: 'fffacd',
2308 lightblue
: 'add8e6',
2309 lightcoral
: 'f08080',
2310 lightcyan
: 'e0ffff',
2311 lightgoldenrodyellow
: 'fafad2',
2312 lightgrey
: 'd3d3d3',
2313 lightgreen
: '90ee90',
2314 lightpink
: 'ffb6c1',
2315 lightsalmon
: 'ffa07a',
2316 lightseagreen
: '20b2aa',
2317 lightskyblue
: '87cefa',
2318 lightslateblue
: '8470ff',
2319 lightslategray
: '778899',
2320 lightsteelblue
: 'b0c4de',
2321 lightyellow
: 'ffffe0',
2323 limegreen
: '32cd32',
2327 mediumaquamarine
: '66cdaa',
2328 mediumblue
: '0000cd',
2329 mediumorchid
: 'ba55d3',
2330 mediumpurple
: '9370d8',
2331 mediumseagreen
: '3cb371',
2332 mediumslateblue
: '7b68ee',
2333 mediumspringgreen
: '00fa9a',
2334 mediumturquoise
: '48d1cc',
2335 mediumvioletred
: 'c71585',
2336 midnightblue
: '191970',
2337 mintcream
: 'f5fffa',
2338 mistyrose
: 'ffe4e1',
2340 navajowhite
: 'ffdead',
2344 olivedrab
: '6b8e23',
2346 orangered
: 'ff4500',
2348 palegoldenrod
: 'eee8aa',
2349 palegreen
: '98fb98',
2350 paleturquoise
: 'afeeee',
2351 palevioletred
: 'd87093',
2352 papayawhip
: 'ffefd5',
2353 peachpuff
: 'ffdab9',
2357 powderblue
: 'b0e0e6',
2360 rosybrown
: 'bc8f8f',
2361 royalblue
: '4169e1',
2362 saddlebrown
: '8b4513',
2364 sandybrown
: 'f4a460',
2370 slateblue
: '6a5acd',
2371 slategray
: '708090',
2373 springgreen
: '00ff7f',
2374 steelblue
: '4682b4',
2379 turquoise
: '40e0d0',
2381 violetred
: 'd02090',
2384 whitesmoke
: 'f5f5f5',
2386 yellowgreen
: '9acd32'
2388 for (var key
in simple_colors
) {
2389 if (color_string
== key
) {
2390 color_string
= simple_colors
[key
];
2393 // emd of simple type-in colors
2395 // array of color definition objects
2398 re
: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
2399 example
: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
2400 process: function (bits
){
2409 re
: /^(\w{2})(\w{2})(\w{2})$/,
2410 example
: ['#00ff00', '336699'],
2411 process: function (bits
){
2413 parseInt(bits
[1], 16),
2414 parseInt(bits
[2], 16),
2415 parseInt(bits
[3], 16)
2420 re
: /^(\w{1})(\w{1})(\w{1})$/,
2421 example
: ['#fb0', 'f0f'],
2422 process: function (bits
){
2424 parseInt(bits
[1] + bits
[1], 16),
2425 parseInt(bits
[2] + bits
[2], 16),
2426 parseInt(bits
[3] + bits
[3], 16)
2432 // search through the definitions to find a match
2433 for (var i
= 0; i
< color_defs
.length
; i
++) {
2434 var re
= color_defs
[i
].re
;
2435 var processor
= color_defs
[i
].process
;
2436 var bits
= re
.exec(color_string
);
2438 channels
= processor(bits
);
2439 this.r
= channels
[0];
2440 this.g
= channels
[1];
2441 this.b
= channels
[2];
2447 // validate/cleanup values
2448 this.r
= (this.r
< 0 || isNaN(this.r
)) ? 0 : ((this.r
> 255) ? 255 : this.r
);
2449 this.g
= (this.g
< 0 || isNaN(this.g
)) ? 0 : ((this.g
> 255) ? 255 : this.g
);
2450 this.b
= (this.b
< 0 || isNaN(this.b
)) ? 0 : ((this.b
> 255) ? 255 : this.b
);
2453 this.toRGB = function () {
2454 return 'rgb(' + this.r
+ ', ' + this.g
+ ', ' + this.b
+ ')';
2456 this.toHex = function () {
2457 var r
= this.r
.toString(16);
2458 var g
= this.g
.toString(16);
2459 var b
= this.b
.toString(16);
2460 if (r
.length
== 1) r
= '0' + r
;
2461 if (g
.length
== 1) g
= '0' + g
;
2462 if (b
.length
== 1) b
= '0' + b
;
2463 return '#' + r
+ g
+ b
;
2467 this.getHelpXML = function () {
2469 var examples
= new Array();
2471 for (var i
= 0; i
< color_defs
.length
; i
++) {
2472 var example
= color_defs
[i
].example
;
2473 for (var j
= 0; j
< example
.length
; j
++) {
2474 examples
[examples
.length
] = example
[j
];
2477 // add type-in colors
2478 for (var sc
in simple_colors
) {
2479 examples
[examples
.length
] = sc
;
2482 var xml
= document
.createElement('ul');
2483 xml
.setAttribute('id', 'rgbcolor-examples');
2484 for (var i
= 0; i
< examples
.length
; i
++) {
2486 var list_item
= document
.createElement('li');
2487 var list_color
= new RGBColor(examples
[i
]);
2488 var example_div
= document
.createElement('div');
2489 example_div
.style
.cssText
=
2491 + 'border: 1px solid black; '
2492 + 'background:' + list_color
.toHex() + '; '
2493 + 'color:' + list_color
.toHex()
2495 example_div
.appendChild(document
.createTextNode('test'));
2496 var list_item_value
= document
.createTextNode(
2497 ' ' + examples
[i
] + ' -> ' + list_color
.toRGB() + ' -> ' + list_color
.toHex()
2499 list_item
.appendChild(example_div
);
2500 list_item
.appendChild(list_item_value
);
2501 xml
.appendChild(list_item
);