Вход Регистрация
Файл: protected/extensions/widgets/highcharts/assets/modules/canvas-tools.src.js
Строк: 3628
<?php
/**
 * @license A class to parse color values
 * @author Stoyan Stefanov <sstoo@gmail.com>
 * @link   http://www.phpied.com/rgb-color-parser-in-javascript/
 * Use it if you like it
 *
 */
function RGBColor(color_string)
{
    
this.ok false;

    
// strip any leading #
    
if (color_string.charAt(0) == '#') { // remove # if any
        
color_string color_string.substr(1,6);
    }

    
color_string color_string.replace(/ /g,'');
    
color_string color_string.toLowerCase();

    
// before getting into regexps, try simple matches
    // and overwrite the input
    
var simple_colors = {
        
aliceblue'f0f8ff',
        
antiquewhite'faebd7',
        
aqua'00ffff',
        
aquamarine'7fffd4',
        
azure'f0ffff',
        
beige'f5f5dc',
        
bisque'ffe4c4',
        
black'000000',
        
blanchedalmond'ffebcd',
        
blue'0000ff',
        
blueviolet'8a2be2',
        
brown'a52a2a',
        
burlywood'deb887',
        
cadetblue'5f9ea0',
        
chartreuse'7fff00',
        
chocolate'd2691e',
        
coral'ff7f50',
        
cornflowerblue'6495ed',
        
cornsilk'fff8dc',
        
crimson'dc143c',
        
cyan'00ffff',
        
darkblue'00008b',
        
darkcyan'008b8b',
        
darkgoldenrod'b8860b',
        
darkgray'a9a9a9',
        
darkgreen'006400',
        
darkkhaki'bdb76b',
        
darkmagenta'8b008b',
        
darkolivegreen'556b2f',
        
darkorange'ff8c00',
        
darkorchid'9932cc',
        
darkred'8b0000',
        
darksalmon'e9967a',
        
darkseagreen'8fbc8f',
        
darkslateblue'483d8b',
        
darkslategray'2f4f4f',
        
darkturquoise'00ced1',
        
darkviolet'9400d3',
        
deeppink'ff1493',
        
deepskyblue'00bfff',
        
dimgray'696969',
        
dodgerblue'1e90ff',
        
feldspar'd19275',
        
firebrick'b22222',
        
floralwhite'fffaf0',
        
forestgreen'228b22',
        
fuchsia'ff00ff',
        
gainsboro'dcdcdc',
        
ghostwhite'f8f8ff',
        
gold'ffd700',
        
goldenrod'daa520',
        
gray'808080',
        
green'008000',
        
greenyellow'adff2f',
        
honeydew'f0fff0',
        
hotpink'ff69b4',
        
indianred 'cd5c5c',
        
indigo '4b0082',
        
ivory'fffff0',
        
khaki'f0e68c',
        
lavender'e6e6fa',
        
lavenderblush'fff0f5',
        
lawngreen'7cfc00',
        
lemonchiffon'fffacd',
        
lightblue'add8e6',
        
lightcoral'f08080',
        
lightcyan'e0ffff',
        
lightgoldenrodyellow'fafad2',
        
lightgrey'd3d3d3',
        
lightgreen'90ee90',
        
lightpink'ffb6c1',
        
lightsalmon'ffa07a',
        
lightseagreen'20b2aa',
        
lightskyblue'87cefa',
        
lightslateblue'8470ff',
        
lightslategray'778899',
        
lightsteelblue'b0c4de',
        
lightyellow'ffffe0',
        
lime'00ff00',
        
limegreen'32cd32',
        
linen'faf0e6',
        
magenta'ff00ff',
        
maroon'800000',
        
mediumaquamarine'66cdaa',
        
mediumblue'0000cd',
        
mediumorchid'ba55d3',
        
mediumpurple'9370d8',
        
mediumseagreen'3cb371',
        
mediumslateblue'7b68ee',
        
mediumspringgreen'00fa9a',
        
mediumturquoise'48d1cc',
        
mediumvioletred'c71585',
        
midnightblue'191970',
        
mintcream'f5fffa',
        
mistyrose'ffe4e1',
        
moccasin'ffe4b5',
        
navajowhite'ffdead',
        
navy'000080',
        
oldlace'fdf5e6',
        
olive'808000',
        
olivedrab'6b8e23',
        
orange'ffa500',
        
orangered'ff4500',
        
orchid'da70d6',
        
palegoldenrod'eee8aa',
        
palegreen'98fb98',
        
paleturquoise'afeeee',
        
palevioletred'd87093',
        
papayawhip'ffefd5',
        
peachpuff'ffdab9',
        
peru'cd853f',
        
pink'ffc0cb',
        
plum'dda0dd',
        
powderblue'b0e0e6',
        
purple'800080',
        
red'ff0000',
        
rosybrown'bc8f8f',
        
royalblue'4169e1',
        
saddlebrown'8b4513',
        
salmon'fa8072',
        
sandybrown'f4a460',
        
seagreen'2e8b57',
        
seashell'fff5ee',
        
sienna'a0522d',
        
silver'c0c0c0',
        
skyblue'87ceeb',
        
slateblue'6a5acd',
        
slategray'708090',
        
snow'fffafa',
        
springgreen'00ff7f',
        
steelblue'4682b4',
        
tan'd2b48c',
        
teal'008080',
        
thistle'd8bfd8',
        
tomato'ff6347',
        
turquoise'40e0d0',
        
violet'ee82ee',
        
violetred'd02090',
        
wheat'f5deb3',
        
white'ffffff',
        
whitesmoke'f5f5f5',
        
yellow'ffff00',
        
yellowgreen'9acd32'
    
};
    for (var 
key in simple_colors) {
        if (
color_string == key) {
            
color_string simple_colors[key];
        }
    }
    
// emd of simple type-in colors

    // array of color definition objects
    
var color_defs = [
        {
            
re: /^rgb((d{1,3}),s*(d{1,3}),s*(d{1,3}))$/,
            
example: ['rgb(123, 234, 45)''rgb(255,234,245)'],
            
process: function (bits){
                return [
                    
parseInt(bits[1]),
                    
parseInt(bits[2]),
                    
parseInt(bits[3])
                ];
            }
        },
        {
            
re: /^(w{2})(w{2})(w{2})$/,
            
example: ['#00ff00''336699'],
            
process: function (bits){
                return [
                    
parseInt(bits[1], 16),
                    
parseInt(bits[2], 16),
                    
parseInt(bits[3], 16)
                ];
            }
        },
        {
            
re: /^(w{1})(w{1})(w{1})$/,
            
example: ['#fb0''f0f'],
            
process: function (bits){
                return [
                    
parseInt(bits[1] + bits[1], 16),
                    
parseInt(bits[2] + bits[2], 16),
                    
parseInt(bits[3] + bits[3], 16)
                ];
            }
        }
    ];

    
// search through the definitions to find a match
    
for (var 0color_defs.lengthi++) {
        var 
re color_defs[i].re;
        var 
processor color_defs[i].process;
        var 
bits re.exec(color_string);
        if (
bits) {
            
channels processor(bits);
            
this.channels[0];
            
this.channels[1];
            
this.channels[2];
            
this.ok true;
        }

    }

    
// validate/cleanup values
    
this.= (this.|| isNaN(this.r)) ? : ((this.255) ? 255 this.r);
    
this.= (this.|| isNaN(this.g)) ? : ((this.255) ? 255 this.g);
    
this.= (this.|| isNaN(this.b)) ? : ((this.255) ? 255 this.b);

    
// some getters
    
this.toRGB = function () {
        return 
'rgb(' this.', ' this.', ' this.')';
    }
    
this.toHex = function () {
        var 
this.r.toString(16);
        var 
this.g.toString(16);
        var 
this.b.toString(16);
        if (
r.length == 1'0' r;
        if (
g.length == 1'0' g;
        if (
b.length == 1'0' b;
        return 
'#' b;
    }

    
// help
    
this.getHelpXML = function () {

        var 
examples = new Array();
        
// add regexps
        
for (var 0color_defs.lengthi++) {
            var 
example color_defs[i].example;
            for (var 
0example.lengthj++) {
                
examples[examples.length] = example[j];
            }
        }
        
// add type-in colors
        
for (var sc in simple_colors) {
            
examples[examples.length] = sc;
        }

        var 
xml document.createElement('ul');
        
xml.setAttribute('id''rgbcolor-examples');
        for (var 
0examples.lengthi++) {
            try {
                var 
list_item document.createElement('li');
                var 
list_color = new RGBColor(examples[i]);
                var 
example_div document.createElement('div');
                
example_div.style.cssText =
                        
'margin: 3px; '
                        
'border: 1px solid black; '
                        
'background:' list_color.toHex() + '; '
                        
'color:' list_color.toHex()
                ;
                
example_div.appendChild(document.createTextNode('test'));
                var 
list_item_value document.createTextNode(
                    
' ' examples[i] + ' -> ' list_color.toRGB() + ' -> ' list_color.toHex()
                );
                
list_item.appendChild(example_div);
                
list_item.appendChild(list_item_value);
                
xml.appendChild(list_item);

            } catch(
e){}
        }
        return 
xml;

    }

}

/**
 * @license canvg.js - Javascript SVG parser and renderer on Canvas
 * MIT Licensed 
 * Gabe Lerner (gabelerner@gmail.com)
 * http://code.google.com/p/canvg/
 *
 * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
 *
 */
if(!window.console) {
    
window.console = {};
    
window.console.log = function(str) {};
    
window.console.dir = function(str) {};
}

if(!Array.
prototype.indexOf){
    Array.
prototype.indexOf = function(obj){
        for(var 
i=0i<this.lengthi++){
            if(
this[i]==obj){
                return 
i;
            }
        }
        return -
1;
    }
}

(function(){
    
// canvg(target, s)
    // empty parameters: replace all 'svg' elements on page with 'canvas' elements
    // target: canvas element or the id of a canvas element
    // s: svg string, url to svg file, or xml document
    // opts: optional hash of options
    //         ignoreMouse: true => ignore mouse events
    //         ignoreAnimation: true => ignore animations
    //         ignoreDimensions: true => does not try to resize canvas
    //         ignoreClear: true => does not clear canvas
    //         offsetX: int => draws at a x offset
    //         offsetY: int => draws at a y offset
    //         scaleWidth: int => scales horizontally to width
    //         scaleHeight: int => scales vertically to height
    //         renderCallback: function => will call the function after the first render is completed
    //         forceRedraw: function => will call the function on every frame, if it returns true, will redraw
    
this.canvg = function (targetsopts) {
        
// no parameters
        
if (target == null && == null && opts == null) {
            var 
svgTags document.getElementsByTagName('svg');
            for (var 
i=0i<svgTags.lengthi++) {
                var 
svgTag svgTags[i];
                var 
document.createElement('canvas');
                
c.width svgTag.clientWidth;
                
c.height svgTag.clientHeight;
                
svgTag.parentNode.insertBefore(csvgTag);
                
svgTag.parentNode.removeChild(svgTag);
                var 
div document.createElement('div');
                
div.appendChild(svgTag);
                
canvg(cdiv.innerHTML);
            }
            return;
        }    
        
opts opts || {};
    
        if (
typeof target == 'string') {
            
target document.getElementById(target);
        }
        
        
// reuse class per canvas
        
var svg;
        if (
target.svg == null) {
            
svg build();
            
target.svg svg;
        }
        else {
            
svg target.svg;
            
svg.stop();
        }
        
svg.opts opts;
        
        var 
ctx target.getContext('2d');
        if (
typeof(s.documentElement) != 'undefined') {
            
// load from xml doc
            
svg.loadXmlDoc(ctxs);
        }
        else if (
s.substr(0,1) == '<') {
            
// load from xml string
            
svg.loadXml(ctxs);
        }
        else {
            
// load from url
            
svg.load(ctxs);
        }
    }

    function 
build() {
        var 
svg = { };
        
        
svg.FRAMERATE 30;
        
svg.MAX_VIRTUAL_PIXELS 30000;
        
        
// globals
        
svg.init = function(ctx) {
            
svg.Definitions = {};
            
svg.Styles = {};
            
svg.Animations = [];
            
svg.Images = [];
            
svg.ctx ctx;
            
svg.ViewPort = new (function () {
                
this.viewPorts = [];
                
this.Clear = function() { this.viewPorts = []; }
                
this.SetCurrent = function(widthheight) { this.viewPorts.push({ widthwidthheightheight }); }
                
this.RemoveCurrent = function() { this.viewPorts.pop(); }
                
this.Current = function() { return this.viewPorts[this.viewPorts.length 1]; }
                
this.width = function() { return this.Current().width; }
                
this.height = function() { return this.Current().height; }
                
this.ComputeSize = function(d) {
                    if (
!= null && typeof(d) == 'number') return d;
                    if (
== 'x') return this.width();
                    if (
== 'y') return this.height();
                    return 
Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);            
                }
            });
        }
        
svg.init();
        
        
// images loaded
        
svg.ImagesLoaded = function() { 
            for (var 
i=0i<svg.Images.lengthi++) {
                if (!
svg.Images[i].loaded) return false;
            }
            return 
true;
        }

        
// trim
        
svg.trim = function(s) { return s.replace(/^s+|s+$/g''); }
        
        
// compress spaces
        
svg.compressSpaces = function(s) { return s.replace(/[srtn]+/gm,' '); }
        
        
// ajax
        
svg.ajax = function(url) {
            var 
AJAX;
            if(
window.XMLHttpRequest){AJAX=new XMLHttpRequest();}
            else{
AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
            if(
AJAX){
               
AJAX.open('GET',url,false);
               
AJAX.send(null);
               return 
AJAX.responseText;
            }
            return 
null;
        } 
        
        
// parse xml
        
svg.parseXml = function(xml) {
            if (
window.DOMParser)
            {
                var 
parser = new DOMParser();
                return 
parser.parseFromString(xml'text/xml');
            }
            else 
            {
                
xml xml.replace(/<!DOCTYPE svg[^>]*>/, '');
                var 
xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
                
xmlDoc.async 'false';
                
xmlDoc.loadXML(xml); 
                return 
xmlDoc;
            }        
        }
        
        
svg.Property = function(namevalue) {
            
this.name name;
            
this.value value;
            
            
this.hasValue = function() {
                return (
this.value != null && this.value !== '');
            }
                            
            
// return the numerical value of the property
            
this.numValue = function() {
                if (!
this.hasValue()) return 0;
                
                var 
parseFloat(this.value);
                if ((
this.value '').match(/%$/)) {
                    
100.0;
                }
                return 
n;
            }
            
            
this.valueOrDefault = function(def) {
                if (
this.hasValue()) return this.value;
                return 
def;
            }
            
            
this.numValueOrDefault = function(def) {
                if (
this.hasValue()) return this.numValue();
                return 
def;
            }
            
            
/* EXTENSIONS */
            
var that this;
            
            
// color extensions
            
this.Color = {
                
// augment the current color value with the opacity
                
addOpacity: function(opacity) {
                    var 
newValue that.value;
                    if (
opacity != null && opacity != '') {
                        var 
color = new RGBColor(that.value);
                        if (
color.ok) {
                            
newValue 'rgba(' color.', ' color.', ' color.', ' opacity ')';
                        }
                    }
                    return new 
svg.Property(that.namenewValue);
                }
            }
            
            
// definition extensions
            
this.Definition = {
                
// get the definition from the definitions table
                
getDefinition: function() {
                    var 
name that.value.replace(/^(url()?#([^)]+))?$/, '$2');
                    
return svg.Definitions[name];
                },
                
                
isUrl: function() {
                    return 
that.value.indexOf('url(') == 0
                
},
                
                
getFillStyle: function(e) {
                    var 
def this.getDefinition();
                    
                    
// gradient
                    
if (def != null && def.createGradient) {
                        return 
def.createGradient(svg.ctxe);
                    }
                    
                    
// pattern
                    
if (def != null && def.createPattern) {
                        return 
def.createPattern(svg.ctxe);
                    }
                    
                    return 
null;
                }
            }
            
            
// length extensions
            
this.Length = {
                
DPI: function(viewPort) {
                    return 
96.0// TODO: compute?
                
},
                
                
EM: function(viewPort) {
                    var 
em 12;
                    
                    var 
fontSize = new svg.Property('fontSize'svg.Font.Parse(svg.ctx.font).fontSize);
                    if (
fontSize.hasValue()) em fontSize.Length.toPixels(viewPort);
                    
                    return 
em;
                },
            
                
// get the length as pixels
                
toPixels: function(viewPort) {
                    if (!
that.hasValue()) return 0;
                    var 
that.value+'';
                    if (
s.match(/em$/)) return that.numValue() * this.EM(viewPort);
                    if (
s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0;
                    if (
s.match(/px$/)) return that.numValue();
                    if (
s.match(/pt$/)) return that.numValue() * 1.25;
                    if (
s.match(/pc$/)) return that.numValue() * 15;
                    if (
s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54;
                    if (
s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4;
                    if (
s.match(/in$/)) return that.numValue() * this.DPI(viewPort);
                    if (
s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort);
                    return 
that.numValue();
                }
            }
            
            
// time extensions
            
this.Time = {
                
// get the time as milliseconds
                
toMilliseconds: function() {
                    if (!
that.hasValue()) return 0;
                    var 
that.value+'';
                    if (
s.match(/s$/)) return that.numValue() * 1000;
                    if (
s.match(/ms$/)) return that.numValue();
                    return 
that.numValue();
                }
            }
            
            
// angle extensions
            
this.Angle = {
                
// get the angle as radians
                
toRadians: function() {
                    if (!
that.hasValue()) return 0;
                    var 
that.value+'';
                    if (
s.match(/deg$/)) return that.numValue() * (Math.PI 180.0);
                    if (
s.match(/grad$/)) return that.numValue() * (Math.PI 200.0);
                    if (
s.match(/rad$/)) return that.numValue();
                    return 
that.numValue() * (Math.PI 180.0);
                }
            }
        }
        
        
// fonts
        
svg.Font = new (function() {
            
this.Styles = ['normal','italic','oblique','inherit'];
            
this.Variants = ['normal','small-caps','inherit'];
            
this.Weights = ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit'];
            
            
this.CreateFont = function(fontStylefontVariantfontWeightfontSizefontFamilyinherit) { 
                var 
inherit != null this.Parse(inherit) : this.CreateFont(''''''''''svg.ctx.font);
                return { 
                    
fontFamilyfontFamily || f.fontFamily
                    
fontSizefontSize || f.fontSize
                    
fontStylefontStyle || f.fontStyle
                    
fontWeightfontWeight || f.fontWeight
                    
fontVariantfontVariant || f.fontVariant,
                    
toString: function () { return [this.fontStylethis.fontVariantthis.fontWeightthis.fontSizethis.fontFamily].join(' ') } 
                } 
            }
            
            var 
that this;
            
this.Parse = function(s) {
                var 
= {};
                var 
svg.trim(svg.compressSpaces(|| '')).split(' ');
                var 
set = { fontSizefalsefontStylefalsefontWeightfalsefontVariantfalse }
                var 
ff '';
                for (var 
i=0i<d.lengthi++) {
                    if (!
set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit'f.fontStyle d[i]; set.fontStyle true; }
                    else if (!
set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit'f.fontVariant d[i]; set.fontStyle set.fontVariant true;    }
                    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; }
                    else if (!
set.fontSize) { if (d[i] != 'inherit'f.fontSize d[i].split('/')[0]; set.fontStyle set.fontVariant set.fontWeight set.fontSize true; }
                    else { if (
d[i] != 'inherit'ff += d[i]; }
                } if (
ff != ''f.fontFamily ff;
                return 
f;
            }
        });
        
        
// points and paths
        
svg.ToNumberArray = function(s) {
            var 
svg.trim(svg.compressSpaces((|| '').replace(/,/g' '))).split(' ');
            for (var 
i=0i<a.lengthi++) {
                
a[i] = parseFloat(a[i]);
            }
            return 
a;
        }        
        
svg.Point = function(xy) {
            
this.x;
            
this.y;
            
            
this.angleTo = function(p) {
                return 
Math.atan2(p.this.yp.this.x);
            }
            
            
this.applyTransform = function(v) {
                var 
xp this.v[0] + this.v[2] + v[4];
                var 
yp this.v[1] + this.v[3] + v[5];
                
this.xp;
                
this.yp;
            }
        }
        
svg.CreatePoint = function(s) {
            var 
svg.ToNumberArray(s);
            return new 
svg.Point(a[0], a[1]);
        }
        
svg.CreatePath = function(s) {
            var 
svg.ToNumberArray(s);
            var 
path = [];
            for (var 
i=0i<a.lengthi+=2) {
                
path.push(new svg.Point(a[i], a[i+1]));
            }
            return 
path;
        }
        
        
// bounding box
        
svg.BoundingBox = function(x1y1x2y2) { // pass in initial points if you want
            
this.x1 Number.NaN;
            
this.y1 Number.NaN;
            
this.x2 Number.NaN;
            
this.y2 Number.NaN;
            
            
this.= function() { return this.x1; }
            
this.= function() { return this.y1; }
            
this.width = function() { return this.x2 this.x1; }
            
this.height = function() { return this.y2 this.y1; }
            
            
this.addPoint = function(xy) {    
                if (
!= null) {
                    if (
isNaN(this.x1) || isNaN(this.x2)) {
                        
this.x1 x;
                        
this.x2 x;
                    }
                    if (
this.x1this.x1 x;
                    if (
this.x2this.x2 x;
                }
            
                if (
!= null) {
                    if (
isNaN(this.y1) || isNaN(this.y2)) {
                        
this.y1 y;
                        
this.y2 y;
                    }
                    if (
this.y1this.y1 y;
                    if (
this.y2this.y2 y;
                }
            }            
            
this.addX = function(x) { this.addPoint(xnull); }
            
this.addY = function(y) { this.addPoint(nully); }
            
            
this.addBoundingBox = function(bb) {
                
this.addPoint(bb.x1bb.y1);
                
this.addPoint(bb.x2bb.y2);
            }
            
            
this.addQuadraticCurve = function(p0xp0yp1xp1yp2xp2y) {
                var 
cp1x p0x 2/* (p1x p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
                
var cp1y p0y 2/* (p1y p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
                
var cp2x cp1x 1/* (p2x p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
                
var cp2y cp1y 1/* (p2y p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
                
this.addBezierCurve(p0xp0ycp1xcp2xcp1y,    cp2yp2xp2y);
            }
            
            
this.addBezierCurve = function(p0xp0yp1xp1yp2xp2yp3xp3y) {
                
// from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
                
var p0 = [p0xp0y], p1 = [p1xp1y], p2 = [p2xp2y], p3 = [p3xp3y];
                
this.addPoint(p0[0], p0[1]);
                
this.addPoint(p3[0], p3[1]);
                
                for (
i=0i<=1i++) {
                    var 
= function(t) { 
                        return 
Math.pow(1-t3) * p0[i]
                        + 
Math.pow(1-t2) * p1[i]
                        + 
* (1-t) * Math.pow(t2) * p2[i]
                        + 
Math.pow(t3) * p3[i];
                    }
                    
                    var 
p0[i] - 12 p1[i] + p2[i];
                    var 
= -p0[i] + p1[i] - p2[i] + p3[i];
                    var 
p1[i] - p0[i];
                    
                    if (
== 0) {
                        if (
== 0) continue;
                        var 
= -b;
                        if (
&& 1) {
                            if (
== 0this.addX(f(t));
                            if (
== 1this.addY(f(t));
                        }
                        continue;
                    }
                    
                    var 
b2ac Math.pow(b2) - a;
                    if (
b2ac 0) continue;
                    var 
t1 = (-Math.sqrt(b2ac)) / (a);
                    if (
t1 && t1 1) {
                        if (
== 0this.addX(f(t1));
                        if (
== 1this.addY(f(t1));
                    }
                    var 
t2 = (-Math.sqrt(b2ac)) / (a);
                    if (
t2 && t2 1) {
                        if (
== 0this.addX(f(t2));
                        if (
== 1this.addY(f(t2));
                    }
                }
            }
            
            
this.isPointInBox = function(xy) {
                return (
this.x1 <= && <= this.x2 && this.y1 <= && <= this.y2);
            }
            
            
this.addPoint(x1y1);
            
this.addPoint(x2y2);
        }
        
        
// transforms
        
svg.Transform = function(v) {    
            var 
that this;
            
this.Type = {}
        
            
// translate
            
this.Type.translate = function(s) {
                
this.svg.CreatePoint(s);            
                
this.apply = function(ctx) {
                    
ctx.translate(this.p.|| 0.0this.p.|| 0.0);
                }
                
this.applyToPoint = function(p) {
                    
p.applyTransform([1001this.p.|| 0.0this.p.|| 0.0]);
                }
            }
            
            
// rotate
            
this.Type.rotate = function(s) {
                var 
svg.ToNumberArray(s);
                
this.angle = new svg.Property('angle'a[0]);
                
this.cx a[1] || 0;
                
this.cy a[2] || 0;
                
this.apply = function(ctx) {
                    
ctx.translate(this.cxthis.cy);
                    
ctx.rotate(this.angle.Angle.toRadians());
                    
ctx.translate(-this.cx, -this.cy);
                }
                
this.applyToPoint = function(p) {
                    var 
this.angle.Angle.toRadians();
                    
p.applyTransform([1001this.p.|| 0.0this.p.|| 0.0]);
                    
p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 00]);
                    
p.applyTransform([1001, -this.p.|| 0.0, -this.p.|| 0.0]);
                }            
            }
            
            
this.Type.scale = function(s) {
                
this.svg.CreatePoint(s);
                
this.apply = function(ctx) {
                    
ctx.scale(this.p.|| 1.0this.p.|| this.p.|| 1.0);
                }
                
this.applyToPoint = function(p) {
                    
p.applyTransform([this.p.|| 0.000this.p.|| 0.000]);
                }                
            }
            
            
this.Type.matrix = function(s) {
                
this.svg.ToNumberArray(s);
                
this.apply = function(ctx) {
                    
ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
                }
                
this.applyToPoint = function(p) {
                    
p.applyTransform(this.m);
                }                    
            }
            
            
this.Type.SkewBase = function(s) {
                
this.base that.Type.matrix;
                
this.base(s);
                
this.angle = new svg.Property('angle's);
            }
            
this.Type.SkewBase.prototype = new this.Type.matrix;
            
            
this.Type.skewX = function(s) {
                
this.base that.Type.SkewBase;
                
this.base(s);
                
this.= [10Math.tan(this.angle.Angle.toRadians()), 100];
            }
            
this.Type.skewX.prototype = new this.Type.SkewBase;
            
            
this.Type.skewY = function(s) {
                
this.base that.Type.SkewBase;
                
this.base(s);
                
this.= [1Math.tan(this.angle.Angle.toRadians()), 0100];
            }
            
this.Type.skewY.prototype = new this.Type.SkewBase;
        
            
this.transforms = [];
            
            
this.apply = function(ctx) {
                for (var 
i=0i<this.transforms.lengthi++) {
                    
this.transforms[i].apply(ctx);
                }
            }
            
            
this.applyToPoint = function(p) {
                for (var 
i=0i<this.transforms.lengthi++) {
                    
this.transforms[i].applyToPoint(p);
                }
            }
            
            var 
data svg.trim(svg.compressSpaces(v)).split(/s(?=[a-z])/);
            for (var 
i=0i<data.lengthi++) {
                var 
type data[i].split('(')[0];
                var 
data[i].split('(')[1].replace(')','');
                var 
transform = new this.Type[type](s);
                
this.transforms.push(transform);
            }
        }
        
        
// aspect ratio
        
svg.AspectRatio = function(ctxaspectRatiowidthdesiredWidthheightdesiredHeightminXminYrefXrefY) {
            
// aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
            
aspectRatio svg.compressSpaces(aspectRatio);
            
aspectRatio aspectRatio.replace(/^defers/,''); // ignore defer
            
var align aspectRatio.split(' ')[0] || 'xMidYMid';
            var 
meetOrSlice aspectRatio.split(' ')[1] || 'meet';                    
    
            
// calculate scale
            
var scaleX width desiredWidth;
            var 
scaleY height desiredHeight;
            var 
scaleMin Math.min(scaleXscaleY);
            var 
scaleMax Math.max(scaleXscaleY);
            if (
meetOrSlice == 'meet') { desiredWidth *= scaleMindesiredHeight *= scaleMin; }
            if (
meetOrSlice == 'slice') { desiredWidth *= scaleMaxdesiredHeight *= scaleMax; }    
            
            
refX = new svg.Property('refX'refX);
            
refY = new svg.Property('refY'refY);
            if (
refX.hasValue() && refY.hasValue()) {                
                
ctx.translate(-scaleMin refX.Length.toPixels('x'), -scaleMin refY.Length.toPixels('y'));
            } 
            else {                    
                
// align
                
if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width 2.0 desiredWidth 2.00); 
                if (
align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0height 2.0 desiredHeight 2.0); 
                if (
align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width desiredWidth0); 
                if (
align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0height desiredHeight); 
            }
            
            
// scale
            
if (align == 'none'ctx.scale(scaleXscaleY);
            else if (
meetOrSlice == 'meet'ctx.scale(scaleMinscaleMin); 
            else if (
meetOrSlice == 'slice'ctx.scale(scaleMaxscaleMax);     
            
            
// translate
            
ctx.translate(minX == null : -minXminY == null : -minY);            
        }
        
        
// elements
        
svg.Element = {}
        
        
svg.Element.ElementBase = function(node) {    
            
this.attributes = {};
            
this.styles = {};
            
this.children = [];
            
            
// get or create attribute
            
this.attribute = function(namecreateIfNotExists) {
                var 
this.attributes[name];
                if (
!= null) return a;
                            
                
= new svg.Property(name'');
                if (
createIfNotExists == truethis.attributes[name] = a;
                return 
a;
            }
            
            
// get or create style, crawls up node tree
            
this.style = function(namecreateIfNotExists) {
                var 
this.styles[name];
                if (
!= null) return s;
                
                var 
this.attribute(name);
                if (
!= null && a.hasValue()) {
                    return 
a;
                }
                
                var 
this.parent;
                if (
!= null) {
                    var 
ps p.style(name);
                    if (
ps != null && ps.hasValue()) {
                        return 
ps;
                    }
                }
                    
                
= new svg.Property(name'');
                if (
createIfNotExists == truethis.styles[name] = s;
                return 
s;
            }
            
            
// base render
            
this.render = function(ctx) {
                
// don't render display=none
                
if (this.style('display').value == 'none') return;
                
                
// don't render visibility=hidden
                
if (this.attribute('visibility').value == 'hidden') return;
            
                
ctx.save();
                    
this.setContext(ctx);
                        
// mask
                        
if (this.attribute('mask').hasValue()) {
                            var 
mask this.attribute('mask').Definition.getDefinition();
                            if (
mask != nullmask.apply(ctxthis);
                        }
                        else if (
this.style('filter').hasValue()) {
                            var 
filter this.style('filter').Definition.getDefinition();
                            if (
filter != nullfilter.apply(ctxthis);
                        }
                        else 
this.renderChildren(ctx);                
                    
this.clearContext(ctx);
                
ctx.restore();
            }
            
            
// base set context
            
this.setContext = function(ctx) {
                
// OVERRIDE ME!
            
}
            
            
// base clear context
            
this.clearContext = function(ctx) {
                
// OVERRIDE ME!
            
}            
            
            
// base render children
            
this.renderChildren = function(ctx) {
                for (var 
i=0i<this.children.lengthi++) {
                    
this.children[i].render(ctx);
                }
            }
            
            
this.addChild = function(childNodecreate) {
                var 
child childNode;
                if (
createchild svg.CreateElement(childNode);
                
child.parent this;
                
this.children.push(child);            
            }
                
            if (
node != null && node.nodeType == 1) { //ELEMENT_NODE
                // add children
                
for (var i=0i<node.childNodes.lengthi++) {
                    var 
childNode node.childNodes[i];
                    if (
childNode.nodeType == 1this.addChild(childNodetrue); //ELEMENT_NODE
                
}
                
                
// add attributes
                
for (var i=0i<node.attributes.lengthi++) {
                    var 
attribute node.attributes[i];
                    
this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeNameattribute.nodeValue);
                }
                                        
                
// add tag styles
                
var styles svg.Styles[node.nodeName];
                if (
styles != null) {
                    for (var 
name in styles) {
                        
this.styles[name] = styles[name];
                    }
                }                    
                
                
// add class styles
                
if (this.attribute('class').hasValue()) {
                    var 
classes svg.compressSpaces(this.attribute('class').value).split(' ');
                    for (var 
j=0j<classes.lengthj++) {
                        
styles svg.Styles['.'+classes[j]];
                        if (
styles != null) {
                            for (var 
name in styles) {
                                
this.styles[name] = styles[name];
                            }
                        }
                        
styles svg.Styles[node.nodeName+'.'+classes[j]];
                        if (
styles != null) {
                            for (var 
name in styles) {
                                
this.styles[name] = styles[name];
                            }
                        }
                    }
                }
                
                
// add inline styles
                
if (this.attribute('style').hasValue()) {
                    var 
styles this.attribute('style').value.split(';');
                    for (var 
i=0i<styles.lengthi++) {
                        if (
svg.trim(styles[i]) != '') {
                            var 
style styles[i].split(':');
                            var 
name svg.trim(style[0]);
                            var 
value svg.trim(style[1]);
                            
this.styles[name] = new svg.Property(namevalue);
                        }
                    }
                }    

                
// add id
                
if (this.attribute('id').hasValue()) {
                    if (
svg.Definitions[this.attribute('id').value] == null) {
                        
svg.Definitions[this.attribute('id').value] = this;
                    }
                }
            }
        }
        
        
svg.Element.RenderedElementBase = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);
            
            
this.setContext = function(ctx) {
                
// fill
                
if (this.style('fill').Definition.isUrl()) {
                    var 
fs this.style('fill').Definition.getFillStyle(this);
                    if (
fs != nullctx.fillStyle fs;
                }
                else if (
this.style('fill').hasValue()) {
                    var 
fillStyle this.style('fill');
                    if (
this.style('fill-opacity').hasValue()) fillStyle fillStyle.Color.addOpacity(this.style('fill-opacity').value);
                    
ctx.fillStyle = (fillStyle.value == 'none' 'rgba(0,0,0,0)' fillStyle.value);
                }
                                    
                
// stroke
                
if (this.style('stroke').Definition.isUrl()) {
                    var 
fs this.style('stroke').Definition.getFillStyle(this);
                    if (
fs != nullctx.strokeStyle fs;
                }
                else if (
this.style('stroke').hasValue()) {
                    var 
strokeStyle this.style('stroke');
                    if (
this.style('stroke-opacity').hasValue()) strokeStyle strokeStyle.Color.addOpacity(this.style('stroke-opacity').value);
                    
ctx.strokeStyle = (strokeStyle.value == 'none' 'rgba(0,0,0,0)' strokeStyle.value);
                }
                if (
this.style('stroke-width').hasValue()) ctx.lineWidth this.style('stroke-width').Length.toPixels();
                if (
this.style('stroke-linecap').hasValue()) ctx.lineCap this.style('stroke-linecap').value;
                if (
this.style('stroke-linejoin').hasValue()) ctx.lineJoin this.style('stroke-linejoin').value;
                if (
this.style('stroke-miterlimit').hasValue()) ctx.miterLimit this.style('stroke-miterlimit').value;

                
// font
                
if (typeof(ctx.font) != 'undefined') {
                    
ctx.font svg.Font.CreateFont
                        
this.style('font-style').value
                        
this.style('font-variant').value
                        
this.style('font-weight').value
                        
this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' ''
                        
this.style('font-family').value).toString();
                }
                
                
// transform
                
if (this.attribute('transform').hasValue()) { 
                    var 
transform = new svg.Transform(this.attribute('transform').value);
                    
transform.apply(ctx);
                }
                
                
// clip
                
if (this.attribute('clip-path').hasValue()) {
                    var 
clip this.attribute('clip-path').Definition.getDefinition();
                    if (
clip != nullclip.apply(ctx);
                }
                
                
// opacity
                
if (this.style('opacity').hasValue()) {
                    
ctx.globalAlpha this.style('opacity').numValue();
                }
            }        
        }
        
svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
        
        
svg.Element.PathElementBase = function(node) {
            
this.base svg.Element.RenderedElementBase;
            
this.base(node);
            
            
this.path = function(ctx) {
                if (
ctx != nullctx.beginPath();
                return new 
svg.BoundingBox();
            }
            
            
this.renderChildren = function(ctx) {
                
this.path(ctx);
                
svg.Mouse.checkPath(thisctx);
                if (
ctx.fillStyle != ''ctx.fill();
                if (
ctx.strokeStyle != ''ctx.stroke();
                
                var 
markers this.getMarkers();
                if (
markers != null) {
                    if (
this.style('marker-start').Definition.isUrl()) {
                        var 
marker this.style('marker-start').Definition.getDefinition();
                        
marker.render(ctxmarkers[0][0], markers[0][1]);
                    }
                    if (
this.style('marker-mid').Definition.isUrl()) {
                        var 
marker this.style('marker-mid').Definition.getDefinition();
                        for (var 
i=1;i<markers.length-1;i++) {
                            
marker.render(ctxmarkers[i][0], markers[i][1]);
                        }
                    }
                    if (
this.style('marker-end').Definition.isUrl()) {
                        var 
marker this.style('marker-end').Definition.getDefinition();
                        
marker.render(ctxmarkers[markers.length-1][0], markers[markers.length-1][1]);
                    }
                }                    
            }
            
            
this.getBoundingBox = function() {
                return 
this.path();
            }
            
            
this.getMarkers = function() {
                return 
null;
            }
        }
        
svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
        
        
// svg element
        
svg.Element.svg = function(node) {
            
this.base svg.Element.RenderedElementBase;
            
this.base(node);
            
            
this.baseClearContext this.clearContext;
            
this.clearContext = function(ctx) {
                
this.baseClearContext(ctx);
                
svg.ViewPort.RemoveCurrent();
            }
            
            
this.baseSetContext this.setContext;
            
this.setContext = function(ctx) {
                
// initial values
                
ctx.strokeStyle 'rgba(0,0,0,0)';
                
ctx.lineCap 'butt';
                
ctx.lineJoin 'miter';
                
ctx.miterLimit 4;            
            
                
this.baseSetContext(ctx);
                
                
// create new view port
                
if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
                    
ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y'));
                }
                
                var 
width svg.ViewPort.width();
                var 
height svg.ViewPort.height();
                if (
typeof(this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
                    
width this.attribute('width').Length.toPixels('x');
                    
height this.attribute('height').Length.toPixels('y');
                    
                    var 
0;
                    var 
0;
                    if (
this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
                        
= -this.attribute('refX').Length.toPixels('x');
                        
= -this.attribute('refY').Length.toPixels('y');
                    }
                    
                    
ctx.beginPath();
                    
ctx.moveTo(xy);
                    
ctx.lineTo(widthy);
                    
ctx.lineTo(widthheight);
                    
ctx.lineTo(xheight);
                    
ctx.closePath();
                    
ctx.clip();
                }
                
svg.ViewPort.SetCurrent(widthheight);    
                        
                
// viewbox
                
if (this.attribute('viewBox').hasValue()) {                
                    var 
viewBox svg.ToNumberArray(this.attribute('viewBox').value);
                    var 
minX viewBox[0];
                    var 
minY viewBox[1];
                    
width viewBox[2];
                    
height viewBox[3];
                    
                    
svg.AspectRatio(ctx,
                                    
this.attribute('preserveAspectRatio').value
                                    
svg.ViewPort.width(), 
                                    
width,
                                    
svg.ViewPort.height(),
                                    
height,
                                    
minX,
                                    
minY,
                                    
this.attribute('refX').value,
                                    
this.attribute('refY').value);
                                        
                    
svg.ViewPort.RemoveCurrent();    
                    
svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);                        
                }                
            }
        }
        
svg.Element.svg.prototype = new svg.Element.RenderedElementBase;

        
// rect element
        
svg.Element.rect = function(node) {
            
this.base svg.Element.PathElementBase;
            
this.base(node);
            
            
this.path = function(ctx) {
                var 
this.attribute('x').Length.toPixels('x');
                var 
this.attribute('y').Length.toPixels('y');
                var 
width this.attribute('width').Length.toPixels('x');
                var 
height this.attribute('height').Length.toPixels('y');
                var 
rx this.attribute('rx').Length.toPixels('x');
                var 
ry this.attribute('ry').Length.toPixels('y');
                if (
this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry rx;
                if (
this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx ry;
                
                if (
ctx != null) {
                    
ctx.beginPath();
                    
ctx.moveTo(rxy);
                    
ctx.lineTo(width rxy);
                    
ctx.quadraticCurveTo(widthywidthry)
                    
ctx.lineTo(widthheight ry);
                    
ctx.quadraticCurveTo(widthheightwidth rxheight)
                    
ctx.lineTo(rxheight);
                    
ctx.quadraticCurveTo(xheightxheight ry)
                    
ctx.lineTo(xry);
                    
ctx.quadraticCurveTo(xyrxy)
                    
ctx.closePath();
                }
                
                return new 
svg.BoundingBox(xywidthheight);
            }
        }
        
svg.Element.rect.prototype = new svg.Element.PathElementBase;
        
        
// circle element
        
svg.Element.circle = function(node) {
            
this.base svg.Element.PathElementBase;
            
this.base(node);
            
            
this.path = function(ctx) {
                var 
cx this.attribute('cx').Length.toPixels('x');
                var 
cy this.attribute('cy').Length.toPixels('y');
                var 
this.attribute('r').Length.toPixels();
            
                if (
ctx != null) {
                    
ctx.beginPath();
                    
ctx.arc(cxcyr0Math.PI 2true); 
                    
ctx.closePath();
                }
                
                return new 
svg.BoundingBox(cx rcy rcx rcy r);
            }
        }
        
svg.Element.circle.prototype = new svg.Element.PathElementBase;    

        
// ellipse element
        
svg.Element.ellipse = function(node) {
            
this.base svg.Element.PathElementBase;
            
this.base(node);
            
            
this.path = function(ctx) {
                var 
KAPPA * ((Math.sqrt(2) - 1) / 3);
                var 
rx this.attribute('rx').Length.toPixels('x');
                var 
ry this.attribute('ry').Length.toPixels('y');
                var 
cx this.attribute('cx').Length.toPixels('x');
                var 
cy this.attribute('cy').Length.toPixels('y');
                
                if (
ctx != null) {
                    
ctx.beginPath();
                    
ctx.moveTo(cxcy ry);
                    
ctx.bezierCurveTo(cx + (KAPPA rx), cy ry,  cx rxcy - (KAPPA ry), cx rxcy);
                    
ctx.bezierCurveTo(cx rxcy + (KAPPA ry), cx + (KAPPA rx), cy rycxcy ry);
                    
ctx.bezierCurveTo(cx - (KAPPA rx), cy rycx rxcy + (KAPPA ry), cx rxcy);
                    
ctx.bezierCurveTo(cx rxcy - (KAPPA ry), cx - (KAPPA rx), cy rycxcy ry);
                    
ctx.closePath();
                }
                
                return new 
svg.BoundingBox(cx rxcy rycx rxcy ry);
            }
        }
        
svg.Element.ellipse.prototype = new svg.Element.PathElementBase;            
        
        
// line element
        
svg.Element.line = function(node) {
            
this.base svg.Element.PathElementBase;
            
this.base(node);
            
            
this.getPoints = function() {
                return [
                    new 
svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')),
                    new 
svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))];
            }
                                
            
this.path = function(ctx) {
                var 
points this.getPoints();
                
                if (
ctx != null) {
                    
ctx.beginPath();
                    
ctx.moveTo(points[0].xpoints[0].y);
                    
ctx.lineTo(points[1].xpoints[1].y);
                }
                
                return new 
svg.BoundingBox(points[0].xpoints[0].ypoints[1].xpoints[1].y);
            }
            
            
this.getMarkers = function() {
                var 
points this.getPoints();    
                var 
points[0].angleTo(points[1]);
                return [[
points[0], a], [points[1], a]];
            }
        }
        
svg.Element.line.prototype = new svg.Element.PathElementBase;        
                
        
// polyline element
        
svg.Element.polyline = function(node) {
            
this.base svg.Element.PathElementBase;
            
this.base(node);
            
            
this.points svg.CreatePath(this.attribute('points').value);
            
this.path = function(ctx) {
                var 
bb = new svg.BoundingBox(this.points[0].xthis.points[0].y);
                if (
ctx != null) {
                    
ctx.beginPath();
                    
ctx.moveTo(this.points[0].xthis.points[0].y);
                }
                for (var 
i=1i<this.points.lengthi++) {
                    
bb.addPoint(this.points[i].xthis.points[i].y);
                    if (
ctx != nullctx.lineTo(this.points[i].xthis.points[i].y);
                }
                return 
bb;
            }
            
            
this.getMarkers = function() {
                var 
markers = [];
                for (var 
i=0i<this.points.length 1i++) {
                    
markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
                }
                
markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
                return 
markers;
            }            
        }
        
svg.Element.polyline.prototype = new svg.Element.PathElementBase;                
                
        
// polygon element
        
svg.Element.polygon = function(node) {
            
this.base svg.Element.polyline;
            
this.base(node);
            
            
this.basePath this.path;
            
this.path = function(ctx) {
                var 
bb this.basePath(ctx);
                if (
ctx != null) {
                    
ctx.lineTo(this.points[0].xthis.points[0].y);
                    
ctx.closePath();
                }
                return 
bb;
            }
        }
        
svg.Element.polygon.prototype = new svg.Element.polyline;

        
// path element
        
svg.Element.path = function(node) {
            
this.base svg.Element.PathElementBase;
            
this.base(node);
                    
            var 
this.attribute('d').value;
            
// TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
            
d.replace(/,/gm,' '); // get rid of all commas
            
d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
            
d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
            
d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^s])/gm,'$1 $2'); // separate commands from points
            
d.replace(/([^s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
            
d.replace(/([0-9])([+-])/gm,'$1 $2'); // separate digits when no comma
            
d.replace(/(.[0-9]*)(.)/gm,'$1 $2'); // separate digits when no comma
            
d.replace(/([Aa](s+[0-9]+){3})s+([01])s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
            
svg.compressSpaces(d); // compress multiple spaces
            
svg.trim(d);
            
this.PathParser = new (function(d) {
                
this.tokens d.split(' ');
                
                
this.reset = function() {
                    
this.= -1;
                    
this.command '';
                    
this.previousCommand '';
                    
this.start = new svg.Point(00);
                    
this.control = new svg.Point(00);
                    
this.current = new svg.Point(00);
                    
this.points = [];
                    
this.angles = [];
                }
                                
                
this.isEnd = function() {
                    return 
this.>= this.tokens.length 1;
                }
                
                
this.isCommandOrEnd = function() {
                    if (
this.isEnd()) return true;
                    return 
this.tokens[this.1].match(/^[A-Za-z]$/) != null;
                }
                
                
this.isRelativeCommand = function() {
                    return 
this.command == this.command.toLowerCase();
                }
                            
                
this.getToken = function() {
                    
this.this.1;
                    return 
this.tokens[this.i];
                }
                
                
this.getScalar = function() {
                    return 
parseFloat(this.getToken());
                }
                
                
this.nextCommand = function() {
                    
this.previousCommand this.command;
                    
this.command this.getToken();
                }                
                
                
this.getPoint = function() {
                    var 
= new svg.Point(this.getScalar(), this.getScalar());
                    return 
this.makeAbsolute(p);
                }
                
                
this.getAsControlPoint = function() {
                    var 
this.getPoint();
                    
this.control p;
                    return 
p;
                }
                
                
this.getAsCurrentPoint = function() {
                    var 
this.getPoint();
                    
this.current p;
                    return 
p;    
                }
                
                
this.getReflectedControlPoint = function() {
                    if (
this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') {
                        return 
this.current;
                    }
                    
                    
// reflect point
                    
var = new svg.Point(this.current.this.control.xthis.current.this.control.y);                    
                    return 
p;
                }
                
                
this.makeAbsolute = function(p) {
                    if (
this.isRelativeCommand()) {
                        
p.this.current.p.x;
                        
p.this.current.p.y;
                    }
                    return 
p;
                }
                
                
this.addMarker = function(pfrompriorTo) {
                    
// if the last angle isn't filled in because we didn't have this point yet ...
                    
if (priorTo != null && this.angles.length && this.angles[this.angles.length-1] == null) {
                        
this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
                    }
                    
this.addMarkerAngle(pfrom == null null from.angleTo(p));
                }
                
                
this.addMarkerAngle = function(pa) {
                    
this.points.push(p);
                    
this.angles.push(a);
                }                
                
                
this.getMarkerPoints = function() { return this.points; }
                
this.getMarkerAngles = function() {
                    for (var 
i=0i<this.angles.lengthi++) {
                        if (
this.angles[i] == null) {
                            for (var 
j=i+1j<this.angles.lengthj++) {
                                if (
this.angles[j] != null) {
                                    
this.angles[i] = this.angles[j];
                                    break;
                                }
                            }
                        }
                    }
                    return 
this.angles;
                }
            })(
d);

            
this.path = function(ctx) {
                var 
pp this.PathParser;
                
pp.reset();

                var 
bb = new svg.BoundingBox();
                if (
ctx != nullctx.beginPath();
                while (!
pp.isEnd()) {
                    
pp.nextCommand();
                    switch (
pp.command.toUpperCase()) {
                    case 
'M':
                        var 
pp.getAsCurrentPoint();
                        
pp.addMarker(p);
                        
bb.addPoint(p.xp.y);
                        if (
ctx != nullctx.moveTo(p.xp.y);
                        
pp.start pp.current;
                        while (!
pp.isCommandOrEnd()) {
                            var 
pp.getAsCurrentPoint();
                            
pp.addMarker(ppp.start);
                            
bb.addPoint(p.xp.y);
                            if (
ctx != nullctx.lineTo(p.xp.y);
                        }
                        break;
                    case 
'L':
                        while (!
pp.isCommandOrEnd()) {
                            var 
pp.current;
                            var 
pp.getAsCurrentPoint();
                            
pp.addMarker(pc);
                            
bb.addPoint(p.xp.y);
                            if (
ctx != nullctx.lineTo(p.xp.y);
                        }
                        break;
                    case 
'H':
                        while (!
pp.isCommandOrEnd()) {
                            var 
newP = new svg.Point((pp.isRelativeCommand() ? pp.current.0) + pp.getScalar(), pp.current.y);
                            
pp.addMarker(newPpp.current);
                            
pp.current newP;
                            
bb.addPoint(pp.current.xpp.current.y);
                            if (
ctx != nullctx.lineTo(pp.current.xpp.current.y);
                        }
                        break;
                    case 
'V':
                        while (!
pp.isCommandOrEnd()) {
                            var 
newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.0) + pp.getScalar());
                            
pp.addMarker(newPpp.current);
                            
pp.current newP;
                            
bb.addPoint(pp.current.xpp.current.y);
                            if (
ctx != nullctx.lineTo(pp.current.xpp.current.y);
                        }
                        break;
                    case 
'C':
                        while (!
pp.isCommandOrEnd()) {
                            var 
curr pp.current;
                            var 
p1 pp.getPoint();
                            var 
cntrl pp.getAsControlPoint();
                            var 
cp pp.getAsCurrentPoint();
                            
pp.addMarker(cpcntrlp1);
                            
bb.addBezierCurve(curr.xcurr.yp1.xp1.ycntrl.xcntrl.ycp.xcp.y);
                            if (
ctx != nullctx.bezierCurveTo(p1.xp1.ycntrl.xcntrl.ycp.xcp.y);
                        }
                        break;
                    case 
'S':
                        while (!
pp.isCommandOrEnd()) {
                            var 
curr pp.current;
                            var 
p1 pp.getReflectedControlPoint();
                            var 
cntrl pp.getAsControlPoint();
                            var 
cp pp.getAsCurrentPoint();
                            
pp.addMarker(cpcntrlp1);
                            
bb.addBezierCurve(curr.xcurr.yp1.xp1.ycntrl.xcntrl.ycp.xcp.y);
                            if (
ctx != nullctx.bezierCurveTo(p1.xp1.ycntrl.xcntrl.ycp.xcp.y);
                        }
                        break;
                    case 
'Q':
                        while (!
pp.isCommandOrEnd()) {
                            var 
curr pp.current;
                            var 
cntrl pp.getAsControlPoint();
                            var 
cp pp.getAsCurrentPoint();
                            
pp.addMarker(cpcntrlcntrl);
                            
bb.addQuadraticCurve(curr.xcurr.ycntrl.xcntrl.ycp.xcp.y);
                            if (
ctx != nullctx.quadraticCurveTo(cntrl.xcntrl.ycp.xcp.y);
                        }
                        break;
                    case 
'T':
                        while (!
pp.isCommandOrEnd()) {
                            var 
curr pp.current;
                            var 
cntrl pp.getReflectedControlPoint();
                            
pp.control cntrl;
                            var 
cp pp.getAsCurrentPoint();
                            
pp.addMarker(cpcntrlcntrl);
                            
bb.addQuadraticCurve(curr.xcurr.ycntrl.xcntrl.ycp.xcp.y);
                            if (
ctx != nullctx.quadraticCurveTo(cntrl.xcntrl.ycp.xcp.y);
                        }
                        break;
                    case 
'A':
                        while (!
pp.isCommandOrEnd()) {
                            var 
curr pp.current;
                            var 
rx pp.getScalar();
                            var 
ry pp.getScalar();
                            var 
xAxisRotation pp.getScalar() * (Math.PI 180.0);
                            var 
largeArcFlag pp.getScalar();
                            var 
sweepFlag pp.getScalar();
                            var 
cp pp.getAsCurrentPoint();

                            
// Conversion from endpoint to center parameterization
                            // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
                            // x1', y1'
                            
var currp = new svg.Point(
                                
Math.cos(xAxisRotation) * (curr.cp.x) / 2.0 Math.sin(xAxisRotation) * (curr.cp.y) / 2.0,
                                -
Math.sin(xAxisRotation) * (curr.cp.x) / 2.0 Math.cos(xAxisRotation) * (curr.cp.y) / 2.0
                            
);
                            
// adjust radii
                            
var Math.pow(currp.x,2)/Math.pow(rx,2)+Math.pow(currp.y,2)/Math.pow(ry,2);
                            if (
1) {
                                
rx *= Math.sqrt(l);
                                
ry *= Math.sqrt(l);
                            }
                            
// cx', cy'
                            
var = (largeArcFlag == sweepFlag ? -1) * Math.sqrt(
                                ((
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))) /
                                (
Math.pow(rx,2)*Math.pow(currp.y,2)+Math.pow(ry,2)*Math.pow(currp.x,2))
                            );
                            if (
isNaN(s)) 0;
                            var 
cpp = new svg.Point(rx currp.ry* -ry currp.rx);
                            
// cx, cy
                            
var centp = new svg.Point(
                                (
curr.cp.x) / 2.0 Math.cos(xAxisRotation) * cpp.Math.sin(xAxisRotation) * cpp.y,
                                (
curr.cp.y) / 2.0 Math.sin(xAxisRotation) * cpp.Math.cos(xAxisRotation) * cpp.y
                            
);
                            
// vector magnitude
                            
var = function(v) { return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2)); }
                            
// ratio between two vectors
                            
var = function(uv) { return (u[0]*v[0]+u[1]*v[1]) / (m(u)*m(v)) }
                            
// angle between two vectors
                            
var = function(uv) { return (u[0]*v[1] < u[1]*v[0] ? -1) * Math.acos(r(u,v)); }
                            
// initial angle
                            
var a1 a([1,0], [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry]);
                            
// angle delta
                            
var = [(currp.x-cpp.x)/rx,(currp.y-cpp.y)/ry];
                            var 
= [(-currp.x-cpp.x)/rx,(-currp.y-cpp.y)/ry];
                            var 
ad a(uv);
                            if (
r(u,v) <= -1ad Math.PI;
                            if (
r(u,v) >= 1ad 0;

                            if (
sweepFlag == && ad 0ad ad Math.PI;
                            if (
sweepFlag == && ad 0ad ad Math.PI;

                            
// for markers
                            
var halfWay = new svg.Point(
                                
centp.rx Math.cos((a1 ad) / 2),
                                
centp.ry Math.sin((a1 ad) / 2)
                            );
                            
pp.addMarkerAngle(halfWay, (a1 ad) / + (sweepFlag == : -1) * Math.PI 2);
                            
pp.addMarkerAngle(cpad + (sweepFlag == : -1) * Math.PI 2);

                            
bb.addPoint(cp.xcp.y); // TODO: this is too naive, make it better
                            
if (ctx != null) {
                                var 
rx ry rx ry;
                                var 
sx rx ry rx ry;
                                var 
sy rx ry ry rx 1;

                                
ctx.translate(centp.xcentp.y);
                                
ctx.rotate(xAxisRotation);
                                
ctx.scale(sxsy);
                                
ctx.arc(00ra1a1 adsweepFlag);
                                
ctx.scale(1/sx1/sy);
                                
ctx.rotate(-xAxisRotation);
                                
ctx.translate(-centp.x, -centp.y);
                            }
                        }
                        break;
                    case 
'Z':
                        if (
ctx != nullctx.closePath();
                        
pp.current pp.start;
                    }
                }

                return 
bb;
            }

            
this.getMarkers = function() {
                var 
points this.PathParser.getMarkerPoints();
                var 
angles this.PathParser.getMarkerAngles();
                
                var 
markers = [];
                for (var 
i=0i<points.lengthi++) {
                    
markers.push([points[i], angles[i]]);
                }
                return 
markers;
            }
        }
        
svg.Element.path.prototype = new svg.Element.PathElementBase;
        
        
// pattern element
        
svg.Element.pattern = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);
            
            
this.createPattern = function(ctxelement) {
                
// render me using a temporary svg element
                
var tempSvg = new svg.Element.svg();
                
tempSvg.attributes['viewBox'] = new svg.Property('viewBox'this.attribute('viewBox').value);
                
tempSvg.attributes['x'] = new svg.Property('x'this.attribute('x').value);
                
tempSvg.attributes['y'] = new svg.Property('y'this.attribute('y').value);
                
tempSvg.attributes['width'] = new svg.Property('width'this.attribute('width').value);
                
tempSvg.attributes['height'] = new svg.Property('height'this.attribute('height').value);
                
tempSvg.children this.children;
                
                var 
document.createElement('canvas');
                
c.width this.attribute('width').Length.toPixels('x');
                
c.height this.attribute('height').Length.toPixels('y');
                
tempSvg.render(c.getContext('2d'));        
                return 
ctx.createPattern(c'repeat');
            }
        }
        
svg.Element.pattern.prototype = new svg.Element.ElementBase;
        
        
// marker element
        
svg.Element.marker = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);
            
            
this.baseRender this.render;
            
this.render = function(ctxpointangle) {
                
ctx.translate(point.xpoint.y);
                if (
this.attribute('orient').valueOrDefault('auto') == 'auto'ctx.rotate(angle);
                if (
this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth'ctx.scale(ctx.lineWidthctx.lineWidth);
                
ctx.save();
                            
                
// render me using a temporary svg element
                
var tempSvg = new svg.Element.svg();
                
tempSvg.attributes['viewBox'] = new svg.Property('viewBox'this.attribute('viewBox').value);
                
tempSvg.attributes['refX'] = new svg.Property('refX'this.attribute('refX').value);
                
tempSvg.attributes['refY'] = new svg.Property('refY'this.attribute('refY').value);
                
tempSvg.attributes['width'] = new svg.Property('width'this.attribute('markerWidth').value);
                
tempSvg.attributes['height'] = new svg.Property('height'this.attribute('markerHeight').value);
                
tempSvg.attributes['fill'] = new svg.Property('fill'this.attribute('fill').valueOrDefault('black'));
                
tempSvg.attributes['stroke'] = new svg.Property('stroke'this.attribute('stroke').valueOrDefault('none'));
                
tempSvg.children this.children;
                
tempSvg.render(ctx);
                
                
ctx.restore();
                if (
this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth'ctx.scale(1/ctx.lineWidth1/ctx.lineWidth);
                if (
this.attribute('orient').valueOrDefault('auto') == 'auto'ctx.rotate(-angle);
                
ctx.translate(-point.x, -point.y);
            }
        }
        
svg.Element.marker.prototype = new svg.Element.ElementBase;
        
        
// definitions element
        
svg.Element.defs = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);    
            
            
this.render = function(ctx) {
                
// NOOP
            
}
        }
        
svg.Element.defs.prototype = new svg.Element.ElementBase;
        
        
// base for gradients
        
svg.Element.GradientBase = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);
            
            
this.gradientUnits this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
            
            
this.stops = [];            
            for (var 
i=0i<this.children.lengthi++) {
                var 
child this.children[i];
                
this.stops.push(child);
            }    
            
            
this.getGradient = function() {
                
// OVERRIDE ME!
            
}            

            
this.createGradient = function(ctxelement) {
                var 
stopsContainer this;
                if (
this.attribute('xlink:href').hasValue()) {
                    
stopsContainer this.attribute('xlink:href').Definition.getDefinition();
                }
            
                var 
this.getGradient(ctxelement);
                for (var 
i=0i<stopsContainer.stops.lengthi++) {
                    
g.addColorStop(stopsContainer.stops[i].offsetstopsContainer.stops[i].color);
                }
                
                if (
this.attribute('gradientTransform').hasValue()) {
                    
// render as transformed pattern on temporary canvas
                    
var rootView svg.ViewPort.viewPorts[0];
                    
                    var 
rect = new svg.Element.rect();
                    
rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS/3.0);
                    
rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS/3.0);
                    
rect.attributes['width'] = new svg.Property('width'svg.MAX_VIRTUAL_PIXELS);
                    
rect.attributes['height'] = new svg.Property('height'svg.MAX_VIRTUAL_PIXELS);
                    
                    var 
group = new svg.Element.g();
                    
group.attributes['transform'] = new svg.Property('transform'this.attribute('gradientTransform').value);
                    
group.children = [ rect ];
                    
                    var 
tempSvg = new svg.Element.svg();
                    
tempSvg.attributes['x'] = new svg.Property('x'0);
                    
tempSvg.attributes['y'] = new svg.Property('y'0);
                    
tempSvg.attributes['width'] = new svg.Property('width'rootView.width);
                    
tempSvg.attributes['height'] = new svg.Property('height'rootView.height);
                    
tempSvg.children = [ group ];
                    
                    var 
document.createElement('canvas');
                    
c.width rootView.width;
                    
c.height rootView.height;
                    var 
tempCtx c.getContext('2d');
                    
tempCtx.fillStyle g;
                    
tempSvg.render(tempCtx);        
                    return 
tempCtx.createPattern(c'no-repeat');
                }
                
                return 
g;                
            }
        }
        
svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
        
        
// linear gradient element
        
svg.Element.linearGradient = function(node) {
            
this.base svg.Element.GradientBase;
            
this.base(node);
            
            
this.getGradient = function(ctxelement) {
                var 
bb element.getBoundingBox();
                
                var 
x1 = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.x() + bb.width() * this.attribute('x1').numValue() 
                    : 
this.attribute('x1').Length.toPixels('x'));
                var 
y1 = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.y() + bb.height() * this.attribute('y1').numValue()
                    : 
this.attribute('y1').Length.toPixels('y'));
                var 
x2 = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.x() + bb.width() * this.attribute('x2').numValue()
                    : 
this.attribute('x2').Length.toPixels('x'));
                var 
y2 = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.y() + bb.height() * this.attribute('y2').numValue()
                    : 
this.attribute('y2').Length.toPixels('y'));

                return 
ctx.createLinearGradient(x1y1x2y2);
            }
        }
        
svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
        
        
// radial gradient element
        
svg.Element.radialGradient = function(node) {
            
this.base svg.Element.GradientBase;
            
this.base(node);
            
            
this.getGradient = function(ctxelement) {
                var 
bb element.getBoundingBox();
                
                var 
cx = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.x() + bb.width() * this.attribute('cx').numValue() 
                    : 
this.attribute('cx').Length.toPixels('x'));
                var 
cy = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.y() + bb.height() * this.attribute('cy').numValue() 
                    : 
this.attribute('cy').Length.toPixels('y'));
                
                var 
fx cx;
                var 
fy cy;
                if (
this.attribute('fx').hasValue()) {
                    
fx = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.x() + bb.width() * this.attribute('fx').numValue() 
                    : 
this.attribute('fx').Length.toPixels('x'));
                }
                if (
this.attribute('fy').hasValue()) {
                    
fy = (this.gradientUnits == 'objectBoundingBox' 
                    
bb.y() + bb.height() * this.attribute('fy').numValue() 
                    : 
this.attribute('fy').Length.toPixels('y'));
                }
                
                var 
= (this.gradientUnits == 'objectBoundingBox' 
                    
? (bb.width() + bb.height()) / 2.0 this.attribute('r').numValue()
                    : 
this.attribute('r').Length.toPixels());
                
                return 
ctx.createRadialGradient(fxfy0cxcyr);
            }
        }
        
svg.Element.radialGradient.prototype = new svg.Element.GradientBase;
        
        
// gradient stop element
        
svg.Element.stop = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);
            
            
this.offset this.attribute('offset').numValue();
            
            var 
stopColor this.style('stop-color');
            if (
this.style('stop-opacity').hasValue()) stopColor stopColor.Color.addOpacity(this.style('stop-opacity').value);
            
this.color stopColor.value;
        }
        
svg.Element.stop.prototype = new svg.Element.ElementBase;
        
        
// animation base element
        
svg.Element.AnimateBase = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);
            
            
svg.Animations.push(this);
            
            
this.duration 0.0;
            
this.begin this.attribute('begin').Time.toMilliseconds();
            
this.maxDuration this.begin this.attribute('dur').Time.toMilliseconds();
            
            
this.getProperty = function() {
                var 
attributeType this.attribute('attributeType').value;
                var 
attributeName this.attribute('attributeName').value;
                
                if (
attributeType == 'CSS') {
                    return 
this.parent.style(attributeNametrue);
                }
                return 
this.parent.attribute(attributeNametrue);            
            };
            
            
this.initialValue null;
            
this.removed false;            

            
this.calcValue = function() {
                
// OVERRIDE ME!
                
return '';
            }
            
            
this.update = function(delta) {    
                
// set initial value
                
if (this.initialValue == null) {
                    
this.initialValue this.getProperty().value;
                }
            
                
// if we're past the end time
                
if (this.duration this.maxDuration) {
                    
// loop for indefinitely repeating animations
                    
if (this.attribute('repeatCount').value == 'indefinite') {
                        
this.duration 0.0
                    
}
                    else if (
this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
                        
this.removed true;
                        
this.getProperty().value this.initialValue;
                        return 
true;
                    }
                    else {
                        return 
false// no updates made
                    
}
                }            
                
this.duration this.duration delta;
            
                
// if we're past the begin time
                
var updated false;
                if (
this.begin this.duration) {
                    var 
newValue this.calcValue(); // tween
                    
                    
if (this.attribute('type').hasValue()) {
                        
// for transform, etc.
                        
var type this.attribute('type').value;
                        
newValue type '(' newValue ')';
                    }
                    
                    
this.getProperty().value newValue;
                    
updated true;
                }
                
                return 
updated;
            }
            
            
// fraction of duration we've covered
            
this.progress = function() {
                return ((
this.duration this.begin) / (this.maxDuration this.begin));
            }            
        }
        
svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
        
        
// animate element
        
svg.Element.animate = function(node) {
            
this.base svg.Element.AnimateBase;
            
this.base(node);
            
            
this.calcValue = function() {
                var 
from this.attribute('from').numValue();
                var 
to this.attribute('to').numValue();
                
                
// tween value linearly
                
return from + (to from) * this.progress(); 
            };
        }
        
svg.Element.animate.prototype = new svg.Element.AnimateBase;
            
        
// animate color element
        
svg.Element.animateColor = function(node) {
            
this.base svg.Element.AnimateBase;
            
this.base(node);

            
this.calcValue = function() {
                var 
from = new RGBColor(this.attribute('from').value);
                var 
to = new RGBColor(this.attribute('to').value);
                
                if (
from.ok && to.ok) {
                    
// tween color linearly
                    
var from.+ (to.from.r) * this.progress();
                    var 
from.+ (to.from.g) * this.progress();
                    var 
from.+ (to.from.b) * this.progress();
                    return 
'rgb('+parseInt(r,10)+','+parseInt(g,10)+','+parseInt(b,10)+')';
                }
                return 
this.attribute('from').value;
            };
        }
        
svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
        
        
// animate transform element
        
svg.Element.animateTransform = function(node) {
            
this.base svg.Element.animate;
            
this.base(node);
        }
        
svg.Element.animateTransform.prototype = new svg.Element.animate;
        
        
// font element
        
svg.Element.font = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);

            
this.horizAdvX this.attribute('horiz-adv-x').numValue();            
            
            
this.isRTL false;
            
this.isArabic false;
            
this.fontFace null;
            
this.missingGlyph null;
            
this.glyphs = [];            
            for (var 
i=0i<this.children.lengthi++) {
                var 
child this.children[i];
                if (
child.type == 'font-face') {
                    
this.fontFace child;
                    if (
child.style('font-family').hasValue()) {
                        
svg.Definitions[child.style('font-family').value] = this;
                    }
                }
                else if (
child.type == 'missing-glyph'this.missingGlyph child;
                else if (
child.type == 'glyph') {
                    if (
child.arabicForm != '') {
                        
this.isRTL true;
                        
this.isArabic true;
                        if (
typeof(this.glyphs[child.unicode]) == 'undefined'this.glyphs[child.unicode] = [];
                        
this.glyphs[child.unicode][child.arabicForm] = child;
                    }
                    else {
                        
this.glyphs[child.unicode] = child;
                    }
                }
            }    
        }
        
svg.Element.font.prototype = new svg.Element.ElementBase;
        
        
// font-face element
        
svg.Element.fontface = function(node) {
            
this.base svg.Element.ElementBase;
            
this.base(node);    
            
            
this.ascent this.attribute('ascent').value;
            
this.descent this.attribute('descent').value;
            
this.unitsPerEm this.attribute('units-per-em').numValue();                
        }
        
svg.Element.fontface.prototype = new svg.Element.ElementBase;
        
        
// missing-glyph element
        
svg.Element.missingglyph = function(node) {
            
this.base svg.Element.path;
            
this.base(node);    
            
            
this.horizAdvX 0;
        }
        
svg.Element.missingglyph.prototype = new svg.Element.path;
        
        
// glyph element
        
svg.Element.glyph = function(node) {
            
this.base svg.Element.path;
            
this.base(node);    
            
            
this.horizAdvX this.attribute('horiz-adv-x').numValue();
            
this.unicode this.attribute('unicode').value;
            
this.arabicForm this.attribute('arabic-form').value;
        }
        
svg.Element.glyph.prototype = new svg.Element.path;
        
        
// text element
        
svg.Element.text = function(node) {
            
this.base svg.Element.RenderedElementBase;
            
this.base(node);
            
            if (
node != null) {
                
// add children
                
this.children = [];
                for (var 
i=0i<node.childNodes.lengthi++) {
                    var 
childNode node.childNodes[i];
                    if (
childNode.nodeType == 1) { // capture tspan and tref nodes
                        
this.addChild(childNodetrue);
                    }
                    else if (
childNode.nodeType == 3) { // capture text
                        
this.addChild(new svg.Element.tspan(childNode), false);
                    }
                }
            }
            
            
this.baseSetContext this.setContext;
            
this.setContext = function(ctx) {
                
this.baseSetContext(ctx);
                if (
this.style('dominant-baseline').hasValue()) ctx.textBaseline this.style('dominant-baseline').value;
                if (
this.style('alignment-baseline').hasValue()) ctx.textBaseline this.style('alignment-baseline').value;
            }
            
            
this.renderChildren = function(ctx) {
                var 
textAnchor this.style('text-anchor').valueOrDefault('start');
                var 
this.attribute('x').Length.toPixels('x');
                var 
this.attribute('y').Length.toPixels('y');
                for (var 
i=0i<this.children.lengthi++) {
                    var 
child this.children[i];
                
                    if (
child.attribute('x').hasValue()) {
                        
child.child.attribute('x').Length.toPixels('x');
                    }
                    else {
                        if (
child.attribute('dx').hasValue()) += child.attribute('dx').Length.toPixels('x');
                        
child.x;
                    }
                    
                    var 
childLength child.measureText(ctx);
                    if (
textAnchor != 'start' && (i==|| child.attribute('x').hasValue())) { // new group?
                        // loop through rest of children
                        
var groupLength childLength;
                        for (var 
j=i+1j<this.children.lengthj++) {
                            var 
childInGroup this.children[j];
                            if (
childInGroup.attribute('x').hasValue()) break; // new group
                            
groupLength += childInGroup.measureText(ctx);
                        }
                        
child.-= (textAnchor == 'end' groupLength groupLength 2.0);
                    }
                    
child.childLength;
                    
                    if (
child.attribute('y').hasValue()) {
                        
child.child.attribute('y').Length.toPixels('y');
                    }
                    else {
                        if (
child.attribute('dy').hasValue()) += child.attribute('dy').Length.toPixels('y');
                        
child.y;
                    }    
                    
child.y;
                    
                    
child.render(ctx);
                }
            }
        }
        
svg.Element.text.prototype = new svg.Element.RenderedElementBase;
        
        
// text base
        
svg.Element.TextElementBase = function(node) {
            
this.base svg.Element.RenderedElementBase;
            
this.base(node);
            
            
this.getGlyph = function(fonttexti) {
                var 
text[i];
                var 
glyph null;
                if (
font.isArabic) {
                    var 
arabicForm 'isolated';
                    if ((
i==|| text[i-1]==' ') && i<text.length-&& text[i+1]!=' 'arabicForm 'terminal'
                    if (
i>&& text[i-1]!=' ' && i<text.length-&& text[i+1]!=' 'arabicForm 'medial';
                    if (
i>&& text[i-1]!=' ' && (== text.length-|| text[i+1]==' ')) arabicForm 'initial';
                    if (
typeof(font.glyphs[c]) != 'undefined') {
                        
glyph font.glyphs[c][arabicForm];
                        if (
glyph == null && font.glyphs[c].type == 'glyph'glyph font.glyphs[c];
                    }
                }
                else {
                    
glyph font.glyphs[c];
                }
                if (
glyph == nullglyph font.missingGlyph;
                return 
glyph;
            }
            
            
this.renderChildren = function(ctx) {
                var 
customFont this.parent.style('font-family').Definition.getDefinition();
                if (
customFont != null) {
                    var 
fontSize this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
                    var 
fontStyle this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
                    var 
text this.getText();
                    if (
customFont.isRTLtext text.split("").reverse().join("");
                    
                    var 
dx svg.ToNumberArray(this.parent.attribute('dx').value);
                    for (var 
i=0i<text.lengthi++) {
                        var 
glyph this.getGlyph(customFonttexti);
                        var 
scale fontSize customFont.fontFace.unitsPerEm;
                        
ctx.translate(this.xthis.y);
                        
ctx.scale(scale, -scale);
                        var 
lw ctx.lineWidth;
                        
ctx.lineWidth ctx.lineWidth customFont.fontFace.unitsPerEm fontSize;
                        if (
fontStyle == 'italic'ctx.transform(10.4100);
                        
glyph.render(ctx);
                        if (
fontStyle == 'italic'ctx.transform(10, -.4100);
                        
ctx.lineWidth lw;
                        
ctx.scale(1/scale, -1/scale);
                        
ctx.translate(-this.x, -this.y);    
                        
                        
this.+= fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm;
                        if (
typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
                            
this.+= dx[i];
                        }
                    }
                    return;
                }
            
                if (
ctx.strokeStyle != ''ctx.strokeText(svg.compressSpaces(this.getText()), this.xthis.y);
                if (
ctx.fillStyle != ''ctx.fillText(svg.compressSpaces(this.getText()), this.xthis.y);
            }
            
            
this.getText = function() {
                
// OVERRIDE ME
            
}
            
            
this.measureText = function(ctx) {
                var 
customFont this.parent.style('font-family').Definition.getDefinition();
                if (
customFont != null) {
                    var 
fontSize this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
                    var 
measure 0;
                    var 
text this.getText();
                    if (
customFont.isRTLtext text.split("").reverse().join("");
                    var 
dx svg.ToNumberArray(this.parent.attribute('dx').value);
                    for (var 
i=0i<text.lengthi++) {
                        var 
glyph this.getGlyph(customFonttexti);
                        
measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize customFont.fontFace.unitsPerEm;
                        if (
typeof(dx[i]) != 'undefined' && !isNaN(dx[i])) {
                            
measure += dx[i];
                        }
                    }
                    return 
measure;
                }
            
                var 
textToMeasure svg.compressSpaces(this.getText());
                if (!
ctx.measureText) return textToMeasure.length 10;
                
                
ctx.save();
                
this.setContext(ctx);
                var 
width ctx.measureText(textToMeasure).width;
                
ctx.restore();
                return 
width;
            }
        }
        
svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase;
        
        
// tspan 
        
svg.Element.tspan = function(node) {
            
this.base svg.Element.TextElementBase;
            
this.base(node);
            
            
this.text node.nodeType == node.nodeValue // text
                        
node.childNodes.length node.childNodes[0].nodeValue // element
                        
node.text;
            
this.getText = function() {
                return 
this.text;
            }
        }
        
svg.Element.tspan.prototype = new svg.Element.TextElementBase;
        
        
// tref
        
svg.Element.tref = function(node) {
            
this.base svg.Element.TextElementBase;
            
this.base(node);
            
            
this.getText = function() {
                var 
element this.attribute('xlink:href').Definition.getDefinition();
                if (
element != null) return element.children[0].getText();
            }
        }
        
svg.Element.tref.prototype = new svg.Element.TextElementBase;        
        
        
// a element
        
svg.Element.= function(node) {
            
this.base svg.Element.TextElementBase;
            
this.base(node);
            
            
this.hasText true;
            for (var 
i=0i<node.childNodes.lengthi++) {
                if (
node.childNodes[i].nodeType != 3this.hasText false;
            }
            
            
// this might contain text
            
this.text this.hasText node.childNodes[0].nodeValue '';
            
this.getText = function() {
                return 
this.text;
            }        

            
this.baseRenderChildren this.renderChildren;
            
this.renderChildren = function(ctx) {
                if (
this.hasText) {
                    
// render as text element
                    
this.baseRenderChildren(ctx);
                    var 
fontSize = new svg.Property('fontSize'svg.Font.Parse(svg.ctx.font).fontSize);
                    
svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.xthis.fontSize.Length.toPixels('y'), this.this.measureText(ctx), this.y));                    
                }
                else {
                    
// render as temporary group
                    
var = new svg.Element.g();
                    
g.children this.children;
                    
g.parent this;
                    
g.render(ctx);
                }
            }
            
            
this.onclick = function() {
                
window.open(this.attribute('xlink:href').value);
            }
            
            
this.onmousemove = function() {
                
svg.ctx.canvas.style.cursor 'pointer';
            }
        }
        
svg.Element.a.prototype = new svg.Element.TextElementBase;        
        
        
// image element
        
svg.Element.image = function(node) {
            
this.base svg.Element.RenderedElementBase;
            
this.base(node);
            
            
svg.Images.push(this);
            
this.img document.createElement('img');
            
this.loaded false;
            var 
that this;
            
this.img.onload = function() { that.loaded true; }
            
this.img.src this.attribute('xlink:href').value;
            
            
this.renderChildren = function(ctx) {
                var 
this.attribute('x').Length.toPixels('x');
                var 
this.attribute('y').Length.toPixels('y');
                
                var 
width this.attribute('width').Length.toPixels('x');
                var 
height this.attribute('height').Length.toPixels('y');            
                if (
width == || height == 0) return;
            
                
ctx.save();
                
ctx.translate(xy);
                
svg.AspectRatio(ctx,
                                
this.attribute('preserveAspectRatio').value,
                                
width,
                                
this.img.width,
                                
height,
                                
this.img.height,
                                
0,
                                
0);    
                
ctx.drawImage(this.img00);            
                
ctx.restore();
            }
        }
        
svg.Element.image.prototype = new svg.Element.RenderedElementBase;
        
        
// group element
        
svg.Element.= function(node) {
            
this.base svg.Element.RenderedElementBase;
            
this.base(node);
            
            
this.getBoundingBox = function() {
                var 
bb = new svg.BoundingBox();
                for (var 
i=0i<this.children.lengthi++) {
                    
bb.addBoundingBox(this.children[i].getBoundingBox());
                }
                return 
bb;
            };
        }
        
svg.Element.g.prototype = new svg.Element.RenderedElementBase;

        
// symbol element
        
svg.Element.symbol = function(node) {
            
this.base svg.Element.RenderedElementBase;
            
this.base(node);
            
            
this.baseSetContext this.setContext;
            
this.setContext = function(ctx) {        
                
this.baseSetContext(ctx);
                
                
// viewbox
                
if (this.attribute('viewBox').hasValue()) {                
                    var 
viewBox svg.ToNumberArray(this.attribute('viewBox').value);
                    var 
minX viewBox[0];
                    var 
minY viewBox[1];
                    
width viewBox[2];
                    
height viewBox[3];
                    
                    
svg.AspectRatio(ctx,
                                    
this.attribute('preserveAspectRatio').value
                                    
this.attribute('width').Length.toPixels('x'),
                                    
width,
                                    
this.attribute('height').Length.toPixels('y'),
                                    
height,
                                    
minX,
                                    
minY);

                    
svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);                        
                }
            }            
        }
        
svg.Element.symbol.prototype = new svg.Element.RenderedElementBase;        
            
        
// style element
        
svg.Element.style = function(node) { 
            
this.base svg.Element.ElementBase;
            
this.base(node);
            
            
// text, or spaces then CDATA
            
var css node.childNodes[0].nodeValue + (node.childNodes.length node.childNodes[1].nodeValue '');
            
css css.replace(/(/*([^*]|[rn]|(*+([^*/]|[rn])))**+/)|(^[s]*//.*)/gm, ''); // remove comments
            
css svg.compressSpaces(css); // replace whitespace
            
var cssDefs css.split('}');
            for (var 
i=0i<cssDefs.lengthi++) {
                if (
svg.trim(cssDefs[i]) != '') {
                    var 
cssDef cssDefs[i].split('{');
                    var 
cssClasses cssDef[0].split(',');
                    var 
cssProps cssDef[1].split(';');
                    for (var 
j=0j<cssClasses.lengthj++) {
                        var 
cssClass svg.trim(cssClasses[j]);
                        if (
cssClass != '') {
                            var 
props = {};
                            for (var 
k=0k<cssProps.lengthk++) {
                                var 
prop cssProps[k].indexOf(':');
                                var 
name cssProps[k].substr(0prop);
                                var 
value cssProps[k].substr(prop 1cssProps[k].length prop);
                                if (
name != null && value != null) {
                                    
props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value));
                                }
                            }
                            
svg.Styles[cssClass] = props;
                            if (
cssClass == '@font-face') {
                                var 
fontFamily props['font-family'].value.replace(/"/g,'');
                                var srcs = props['src'].value.split(',');
                                for (var s=0; s<srcs.length; s++) {
                                    if (srcs[s].indexOf('format("
svg")') > 0) {
                                        var urlStart = srcs[s].indexOf('url');
                                        var urlEnd = srcs[s].indexOf(')', urlStart);
                                        var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
                                        var doc = svg.parseXml(svg.ajax(url));
                                        var fonts = doc.getElementsByTagName('font');
                                        for (var f=0; f<fonts.length; f++) {
                                            var font = svg.CreateElement(fonts[f]);
                                            svg.Definitions[fontFamily] = font;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        svg.Element.style.prototype = new svg.Element.ElementBase;
        
        // use element 
        svg.Element.use = function(node) {
            this.base = svg.Element.RenderedElementBase;
            this.base(node);
            
            this.baseSetContext = this.setContext;
            this.setContext = function(ctx) {
                this.baseSetContext(ctx);
                if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').Length.toPixels('x'), 0);
                if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').Length.toPixels('y'));
            }
            
            this.getDefinition = function() {
                var element = this.attribute('xlink:href').Definition.getDefinition();
                if (this.attribute('width').hasValue()) element.attribute('width', true).value = this.attribute('width').value;
                if (this.attribute('height').hasValue()) element.attribute('height', true).value = this.attribute('height').value;
                return element;
            }
            
            this.path = function(ctx) {
                var element = this.getDefinition();
                if (element != null) element.path(ctx);
            }
            
            this.renderChildren = function(ctx) {
                var element = this.getDefinition();
                if (element != null) element.render(ctx);
            }
        }
        svg.Element.use.prototype = new svg.Element.RenderedElementBase;
        
        // mask element
        svg.Element.mask = function(node) {
            this.base = svg.Element.ElementBase;
            this.base(node);
                        
            this.apply = function(ctx, element) {
                // render as temp svg    
                var x = this.attribute('x').Length.toPixels('x');
                var y = this.attribute('y').Length.toPixels('y');
                var width = this.attribute('width').Length.toPixels('x');
                var height = this.attribute('height').Length.toPixels('y');
                
                // temporarily remove mask to avoid recursion
                var mask = element.attribute('mask').value;
                element.attribute('mask').value = '';
                
                    var cMask = document.createElement('canvas');
                    cMask.width = x + width;
                    cMask.height = y + height;
                    var maskCtx = cMask.getContext('2d');
                    this.renderChildren(maskCtx);
                
                    var c = document.createElement('canvas');
                    c.width = x + width;
                    c.height = y + height;
                    var tempCtx = c.getContext('2d');
                    element.render(tempCtx);
                    tempCtx.globalCompositeOperation = 'destination-in';
                    tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat');
                    tempCtx.fillRect(0, 0, x + width, y + height);
                    
                    ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat');
                    ctx.fillRect(0, 0, x + width, y + height);
                    
                // reassign mask
                element.attribute('mask').value = mask;    
            }
            
            this.render = function(ctx) {
                // NO RENDER
            }
        }
        svg.Element.mask.prototype = new svg.Element.ElementBase;
        
        // clip element
        svg.Element.clipPath = function(node) {
            this.base = svg.Element.ElementBase;
            this.base(node);
            
            this.apply = function(ctx) {
                for (var i=0; i<this.children.length; i++) {
                    if (this.children[i].path) {
                        this.children[i].path(ctx);
                        ctx.clip();
                    }
                }
            }
            
            this.render = function(ctx) {
                // NO RENDER
            }
        }
        svg.Element.clipPath.prototype = new svg.Element.ElementBase;

        // filters
        svg.Element.filter = function(node) {
            this.base = svg.Element.ElementBase;
            this.base(node);
                        
            this.apply = function(ctx, element) {
                // render as temp svg    
                var bb = element.getBoundingBox();
                var x = this.attribute('x').Length.toPixels('x');
                var y = this.attribute('y').Length.toPixels('y');
                if (x == 0 || y == 0) {
                    x = bb.x1;
                    y = bb.y1;
                }
                var width = this.attribute('width').Length.toPixels('x');
                var height = this.attribute('height').Length.toPixels('y');
                if (width == 0 || height == 0) {
                    width = bb.width();
                    height = bb.height();
                }
                
                // temporarily remove filter to avoid recursion
                var filter = element.style('filter').value;
                element.style('filter').value = '';
                
                // max filter distance
                var extraPercent = .20;
                var px = extraPercent * width;
                var py = extraPercent * height;
                
                var c = document.createElement('canvas');
                c.width = width + 2*px;
                c.height = height + 2*py;
                var tempCtx = c.getContext('2d');
                tempCtx.translate(-x + px, -y + py);
                element.render(tempCtx);
            
                // apply filters
                for (var i=0; i<this.children.length; i++) {
                    this.children[i].apply(tempCtx, 0, 0, width + 2*px, height + 2*py);
                }
                
                // render on me
                ctx.drawImage(c, 0, 0, width + 2*px, height + 2*py, x - px, y - py, width + 2*px, height + 2*py);
                
                // reassign filter
                element.style('filter', true).value = filter;    
            }
            
            this.render = function(ctx) {
                // NO RENDER
            }        
        }
        svg.Element.filter.prototype = new svg.Element.ElementBase;
        
        svg.Element.feGaussianBlur = function(node) {
            this.base = svg.Element.ElementBase;
            this.base(node);    
            
            function make_fgauss(sigma) {
                sigma = Math.max(sigma, 0.01);                  
                var len = Math.ceil(sigma * 4.0) + 1;                     
                mask = [];                               
                for (var i = 0; i < len; i++) {                             
                    mask[i] = Math.exp(-0.5 * (i / sigma) * (i / sigma));                                           
                }                                                           
                return mask; 
            }
            
            function normalize(mask) {
                var sum = 0;
                for (var i = 1; i < mask.length; i++) {
                    sum += Math.abs(mask[i]);
                }
                sum = 2 * sum + Math.abs(mask[0]);
                for (var i = 0; i < mask.length; i++) {
                    mask[i] /= sum;
                }
                return mask;
            }
            
            function convolve_even(src, dst, mask, width, height) {
              for (var y = 0; y < height; y++) {
                for (var x = 0; x < width; x++) {
                  var a = imGet(src, x, y, width, height, 3)/255;
                  for (var rgba = 0; rgba < 4; rgba++) {                      
                      var sum = mask[0] * (a==0?255:imGet(src, x, y, width, height, rgba)) * (a==0||rgba==3?1:a);
                      for (var i = 1; i < mask.length; i++) {
                        var a1 = imGet(src, Math.max(x-i,0), y, width, height, 3)/255;
                        var a2 = imGet(src, Math.min(x+i, width-1), y, width, height, 3)/255;
                        sum += mask[i] * 
                          ((a1==0?255:imGet(src, Math.max(x-i,0), y, width, height, rgba)) * (a1==0||rgba==3?1:a1) + 
                           (a2==0?255:imGet(src, Math.min(x+i, width-1), y, width, height, rgba)) * (a2==0||rgba==3?1:a2));
                      }
                      imSet(dst, y, x, height, width, rgba, sum);
                  }              
                }
              }
            }        

            function imGet(img, x, y, width, height, rgba) {
                return img[y*width*4 + x*4 + rgba];
            }
            
            function imSet(img, x, y, width, height, rgba, val) {
                img[y*width*4 + x*4 + rgba] = val;
            }
                        
            function blur(ctx, width, height, sigma)
            {
                var srcData = ctx.getImageData(0, 0, width, height);
                var mask = make_fgauss(sigma);
                mask = normalize(mask);
                tmp = [];
                convolve_even(srcData.data, tmp, mask, width, height);
                convolve_even(tmp, srcData.data, mask, height, width);
                ctx.clearRect(0, 0, width, height);
                ctx.putImageData(srcData, 0, 0);
            }            
        
            this.apply = function(ctx, x, y, width, height) {
                // assuming x==0 && y==0 for now
                blur(ctx, width, height, this.attribute('stdDeviation').numValue());
            }
        }
        svg.Element.filter.prototype = new svg.Element.feGaussianBlur;
        
        // title element, do nothing
        svg.Element.title = function(node) {
        }
        svg.Element.title.prototype = new svg.Element.ElementBase;

        // desc element, do nothing
        svg.Element.desc = function(node) {
        }
        svg.Element.desc.prototype = new svg.Element.ElementBase;        
        
        svg.Element.MISSING = function(node) {
            console.log('ERROR: Element '' + node.nodeName + '' not yet implemented.');
        }
        svg.Element.MISSING.prototype = new svg.Element.ElementBase;
        
        // element factory
        svg.CreateElement = function(node) {    
            var className = node.nodeName.replace(/^[^:]+:/,''); // remove namespace
            className = className.replace(/-/g,''); // remove dashes
            var e = null;
            if (typeof(svg.Element[className]) != 'undefined') {
                e = new svg.Element[className](node);
            }
            else {
                e = new svg.Element.MISSING(node);
            }

            e.type = node.nodeName;
            return e;
        }
                
        // load from url
        svg.load = function(ctx, url) {
            svg.loadXml(ctx, svg.ajax(url));
        }
        
        // load from xml
        svg.loadXml = function(ctx, xml) {
            svg.loadXmlDoc(ctx, svg.parseXml(xml));
        }
        
        svg.loadXmlDoc = function(ctx, dom) {
            svg.init(ctx);
            
            var mapXY = function(p) {
                var e = ctx.canvas;
                while (e) {
                    p.x -= e.offsetLeft;
                    p.y -= e.offsetTop;
                    e = e.offsetParent;
                }
                if (window.scrollX) p.x += window.scrollX;
                if (window.scrollY) p.y += window.scrollY;
                return p;
            }
            
            // bind mouse
            if (svg.opts['ignoreMouse'] != true) {
                ctx.canvas.onclick = function(e) {
                    var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
                    svg.Mouse.onclick(p.x, p.y);
                };
                ctx.canvas.onmousemove = function(e) {
                    var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
                    svg.Mouse.onmousemove(p.x, p.y);
                };
            }
        
            var e = svg.CreateElement(dom.documentElement);
            e.root = true;
                    
            // render loop
            var isFirstRender = true;
            var draw = function() {
                svg.ViewPort.Clear();
                if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight);
            
                if (svg.opts['ignoreDimensions'] != true) {
                    // set canvas size
                    if (e.style('width').hasValue()) {
                        ctx.canvas.width = e.style('width').Length.toPixels('x');
                        ctx.canvas.style.width = ctx.canvas.width + 'px';
                    }
                    if (e.style('height').hasValue()) {
                        ctx.canvas.height = e.style('height').Length.toPixels('y');
                        ctx.canvas.style.height = ctx.canvas.height + 'px';
                    }
                }
                var cWidth = ctx.canvas.clientWidth || ctx.canvas.width;
                var cHeight = ctx.canvas.clientHeight || ctx.canvas.height;
                svg.ViewPort.SetCurrent(cWidth, cHeight);        
                
                if (svg.opts != null && svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX'];
                if (svg.opts != null && svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY'];
                if (svg.opts != null && svg.opts['scaleWidth'] != null && svg.opts['scaleHeight'] != null) {
                    var xRatio = 1, yRatio = 1;
                    if (e.attribute('width').hasValue()) xRatio = e.attribute('width').Length.toPixels('x') / svg.opts['scaleWidth'];
                    if (e.attribute('height').hasValue()) yRatio = e.attribute('height').Length.toPixels('y') / svg.opts['scaleHeight'];
                
                    e.attribute('width', true).value = svg.opts['scaleWidth'];
                    e.attribute('height', true).value = svg.opts['scaleHeight'];            
                    e.attribute('viewBox', true).value = '0 0 ' + (cWidth * xRatio) + ' ' + (cHeight * yRatio);
                    e.attribute('preserveAspectRatio', true).value = 'none';
                }
            
                // clear and render
                if (svg.opts['ignoreClear'] != true) {
                    ctx.clearRect(0, 0, cWidth, cHeight);
                }
                e.render(ctx);
                if (isFirstRender) {
                    isFirstRender = false;
                    if (svg.opts != null && typeof(svg.opts['renderCallback']) == 'function') svg.opts['renderCallback']();
                }            
            }
            
            var waitingForImages = true;
            if (svg.ImagesLoaded()) {
                waitingForImages = false;
                draw();
            }
            svg.intervalID = setInterval(function() { 
                var needUpdate = false;
                
                if (waitingForImages && svg.ImagesLoaded()) {
                    waitingForImages = false;
                    needUpdate = true;
                }
            
                // need update from mouse events?
                if (svg.opts['ignoreMouse'] != true) {
                    needUpdate = needUpdate | svg.Mouse.hasEvents();
                }
            
                // need update from animations?
                if (svg.opts['ignoreAnimation'] != true) {
                    for (var i=0; i<svg.Animations.length; i++) {
                        needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE);
                    }
                }
                
                // need update from redraw?
                if (svg.opts != null && typeof(svg.opts['forceRedraw']) == 'function') {
                    if (svg.opts['forceRedraw']() == true) needUpdate = true;
                }
                
                // render if needed
                if (needUpdate) {
                    draw();                
                    svg.Mouse.runEvents(); // run and clear our events
                }
            }, 1000 / svg.FRAMERATE);
        }
        
        svg.stop = function() {
            if (svg.intervalID) {
                clearInterval(svg.intervalID);
            }
        }
        
        svg.Mouse = new (function() {
            this.events = [];
            this.hasEvents = function() { return this.events.length != 0; }
        
            this.onclick = function(x, y) {
                this.events.push({ type: 'onclick', x: x, y: y, 
                    run: function(e) { if (e.onclick) e.onclick(); }
                });
            }
            
            this.onmousemove = function(x, y) {
                this.events.push({ type: 'onmousemove', x: x, y: y,
                    run: function(e) { if (e.onmousemove) e.onmousemove(); }
                });
            }            
            
            this.eventElements = [];
            
            this.checkPath = function(element, ctx) {
                for (var i=0; i<this.events.length; i++) {
                    var e = this.events[i];
                    if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element;
                }
            }
            
            this.checkBoundingBox = function(element, bb) {
                for (var i=0; i<this.events.length; i++) {
                    var e = this.events[i];
                    if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element;
                }            
            }
            
            this.runEvents = function() {
                svg.ctx.canvas.style.cursor = '';
                
                for (var i=0; i<this.events.length; i++) {
                    var e = this.events[i];
                    var element = this.eventElements[i];
                    while (element) {
                        e.run(element);
                        element = element.parent;
                    }
                }        
            
                // done running, clear
                this.events = []; 
                this.eventElements = [];
            }
        });
        
        return svg;
    }
})();

if (CanvasRenderingContext2D) {
    CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh) {
        canvg(this.canvas, s, { 
            ignoreMouse: true, 
            ignoreAnimation: true, 
            ignoreDimensions: true, 
            ignoreClear: true, 
            offsetX: dx, 
            offsetY: dy, 
            scaleWidth: dw, 
            scaleHeight: dh
        });
    }
}/**
 * @license Highcharts JS v3.0.4 (2013-08-02)
 * CanVGRenderer Extension module
 *
 * (c) 2011-2012 Torstein Hønsi, Erik Olsson
 *
 * License: www.highcharts.com/license
 */

// JSLint options:
/*global Highcharts */

(function (Highcharts) { // encapsulate
    var UNDEFINED,
        DIV = 'div',
        ABSOLUTE = 'absolute',
        RELATIVE = 'relative',
        HIDDEN = 'hidden',
        VISIBLE = 'visible',
        PX = 'px',
        css = Highcharts.css,
        CanVGRenderer = Highcharts.CanVGRenderer,
        SVGRenderer = Highcharts.SVGRenderer,
        extend = Highcharts.extend,
        merge = Highcharts.merge,
        addEvent = Highcharts.addEvent,
        createElement = Highcharts.createElement,
        discardElement = Highcharts.discardElement;

    // Extend CanVG renderer on demand, inherit from SVGRenderer
    extend(CanVGRenderer.prototype, SVGRenderer.prototype);

    // Add additional functionality:
    extend(CanVGRenderer.prototype, {
        create: function (chart, container, chartWidth, chartHeight) {
            this.setContainer(container, chartWidth, chartHeight);
            this.configure(chart);
        },
        setContainer: function (container, chartWidth, chartHeight) {
            var containerStyle = container.style,
                containerParent = container.parentNode,
                containerLeft = containerStyle.left,
                containerTop = containerStyle.top,
                containerOffsetWidth = container.offsetWidth,
                containerOffsetHeight = container.offsetHeight,
                canvas,
                initialHiddenStyle = { visibility: HIDDEN, position: ABSOLUTE };

            this.init.apply(this, [container, chartWidth, chartHeight]);

            // add the canvas above it
            canvas = createElement('canvas', {
                width: containerOffsetWidth,
                height: containerOffsetHeight
            }, {
                position: RELATIVE,
                left: containerLeft,
                top: containerTop
            }, container);
            this.canvas = canvas;

            // Create the tooltip line and div, they are placed as siblings to
            // the container (and as direct childs to the div specified in the html page)
            this.ttLine = createElement(DIV, null, initialHiddenStyle, containerParent);
            this.ttDiv = createElement(DIV, null, initialHiddenStyle, containerParent);
            this.ttTimer = UNDEFINED;

            // Move away the svg node to a new div inside the container's parent so we can hide it.
            var hiddenSvg = createElement(DIV, {
                width: containerOffsetWidth,
                height: containerOffsetHeight
            }, {
                visibility: HIDDEN,
                left: containerLeft,
                top: containerTop
            }, containerParent);
            this.hiddenSvg = hiddenSvg;
            hiddenSvg.appendChild(this.box);
        },

        /**
         * Configures the renderer with the chart. Attach a listener to the event tooltipRefresh.
         **/
        configure: function (chart) {
            var renderer = this,
                options = chart.options.tooltip,
                borderWidth = options.borderWidth,
                tooltipDiv = renderer.ttDiv,
                tooltipDivStyle = options.style,
                tooltipLine = renderer.ttLine,
                padding = parseInt(tooltipDivStyle.padding, 10);

            // Add border styling from options to the style
            tooltipDivStyle = merge(tooltipDivStyle, {
                padding: padding + PX,
                'background-color': options.backgroundColor,
                'border-style': 'solid',
                'border-width': borderWidth + PX,
                'border-radius': options.borderRadius + PX
            });

            // Optionally add shadow
            if (options.shadow) {
                tooltipDivStyle = merge(tooltipDivStyle, {
                    'box-shadow': '1px 1px 3px gray', // w3c
                    '-webkit-box-shadow': '1px 1px 3px gray' // webkit
                });
            }
            css(tooltipDiv, tooltipDivStyle);

            // Set simple style on the line
            css(tooltipLine, {
                'border-left': '1px solid darkgray'
            });

            // This event is triggered when a new tooltip should be shown
            addEvent(chart, 'tooltipRefresh', function (args) {
                var chartContainer = chart.container,
                    offsetLeft = chartContainer.offsetLeft,
                    offsetTop = chartContainer.offsetTop,
                    position;

                // Set the content of the tooltip
                tooltipDiv.innerHTML = args.text;

                // Compute the best position for the tooltip based on the divs size and container size.
                position = chart.tooltip.getPosition(tooltipDiv.offsetWidth, tooltipDiv.offsetHeight, {plotX: args.x, plotY: args.y});

                css(tooltipDiv, {
                    visibility: VISIBLE,
                    left: position.x + PX,
                    top: position.y + PX,
                    'border-color': args.borderColor
                });

                // Position the tooltip line
                css(tooltipLine, {
                    visibility: VISIBLE,
                    left: offsetLeft + args.x + PX,
                    top: offsetTop + chart.plotTop + PX,
                    height: chart.plotHeight  + PX
                });

                // This timeout hides the tooltip after 3 seconds
                // First clear any existing timer
                if (renderer.ttTimer !== UNDEFINED) {
                    clearTimeout(renderer.ttTimer);
                }

                // Start a new timer that hides tooltip and line
                renderer.ttTimer = setTimeout(function () {
                    css(tooltipDiv, { visibility: HIDDEN });
                    css(tooltipLine, { visibility: HIDDEN });
                }, 3000);
            });
        },

        /**
         * Extend SVGRenderer.destroy to also destroy the elements added by CanVGRenderer.
         */
        destroy: function () {
            var renderer = this;

            // Remove the canvas
            discardElement(renderer.canvas);

            // Kill the timer
            if (renderer.ttTimer !== UNDEFINED) {
                clearTimeout(renderer.ttTimer);
            }

            // Remove the divs for tooltip and line
            discardElement(renderer.ttLine);
            discardElement(renderer.ttDiv);
            discardElement(renderer.hiddenSvg);

            // Continue with base class
            return SVGRenderer.prototype.destroy.apply(renderer);
        },

        /**
         * Take a color and return it if it's a string, do not make it a gradient even if it is a
         * gradient. Currently canvg cannot render gradients (turns out black),
         * see: http://code.google.com/p/canvg/issues/detail?id=104
         *
         * @param {Object} color The color or config object
         */
        color: function (color, elem, prop) {
            if (color && color.linearGradient) {
                // Pick the end color and forward to base implementation
                color = color.stops[color.stops.length - 1][1];
            }
            return SVGRenderer.prototype.color.call(this, color, elem, prop);
        },

        /**
         * Draws the SVG on the canvas or adds a draw invokation to the deferred list.
         */
        draw: function () {
            var renderer = this;
            window.canvg(renderer.canvas, renderer.hiddenSvg.innerHTML);
        }
    });
}(Highcharts));
?>
Онлайн: 1
Реклама