From: wout Date: Wed, 29 Jan 2014 20:20:58 +0000 (+0100) Subject: Added SVG.PathArray, updated data() and bumped to v1.0rc1 X-Git-Tag: 1.0.0-rc.4~42 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5b6736666dd85f8a063b87e6c6483ea47be4391c;p=svg.js.git Added SVG.PathArray, updated data() and bumped to v1.0rc1 --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 172fc5c..0fbf26c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# v1.0rc1 (30/01/2014) + +- added `SVG.PathArray` for real path transformations +- removed `unbiased` system for paths +- added caching on `SVG.PointArray` and `SVG.PathArray` for animations +- enabled proportional resizing on `size()` method with `null` for either `width` or `height` values +- moved data module to separate file +- `data()` method now accepts object for for multiple key / value assignments + # v0.38 (28/01/2014) - added `loop()` method to `SVG.FX` diff --git a/README.md b/README.md index 848a134..83fa1d4 100644 --- a/README.md +++ b/README.md @@ -279,20 +279,9 @@ _Javascript inheritance stack: `SVG.Polygon` < `SVG.Shape` < `SVG.Element`_ The path string is similar to the polygon string but much more complex in order to support curves: ```javascript -// path('path data') var path = draw.path('M10,20L30,40') ``` -For more details on path data strings, please refer to the SVG documentation: -http://www.w3.org/TR/SVG/paths.html#PathData - -Note that paths will always be positioned at x=0, y=0 on creation. This is to make the unified `move()` api possible. Svg.js assumes you are creating a path to move it afterwards. If you need to constantly update your path you probably don't want to use the `move()` method at all. In that case you can create an "unbiased" path like so: - -```javascript -// path('path data', unbiased) -var path = draw.path('M10,20L30,40', true) -``` - Paths can be updated using the `plot()` method: ```javascript @@ -301,6 +290,9 @@ path.plot('M100,200L300,400') _Javascript inheritance stack: `SVG.Path` < `SVG.Shape` < `SVG.Element`_ +For more details on path data strings, please refer to the SVG documentation: +http://www.w3.org/TR/SVG/paths.html#PathData + ### Image When creating images the `width` and `height` values should be defined: @@ -768,6 +760,18 @@ Set the size of an element by a given `width` and `height`: rect.size(200, 300) ``` +Proporional resizing is also possible by leaving out `height`: + +```javascript +rect.size(200) +``` + +Or by passing `null` as the value for `width`: + +```javascript +rect.size(null, 200) +``` + Same as with `move()` the size of an element could be set by using `attr()`. But because every type of element is handles its size differently the `size()` method is much more convenient. @@ -921,7 +925,7 @@ path.bbox() This will return an instance of `SVG.BBox` containing the following values: ```javascript -{ height: 20, width: 20, y: 20, x: 10, cx: 30, cy: 20 } +{ width: 20, height: 20, x: 10, y: 20, cx: 30, cy: 20 } ``` As opposed to the native `getBBox()` method any translations used with the `transform()` method will be taken into account. @@ -955,42 +959,6 @@ rect.inside(60, 70) //-> returns true Note: the `x` and `y` positions are tested against the relative position of the element. Any offset on the parent element is not taken into account. -## Colors - -Svg.js has a dedicated color module handling different types of colors. Accepted values are: - -- hex string; three based (e.g. #f06) or six based (e.g. #ff0066) `new SVG.Color('#f06')` -- rgb string; e.g. rgb(255, 0, 102) `new SVG.Color('rgb(255, 0, 102)')` -- rgb object; e.g. { r: 255, g: 0, b: 102 } `new SVG.Color({ r: 255, g: 0, b: 102 })` - -Note that when working with objects is important to provide all three values every time. - -The `SVG.Color` instance has a few methods of its own. - -### toHex() -Get hex value: - -```javascript -color.toHex() //-> returns '#ff0066' -``` - -### toRgb() -Get rgb string value: - -```javascript -color.toRgb() //-> returns 'rgb(255,0,102)' -``` - -### brightness() -Get the brightness of a color: - -```javascript -color.brightness() //-> returns 0.344 -``` - -This is the perceived brighness where `0` is black and `1` is white. - - ## Animating elements @@ -1312,7 +1280,7 @@ Or: rect.masker.remove() ``` -### References +### masker For your convenience, the masking element is also referenced in the masked element. This can be useful in case you want to change the mask: ```javascript @@ -1323,32 +1291,54 @@ _This functionality requires the mask.js module which is included in the default ## Clipping elements -Clipping elements is exactly the same as masking elements: +Clipping elements works exactly the same as masking elements. The only difference is that clipped elements will adopt the geometry of the clipping element. Therefore events are only triggered when entering the clipping element whereas with masks the masked element triggers the event. Another difference is that masks can define opacity with their fill color and clipPaths don't. +### clipWith() ```javascript -var ellipse = draw.ellipse(80, 40).move(10, 10).fill({ color: '#fff' }) +var ellipse = draw.ellipse(80, 40).move(10, 10) rect.clipWith(ellipse) ``` -Similar to masking, the clipping element can be referenced in the clipped element: +### clip() +Clip multiple elements: ```javascript -rect.clipper.move(20,30) +var ellipse = draw.ellipse(80, 40).move(10, 10) +var text = draw.text('SVG.JS').move(10, 10).font({ size: 36 }) + +var clip = draw.clip().add(text).add(ellipse) + +rect.clipWith(clip) ``` +### unclip() Unclipping the elements can be done with the `unclip()` method: ```javascript rect.unclip() ``` -Removing the clipPath alltogether will also `unclip()` all clipped elements: +### remove() +Removing the clip alltogether will also `unclip()` all clipped elements as well: + +```javascript +clip.remove() +``` + +Or: ```javascript rect.clipper.remove() ``` +### clipper +For your convenience, the clipping element is also referenced in the clipped element. This can be useful in case you want to change the clipPath: + +```javascript +rect.clipper.move(10, 10) +``` + _This functionality requires the clip.js module which is included in the default distribution._ @@ -1602,6 +1592,94 @@ gradient.fill() //-> returns 'url(#SvgjsGradient1234)' _This functionality requires the gradient.js module which is included in the default distribution._ +## Data + +### Setting +The `data()` method allows you to bind arbitrary objects, strings and numbers to SVG elements: + +```javascript +rect.data('key', { value: { data: 0.3 }}) +``` + +Or set multiple values at once: + +```javascript +rect.data({ + forbidden: 'fruit' +, multiple: { + values: 'in' + , an: 'object' + } +}) +``` + +### Getting +Fetching the values is similar to the `attr()` method: + +```javascript +rect.data('key') +``` + +### Removing +Removing the data altogether: + +```javascript +rect.data('key', null) +``` + +### Sustaining data types +Your values will always be stored as JSON and in some cases this might not be desirable. If you want to store the value as-is, just pass true as the third argument: + +```javascript +rect.data('key', 'value', true) +``` + + +## Memory + +### remember() +Storing data in-memory is very much like setting attributes: + +```javascript +rect.remember('oldBBox', rect.bbox()) +``` + +Multiple values can also be remembered at once: + +```javascript +rect.remember({ + oldFill: rect.attr('fill') +, oldStroke: rect.attr('stroke') +}) +``` + +To retrieve a memory + +```javascript +rect.remember('oldBBox') +``` + + +### forget() +Erasing a single memory: + +```javascript +rect.forget('oldBBox') +``` + +Or erasing multiple memories at once: + + +```javascript +rect.forget('oldFill', 'oldStroke') +``` + +And finally, just erasing the whole memory: + +```javascript +rect.forget() +``` + ## Events ### Basic events @@ -1653,79 +1731,173 @@ Obviously unbinding is practically the same: SVG.off(window, 'click', click) ``` -## Data -The `data()` method allows you to bind arbitrary objects, strings and numbers to SVG elements: +## Colors + +Svg.js has a dedicated color module handling different types of colors. Accepted values are: + +- hex string; three based (e.g. #f06) or six based (e.g. #ff0066) `new SVG.Color('#f06')` +- rgb string; e.g. rgb(255, 0, 102) `new SVG.Color('rgb(255, 0, 102)')` +- rgb object; e.g. { r: 255, g: 0, b: 102 } `new SVG.Color({ r: 255, g: 0, b: 102 })` + +Note that when working with objects is important to provide all three values every time. + +The `SVG.Color` instance has a few methods of its own. + +### toHex() +Get hex value: ```javascript -rect.data('key', { value: { data: 0.3 }}) +color.toHex() //-> returns '#ff0066' ``` -Fetching the values is similar to the `attr()` method: +### toRgb() +Get rgb string value: ```javascript -rect.data('key') +color.toRgb() //-> returns 'rgb(255,0,102)' ``` -Removing the data altogether: +### brightness() +Get the brightness of a color: ```javascript -rect.data('key', null) +color.brightness() //-> returns 0.344 ``` -Your values will always be stored as JSON and in some cases this might not be desirable. If you want to store the value as-is, just pass true as the third argument: +This is the perceived brighness where `0` is black and `1` is white. + + +## Arrays +In svg.js every value list string can be cast and passed as an array. This makes writing them more convenient but also adds a lot of key functionality to them. + +### SVG.Array +Is for simple, whitespace separated value strings: ```javascript -rect.data('key', 'value', true) +'0.343 0.669 0.119 0 0 0.249 -0.626 0.13 0 0 0.172 0.334 0.111 0 0 0 0 0 1 0' ``` +Can also be passed like this in a more manageable format: -## Memory +```javascript +new SVG.Array([ .343, .669, .119, 0, 0 + , .249, -.626, .130, 0, 0 + , .172, .334, .111, 0, 0 + , .000, .000, .000, 1, -0 ]) +``` -### remember() -Storing data in-memory is very much like setting attributes: +### SVG.PointArray +Is a bit more complex and is used for polyline and polygon elements. This is a poly-point string: ```javascript -rect.remember('oldBBox', rect.bbox()) +'0,0 100,100' ``` -Multiple values can also be remembered at once: +The dynamic representation: ```javascript -rect.remember({ - oldFill: rect.attr('fill') -, oldStroke: rect.attr('stroke') -}) +new SVG.PointArray([[0, 0], [100, 100]]) ``` -To retrieve a memory +Note that every instance of `SVG.Polyline` and `SVG.Polygon` carry a reference to the `SVG.PointArray` instance: ```javascript -rect.remember('oldBBox') +polygon.array //-> returns the SVG.PointArray instance ``` +_`SVG.PointArray` inherits from `SVG.Array`_ -### forget() -Erasing a single memory: +### SVG.PathArray +Path arrays carry object with the representation of the path string: ```javascript -rect.forget('oldBBox') +'M 0 0 L 100 100 z' ``` -Or erasing multiple memories at once: +The dynamic representation: + +```javascript +new SVG.PathArray([ + { type: 'M', x: 0, y: 0 } +, { type: 'L', x: 100, y: 100 } +, { type: 'z' } +]) +``` +Note that every instance of `SVG.Path` carries a reference to the `SVG.PathArray` instance: ```javascript -rect.forget('oldFill', 'oldStroke') +polygon.array //-> returns the SVG.PointArray instance ``` -And finally, just erasing the whole memory: +_`SVG.PathArray` inherits from `SVG.Array`_ + + +### morph() +In order to animate array values the `morph()` method lets you pass a destination value. This can be either the string value, a plain array or an instance of the same type of svg.js array: ```javascript -rect.forget() +var array = new SVG.PointArray([[0, 0], [100, 100]]) +array.morph('100,0 0,100 200,200') ``` +This method will prepare the array ensuring both the source and destination arrays have the same length. + +Note that this method is currently not available on `SVG.PathArray` but will be soon. + +### at() +This method will morph the array to a given position between `0` and `1`. Continuing with the previous example: + +```javascript +array.at(0.27).toString() //-> returns '27,0 73,100 127,127' +``` + +Note that this method is currently not available on `SVG.PathArray` but will be soon. + +### settle() +When morphing is done the `settle()` method will eliminate any transitional points like duplicates: + +```javascript +array.settle() +``` + +Note that this method is currently not available on `SVG.PathArray` but will be soon. + +### move() +Moves geometry of the array with the given `x` and `y` values: + +```javascript +var array = new SVG.PointArray([[0, 0], [100, 100]]) +array.move(33,75) +array.toString() //-> returns '33,75 133,175' +``` + +Note that this method is only available on `SVG.PointArray` and `SVG.PathArray` + +### size() +Resizes geometry of the array by the given `width` and `height` values: + +```javascript +var array = new SVG.PointArray([[0, 0], [100, 100]]) +array.move(100,100).size(222,333) +array.toString() //-> returns '100,100 322,433' +``` + +Note that this method is only available on `SVG.PointArray` and `SVG.PathArray` + +### bbox() +Gets the bounding box of the geometry of the array: + +```javascript +array.bbox() +``` + +Note that this method is only available on `SVG.PointArray` and `SVG.PathArray` + ## Extending functionality + +### SVG.extend Svg.js has a modular structure. It is very easy to add you own methods at different levels. Let's say we want to add a method to all shape types then we would add our method to SVG.Shape: ```javascript @@ -1775,21 +1947,41 @@ SVG.extend(SVG.Ellipse, SVG.Path, SVG.Polygon, { ``` - ## Plugins Here are a few nice plugins that are available for svg.js: -- [svg.draggable.js](https://github.com/wout/svg.draggable.js) to make elements draggable. -- [svg.easing.js](https://github.com/wout/svg.easing.js) for more easing methods on animations. -- [svg.export.js](https://github.com/wout/svg.export.js) export raw SVG. -- [svg.filter.js](https://github.com/wout/svg.filter.js) adding svg filters to elements. -- [svg.foreignobject.js](https://github.com/john-memloom/svg.foreignobject.js) foreignObject implementation (by john-memloom). -- [svg.import.js](https://github.com/wout/svg.import.js) import raw SVG data. -- [svg.math.js](https://github.com/otm/svg.math.js) a math extension (by Nils Lagerkvist). -- [svg.path.js](https://github.com/otm/svg.path.js) for manually drawing paths (by Nils Lagerkvist). -- [svg.pattern.js](https://github.com/wout/svg.pattern.js) add fill patterns. -- [svg.shapes.js](https://github.com/wout/svg.shapes.js) for more polygon based shapes. -- [svg.topath.js](https://github.com/wout/svg.topath.js) to convert any other shape to a path. +### draggable +[svg.draggable.js](https://github.com/wout/svg.draggable.js) to make elements draggable. + +### easing +[svg.easing.js](https://github.com/wout/svg.easing.js) for more easing methods on animations. + +### export +[svg.export.js](https://github.com/wout/svg.export.js) export raw SVG. + +### filter +[svg.filter.js](https://github.com/wout/svg.filter.js) adding svg filters to elements. + +### foreignobject +[svg.foreignobject.js](https://github.com/john-memloom/svg.foreignobject.js) foreignObject implementation (by john-memloom). + +### import +[svg.import.js](https://github.com/wout/svg.import.js) import raw SVG data. + +### math +[svg.math.js](https://github.com/otm/svg.math.js) a math extension (by Nils Lagerkvist). + +### path +[svg.path.js](https://github.com/otm/svg.path.js) for manually drawing paths (by Nils Lagerkvist). + +### pattern +[svg.pattern.js](https://github.com/wout/svg.pattern.js) add fill patterns. + +### shapes +[svg.shapes.js](https://github.com/wout/svg.shapes.js) for more polygon based shapes. + +### topath +[svg.topath.js](https://github.com/wout/svg.topath.js) to convert any other shape to a path. ## Building diff --git a/Rakefile b/Rakefile index db51346..3167010 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ -SVGJS_VERSION = '0.38' +SVGJS_VERSION = 'v1.0rc1' # all available modules in the correct loading order -MODULES = %w[ svg regex default color array pointarray number viewbox bbox rbox element parent container fx event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set memory loader ] +MODULES = %w[ svg regex default color array pointarray patharray arraycache number viewbox bbox rbox element parent container fx event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader ] # how many bytes in a "kilobyte" KILO = 1024 diff --git a/dist/svg.js b/dist/svg.js index cf9159b..aea380f 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -1,4 +1,4 @@ -/* svg.js v0.38 - svg regex default color array pointarray number viewbox bbox rbox element parent container fx event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set memory loader - svgjs.com/license */ +/* svg.js v1.0rc1 - svg regex default color array pointarray patharray arraycache number viewbox bbox rbox element parent container fx event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader - svgjs.com/license */ ;(function() { this.SVG = function(element) { @@ -7,7 +7,8 @@ } // Default namespaces - SVG.ns = 'http://www.w3.org/2000/svg' + SVG.ns = 'http://www.w3.org/2000/svg' + SVG.xmlns = 'http://www.w3.org/2000/xmlns/' SVG.xlink = 'http://www.w3.org/1999/xlink' // Element id sequence @@ -62,6 +63,35 @@ })() if (!SVG.supported) return false + + // Initialize parsing element + SVG.parser = (function() { + /* select document body and create svg element*/ + var body = document.getElementsByTagName('body')[0] || document.getElementsByTagName('svg')[0] + , svg = SVG.create('svg') + , poly = SVG.create('polygon') + , path = SVG.create('path') + + /* make svg element presently invisible to ensure geometry */ + svg.setAttributeNS(SVG.xmlns, 'xmlns:xlink', SVG.xlink) + svg.setAttribute('style', 'opacity:0;position:fixed;left:100%;top:100%') + svg.setAttribute('width', '2') + svg.setAttribute('height', '2') + + /* build node structure */ + body.appendChild(svg) + svg.appendChild(poly) + svg.appendChild(path) + + /* return parser object */ + return { + body: body + , doc: svg + , poly: poly + , path: path + } + + })() SVG.regex = { /* test a given value */ @@ -282,10 +312,8 @@ } // Clean up any duplicate points , settle: function() { - var i, seen = [] - /* find all unique values */ - for (i = this.value.length - 1; i >= 0; i--) + for (var i = 0, il = this.value.length, seen = []; i < il; i++) if (seen.indexOf(this.value[i]) == -1) seen.push(this.value[i]) @@ -406,35 +434,326 @@ } // Get bounding box of points , bbox: function() { - if (this.value.length == 0) - return { x: 0, y: 0, width: 0, height: 0 } + if (this._cachedBBox) return this._cachedBBox + + SVG.parser.poly.setAttribute('points', this.toString()) + + return SVG.parser.poly.getBBox() + } + + }) + + SVG.PathArray = function(array, fallback) { + this.constructor.call(this, array, fallback) + } - var i - , x = this.value[0][0] - , y = this.value[0][1] - , box = { x: x, y: y } + // Inherit from SVG.Array + SVG.PathArray.prototype = new SVG.Array + + SVG.extend(SVG.PathArray, { + // Convert array to string + toString: function() { + for (var s, i = 0, il = this.value.length, array = []; i < il; i++) { + s = [this.value[i].type] + + switch(this.value[i].type) { + case 'H': + s.push(this.value[i].x) + break + case 'V': + s.push(this.value[i].y) + break + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + if (/[QC]/.test(this.value[i].type)) + s.push(this.value[i].x1, this.value[i].y1) + if (/[CS]/.test(this.value[i].type)) + s.push(this.value[i].x2, this.value[i].y2) + + s.push(this.value[i].x, this.value[i].y) + + break + case 'A': + s.push( + this.value[i].rx + , this.value[i].ry + , this.value[i].angle + , this.value[i].largeArcFlag + , this.value[i].sweepFlag + , this.value[i].x + , this.value[i].y + ) + break + } + + /* add to array */ + array.push(s.join(' ')) + } - /* find position */ - for (i = this.value.length - 1; i >= 0; i--) { - if (this.value[i][0] < box.x) - box.x = this.value[i][0] - if (this.value[i][1] < box.y) - box.y = this.value[i][1] - if (this.value[i][0] > x) - x = this.value[i][0] - if (this.value[i][1] > y) - y = this.value[i][1] + return array.join(' ') + } + // Move path string + , move: function(x, y) { + /* get bounding box of current situation */ + var box = this.bbox() + + /* get relative offset */ + x -= box.x + y -= box.y + + if (!isNaN(x) && !isNaN(y)) { + /* move every point */ + for (var i = this.value.length - 1; i >= 0; i--) { + switch (this.value[i].type) { + case 'H': + /* move along x axis only */ + this.value[i].x += x + break + case 'V': + /* move along y axis only */ + this.value[i].y += y + break + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + /* move first point along x and y axes */ + this.value[i].x += x + this.value[i].y += y + + /* move third points along x and y axes */ + if (/[CQ]/.test(this.value[i].type)) { + this.value[i].x1 += x + this.value[i].y1 += y + } + + /* move second points along x and y axes */ + if (/[CS]/.test(this.value[i].type)) { + this.value[i].x2 += x + this.value[i].y2 += y + } + + break + case 'A': + /* only move position values */ + this.value[i].x += x + this.value[i].y += y + break + } + } } - /* calculate size */ - box.width = x - box.x - box.height = y - box.y + return this + } + // Resize path string + , size: function(width, height) { + /* get bounding box of current situation */ + var box = this.bbox() + + /* recalculate position of all points according to new size */ + for (var i = this.value.length - 1; i >= 0; i--) { + switch (this.value[i].type) { + case 'H': + /* move along x axis only */ + this.value[i].x = ((this.value[i].x - box.x) * width) / box.width + box.x + break + case 'V': + /* move along y axis only */ + this.value[i].y = ((this.value[i].y - box.y) * height) / box.height + box.y + break + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + this.value[i].x = ((this.value[i].x - box.x) * width) / box.width + box.x + this.value[i].y = ((this.value[i].y - box.y) * height) / box.height + box.y + + /* move third points along x and y axes */ + if (/[CQ]/.test(this.value[i].type)) { + this.value[i].x1 = ((this.value[i].x1 - box.x) * width) / box.width + box.x + this.value[i].y1 = ((this.value[i].y1 - box.y) * height) / box.height + box.y + } + + /* move second points along x and y axes */ + if (/[CS]/.test(this.value[i].type)) { + this.value[i].x2 = ((this.value[i].x2 - box.x) * width) / box.width + box.x + this.value[i].y2 = ((this.value[i].y2 - box.y) * height) / box.height + box.y + } - return box + break + case 'A': + /* resize radii */ + this.value[i].values.rx = (this.value[i].values.rx * width) / box.width + this.value[i].values.ry = (this.value[i].values.ry * height) / box.height + + /* move position values */ + this.value[i].values.x = ((this.value[i].values.x - box.x) * width) / box.width + box.x + this.value[i].values.y = ((this.value[i].values.y - box.y) * height) / box.height + box.y + break + } + } + + return this + } + // Absolutize and parse path to array + , parse: function(array) { + array = array.valueOf() + + /* if already is an array, no need to parse it */ + if (Array.isArray(array)) return array + + /* prepare for parsing */ + var i, il, x0, y0, x1, y1, x2, y2, s, seg, segs + , x = 0 + , y = 0 + + /* populate working path */ + SVG.parser.path.setAttribute('d', array) + + /* get segments */ + segs = SVG.parser.path.pathSegList + + for (i = 0, il = segs.numberOfItems; i < il; ++i) { + seg = segs.getItem(i) + s = seg.pathSegTypeAsLetter + + if (/[MLHVCSQTA]/.test(s)) { + if ('x' in seg) x = seg.x + if ('y' in seg) y = seg.y + + } else { + if ('x1' in seg) x1 = x + seg.x1 + if ('x2' in seg) x2 = x + seg.x2 + if ('y1' in seg) y1 = y + seg.y1 + if ('y2' in seg) y2 = y + seg.y2 + if ('x' in seg) x += seg.x + if ('y' in seg) y += seg.y + + switch(s){ + case 'm': + segs.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(x, y), i) + break + case 'l': + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(x, y), i) + break + case 'h': + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(x), i) + break + case 'v': + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(y), i) + break + case 'c': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i) + break + case 's': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i) + break + case 'q': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i) + break + case 't': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i) + break + case 'a': + segs.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i) + break + case 'z': + case 'Z': + x = x0 + y = y0 + break + } + } + + /* record the start of a subpath */ + if (/[Mm]/.test(s)) { + x0 = x + y0 = y + } + } + + /* build internal representation */ + array = [] + segs = SVG.parser.path.pathSegList + + for (i = 0, il = segs.numberOfItems; i < il; ++i) { + seg = segs.getItem(i) + s = {} + + switch (seg.pathSegTypeAsLetter) { + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + if (/[QC]/.test(seg.pathSegTypeAsLetter)) { + s.x1 = seg.x1 + s.y1 = seg.y1 + } + + if (/[SC]/.test(seg.pathSegTypeAsLetter)) { + s.x2 = seg.x2 + s.y2 = seg.y2 + } + + break + case 'A': + s = { + r1: seg.r1 + , r2: seg.r2 + , angle: seg.angle + , largeArcFlag: seg.largeArcFlag + , sweepFlag: seg.sweepFlag + } + break + } + + /* make the letter, x and y values accessible as key/values */ + s.type = seg.pathSegTypeAsLetter + s.x = seg.x + s.y = seg.y + + /* store segment */ + array.push(s) + } + + return array + } + // Get bounding box of path + , bbox: function() { + if (this._cachedBBox) return this._cachedBBox + + SVG.parser.path.setAttribute('d', this.toString()) + + return SVG.parser.path.getBBox() } }) + SVG.extend(SVG.PointArray, SVG.PathArray, { + // Cache bbox + cache: function() { + this._cachedBBox = this.uncache().bbox() + + return this + } + // Remove cache + , uncache: function() { + delete this._cachedBBox + return this + } + + }) + SVG.Number = function(value) { /* initialize defaults */ @@ -767,9 +1086,11 @@ } // Set element size to given width and height , size: function(width, height) { + var p = this._proportionalSize(width, height) + return this.attr({ - width: new SVG.Number(width) - , height: new SVG.Number(height) + width: new SVG.Number(p.width) + , height: new SVG.Number(p.height) }) } // Clone element @@ -981,10 +1302,6 @@ if (o.x != 0 || o.y != 0) transform.push('translate(' + new SVG.Number(o.x / o.scaleX) + ' ' + new SVG.Number(o.y / o.scaleY) + ')') - /* add offset translation */ - if (this._offset && this._offset.x != 0 && this._offset.y != 0) - transform.push('translate(' + (-this._offset.x) + ' ' + (-this._offset.y) + ')') - /* update transformations, even if there are none */ if (transform.length == 0) this.node.removeAttribute('transform') @@ -1042,28 +1359,6 @@ return this } - // Store data values on svg nodes - , data: function(a, v, r) { - if (arguments.length < 2) { - try { - return JSON.parse(this.attr('data-' + a)) - } catch(e) { - return this.attr('data-' + a) - } - - } else { - this.attr( - 'data-' + a - , v === null ? - null : - r === true || typeof v === 'string' || typeof v === 'number' ? - v : - JSON.stringify(v) - ) - } - - return this - } // Get bounding box , bbox: function() { return new SVG.BBox(this) @@ -1129,6 +1424,22 @@ return o } + // Private: calculate proportional width and height values when necessary + , _proportionalSize: function(width, height) { + if (width == null || height == null) { + var box = this.bbox() + + if (height == null) + height = box.height / box.width * width + else if (width == null) + width = box.width / box.height * height + } + + return { + width: width + , height: height + } + } }) @@ -1298,10 +1609,10 @@ akeys.push(key) /* make sure morphable elements are scaled, translated and morphed all together */ - if (element.morphArray) { + if (element.morphArray && akeys.indexOf('points') > -1) { /* get destination */ var box - , p = new element.morphArray(fx._plot || element.points.toString()) + , p = new element.morphArray(fx._plot || element.array) /* add size */ if (fx._size) p.size(fx._size.width.to, fx._size.height.to) @@ -1322,7 +1633,7 @@ delete fx._cy delete fx._size - fx._plot = element.points.morph(p) + fx._plot = element.array.morph(p) } } @@ -1352,27 +1663,35 @@ typeof ease == 'function' ? ease(pos) : pos - - /* run all x-position properties */ - if (fx._x) - element.x(fx._at(fx._x, pos)) - else if (fx._cx) - element.cx(fx._at(fx._cx, pos)) - - /* run all y-position properties */ - if (fx._y) - element.y(fx._at(fx._y, pos)) - else if (fx._cy) - element.cy(fx._at(fx._cy, pos)) - - /* run all size properties */ - if (fx._size) - element.size(fx._at(fx._size.width, pos), fx._at(fx._size.height, pos)) - + /* run plot function */ - if (fx._plot) + if (fx._plot) { element.plot(fx._plot.at(pos)) + } else { + if (element.array) + element.array.cache() + + /* run all x-position properties */ + if (fx._x) + element.x(fx._at(fx._x, pos)) + else if (fx._cx) + element.cx(fx._at(fx._cx, pos)) + + /* run all y-position properties */ + if (fx._y) + element.y(fx._at(fx._y, pos)) + else if (fx._cy) + element.cy(fx._at(fx._cy, pos)) + + /* run all size properties */ + if (fx._size) + element.size(fx._at(fx._size.width, pos), fx._at(fx._size.height, pos)) + + if (element.array) + element.array.uncache() + } + /* run all viewbox properties */ if (fx._viewbox) element.viewbox( @@ -2128,7 +2447,7 @@ /* set svg element attributes */ this .attr({ xmlns: SVG.ns, version: '1.1', width: '100%', height: '100%' }) - .attr('xlink', SVG.xlink, SVG.ns) + .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) /* create the node */ this._defs = new SVG.Defs @@ -2311,9 +2630,11 @@ } // Custom size function , size: function(width, height) { + var p = this._proportionalSize(width, height) + return this.attr({ - rx: new SVG.Number(width).divide(2) - , ry: new SVG.Number(height).divide(2) + rx: new SVG.Number(p.width).divide(2) + , ry: new SVG.Number(p.height).divide(2) }) } @@ -2387,7 +2708,9 @@ } // Set line size by width and height , size: function(width, height) { - return this.width(width).height(height) + var p = this._proportionalSize(width, height) + + return this.width(p.width).height(p.height) } // Set path data , plot: function(x1, y1, x2, y2) { @@ -2431,11 +2754,11 @@ morphArray: SVG.PointArray // Plot new path , plot: function(p) { - return this.attr('points', (this.points = new SVG.PointArray(p, [[0,0]]))) + return this.attr('points', (this.array = new SVG.PointArray(p, [[0,0]]))) } // Move by left top corner , move: function(x, y) { - return this.attr('points', this.points.move(x, y)) + return this.attr('points', this.array.move(x, y)) } // Move by left top corner over x-axis , x: function(x) { @@ -2459,7 +2782,9 @@ } // Set element size to given width and height , size: function(width, height) { - return this.attr('points', this.points.size(width, height)) + var p = this._proportionalSize(width, height) + + return this.attr('points', this.array.size(p.width, p.height)) } }) @@ -2477,78 +2802,52 @@ }) - SVG.Path = function(unbiased) { + SVG.Path = function() { this.constructor.call(this, SVG.create('path')) - - this.unbiased = !!unbiased } // Inherit from SVG.Shape SVG.Path.prototype = new SVG.Shape SVG.extend(SVG.Path, { - // Move over x-axis - x: function(x) { - return x == null ? this.bbox().x : this.transform('x', x) + // Plot new poly points + plot: function(p) { + return this.attr('d', (this.array = new SVG.PathArray(p, [{ type:'M',x:0,y:0 }]))) } - // Move over y-axis - , y: function(y) { - return y == null ? this.bbox().y : this.transform('y', y) + // Move by left top corner + , move: function(x, y) { + return this.attr('d', this.array.move(x, y)) } - // Set width of element - , width: function(width) { - var b = this.bbox() - - return width == null ? b.width : this.size(width, b.height) + // Move by left top corner over x-axis + , x: function(x) { + return x == null ? this.bbox().x : this.move(x, this.bbox().y) } - // Set height of element - , height: function(height) { - var b = this.bbox() - - return height == null ? b.height : this.size(b.width, height) + // Move by left top corner over y-axis + , y: function(y) { + return y == null ? this.bbox().y : this.move(this.bbox().x, y) } - // Set the actual size in pixels + // Set element size to given width and height , size: function(width, height) { - var scale = width / this._offset.width + var p = this._proportionalSize(width, height) - return this.transform({ - scaleX: scale - , scaleY: height != null ? height / this._offset.height : scale - }) + return this.attr('d', this.array.size(p.width, p.height)) } - // Set path data - , plot: function(data) { - var x = this.trans.scaleX - , y = this.trans.scaleY - - /* native plot */ - this._plot(data) - - /* store offset */ - this._offset = this.transform({ scaleX: 1, scaleY: 1 }).bbox() - - /* get and store the actual offset of the element */ - if (this.unbiased) { - this._offset.x = this._offset.y = 0 - } else { - this._offset.x -= this.trans.x - this._offset.y -= this.trans.y - } - - return this.transform({ scaleX: x, scaleY: y }) + // Set width of element + , width: function(width) { + return width == null ? this.bbox().width : this.size(width, this.bbox().height) } - // Private: Native plot - , _plot: function(data) { - return this.attr('d', data || 'M0,0') + // Set height of element + , height: function(height) { + return height == null ? this.bbox().height : this.size(this.bbox().width, height) } - + }) // SVG.extend(SVG.Container, { // Create a wrapped path element - path: function(data, unbiased) { - return this.put(new SVG.Path(unbiased)).plot(data) + path: function(d) { + return this.put(new SVG.Path).plot(d) } }) @@ -3113,6 +3412,35 @@ + SVG.extend(SVG.Element, { + // Store data values on svg nodes + data: function(a, v, r) { + if (typeof a == 'object') { + for (v in a) + this.data(v, a[v]) + + } else if (arguments.length < 2) { + try { + return JSON.parse(this.attr('data-' + a)) + } catch(e) { + return this.attr('data-' + a) + } + + } else { + this.attr( + 'data-' + a + , v === null ? + null : + r === true || typeof v === 'string' || typeof v === 'number' ? + v : + JSON.stringify(v) + ) + } + + return this + } + }) + SVG.extend(SVG.Element, { // Remember arbitrary data remember: function(k, v) { diff --git a/dist/svg.min.js b/dist/svg.min.js index 59ee1eb..425f791 100644 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,2 +1,2 @@ -(function(){if(this.SVG=function(t){return SVG.supported?new SVG.Doc(t):void 0},SVG.ns="http://www.w3.org/2000/svg",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];SVG.Set&&SVG.Set.inherit&&SVG.Set.inherit()},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/},SVG.defaults={matrix:"1 0 0 1 0 0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000"},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r},SVG.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},SVG.extend(SVG.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.length=0;t--)-1==e.indexOf(this.value[t])&&e.push(this.value[t]);return this.value=e},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push(this.value[e]+(this.destination[e]-this.value[e])*t);return new SVG.Array(n)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)},split:function(t){return t.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")}}),SVG.PointArray=function(){this.constructor.apply(this,arguments)},SVG.PointArray.prototype=new SVG.Array,SVG.extend(SVG.PointArray,{toString:function(){for(var t=0,e=this.value.length,i=[];e>t;t++)i.push(this.value[t].join(","));return i.join(" ")},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push([this.value[e][0]+(this.destination[e][0]-this.value[e][0])*t,this.value[e][1]+(this.destination[e][1]-this.value[e][1])*t]);return new SVG.PointArray(n)},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;t=this.split(t);for(var e,i=0,n=t.length,r=[];n>i;i++)e=t[i].split(","),r.push([parseFloat(e[0]),parseFloat(e[1])]);return r},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.x;return this},bbox:function(){if(0==this.value.length)return{x:0,y:0,width:0,height:0};var t,e=this.value[0][0],i=this.value[0][1],n={x:e,y:i};for(t=this.value.length-1;t>=0;t--)this.value[t][0]e&&(e=this.value[t][0]),this.value[t][1]>i&&(i=this.value[t][1]);return n.width=e-n.x,n.height=i-n.y,n}}),SVG.Number=function(t){switch(this.value=0,this.unit="",typeof t){case"number":this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;break;case"string":var e=t.match(SVG.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]&&(this.value/=100),this.unit=e[2]);break;default:t instanceof SVG.Number&&(this.value=t.value,this.unit=t.unit)}},SVG.extend(SVG.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:this.value)+this.unit},valueOf:function(){return this.value},to:function(t){return"string"==typeof t&&(this.unit=t),this},plus:function(t){return this.value=this+new SVG.Number(t),this},minus:function(t){return this.plus(-new SVG.Number(t))},times:function(t){return this.value=this*new SVG.Number(t),this},divide:function(t){return this.value=this/new SVG.Number(t),this}}),SVG.ViewBox=function(t){var e,i,n,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);for(n=new SVG.Number(t.width()),r=new SVG.Number(t.height());"%"==n.unit;)s*=n.value,n=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetWidth:t.width());for(;"%"==r.unit;)h*=r.value,r=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetHeight:t.height());this.x=o.x,this.y=o.y,this.width=n*s,this.height=r*h,this.zoom=1,a&&(e=parseFloat(a[0]),i=parseFloat(a[1]),n=parseFloat(a[2]),r=parseFloat(a[3]),this.zoom=this.width/this.height>n/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.RBox,{merge:function(t){var e=new SVG.RBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.Element=function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},SVG.extend(SVG.Element,{x:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){return this.attr({width:new SVG.Number(t),height:new SVG.Number(e)})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return this._isStyle(t)?"text"==t?this.content:"leading"==t&&this.leading?this.leading():this.style(t):(e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e);if("style"==t)return this.style(e);if("x"==t&&Array.isArray(this.lines))for(i=this.lines.length-1;i>=0;i--)this.lines[i].attr(t,e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),SVG.Color.test(e)||SVG.Color.isRgb(e)?e=new SVG.Color(e):"number"==typeof e?e=new SVG.Number(e):Array.isArray(e)&&(e=new SVG.Array(e)),null!=i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),this._isStyle(t)&&("text"==t?this.text(e):"leading"==t&&this.leading?this.leading(e):this.style(t,e),this.rebuild&&this.rebuild(t,e))}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(2>arguments.length)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+" "+this.trans.b+" "+this.trans.c+" "+this.trans.d+" "+this.trans.e+" "+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+" "+(null==t.cx?this.bbox().cx:t.cx)+" "+(null==t.cy?this.bbox().cy:t.cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+" "+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+new SVG.Number(t.x/t.scaleX)+" "+new SVG.Number(t.y/t.scaleY)+")"),this._offset&&0!=this._offset.x&&0!=this._offset.y&&i.push("translate("+-this._offset.x+" "+-this._offset.y+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(2>arguments.length)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;t.length>i;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},data:function(t,e,i){if(2>arguments.length)try{return JSON.parse(this.attr("data-"+t))}catch(n){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:i===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&i.x+i.width>t&&i.y+i.height>e},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},toString:function(){return this.attr("id")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_isStyle:function(t){return"string"==typeof t?SVG.regex.test(t,"isStyle"):!1},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t}}),SVG.Parent=function(t){this.constructor.call(this,t)},SVG.Parent.prototype=new SVG.Element,SVG.extend(SVG.Parent,{children:function(){return this._children||(this._children=[])},add:function(t,e){if(!this.has(t)){if(e=null==e?this.children().length:e,t.parent){var i=t.parent.children().indexOf(t);t.parent.children().splice(i,1)}this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this}return this._defs&&(this.node.removeChild(this._defs.node),this.node.appendChild(this._defs.node)),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.children().indexOf(t)>=0},get:function(t){return this.children()[t]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;n>i;i++)r[i]instanceof SVG.Element&&t.apply(r[i],[i,r]),e&&r[i]instanceof SVG.Container&&r[i].each(t,e);return this},removeElement:function(t){var e=this.children().indexOf(t);return this.children().splice(e,1),this.node.removeChild(t.node),t.parent=null,this},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&this._defs.clear(),this},defs:function(){return this.doc().defs()}}),SVG.Container=function(t){this.constructor.call(this,t)},SVG.Container.prototype=new SVG.Parent,SVG.extend(SVG.Container,{viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}),SVG.FX=function(t){this.target=t},SVG.extend(SVG.FX,{animate:function(t,e,i){var n,r,s,h,o=this.target,a=this;return"object"==typeof t&&(i=t.delay,e=t.ease,t=t.duration),t=null==t?1e3:t,e=e||"<>",a.to=function(t){var i;if(t=0>t?0:t>1?1:t,null==n){n=[];for(h in a.attrs)n.push(h);if(o.morphArray){var u,l=new o.morphArray(a._plot||o.points.toString());a._size&&l.size(a._size.width.to,a._size.height.to),u=l.bbox(),a._x?l.move(a._x.to,u.y):a._cx&&l.move(a._cx.to-u.width/2,u.y),u=l.bbox(),a._y?l.move(u.x,a._y.to):a._cy&&l.move(u.x,a._cy.to-u.height/2),delete a._x,delete a._y,delete a._cx,delete a._cy,delete a._size,a._plot=o.points.morph(l)}}if(null==r){r=[];for(h in a.trans)r.push(h)}if(null==s){s=[];for(h in a.styles)s.push(h)}for(t="<>"==e?-Math.cos(t*Math.PI)/2+.5:">"==e?Math.sin(t*Math.PI/2):"<"==e?-Math.cos(t*Math.PI/2)+1:"-"==e?t:"function"==typeof e?e(t):t,a._x?o.x(a._at(a._x,t)):a._cx&&o.cx(a._at(a._cx,t)),a._y?o.y(a._at(a._y,t)):a._cy&&o.cy(a._at(a._cy,t)),a._size&&o.size(a._at(a._size.width,t),a._at(a._size.height,t)),a._plot&&o.plot(a._plot.at(t)),a._viewbox&&o.viewbox(a._at(a._viewbox.x,t),a._at(a._viewbox.y,t),a._at(a._viewbox.width,t),a._at(a._viewbox.height,t)),i=n.length-1;i>=0;i--)o.attr(n[i],a._at(a.attrs[n[i]],t));for(i=r.length-1;i>=0;i--)o.transform(r[i],a._at(a.trans[r[i]],t));for(i=s.length-1;i>=0;i--)o.style(s[i],a._at(a.styles[s[i]],t));a._during&&a._during.call(o,t,function(e,i){return a._at({from:e,to:i},t)})},"number"==typeof t&&(this.timeout=setTimeout(function(){var n=(new Date).getTime();a.situation={interval:1e3/60,start:n,play:!0,finish:n+t,duration:t},a.render=function(){if(a.situation.play===!0){var n=(new Date).getTime(),r=n>a.situation.finish?1:(n-a.situation.start)/t;a.to(r),n>a.situation.finish?(a._plot&&o.plot(new SVG.PointArray(a._plot.destination).settle()),a._loop===!0||"number"==typeof a._loop&&a._loop>1?("number"==typeof a._loop&&--a._loop,a.animate(t,e,i)):a._after?a._after.apply(o,[a]):a.stop()):requestAnimFrame(a.render)}else requestAnimFrame(a.render)},a.render()},i||0)),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else this.attrs[t]={from:this.target.attr(t),to:e};return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},plot:function(t){return this._plot=t,this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var r=this.target.viewbox();this._viewbox={x:{from:r.x,to:t},y:{from:r.y,to:e},width:{from:r.width,to:i},height:{from:r.height,to:n}}}return this},update:function(t){return this.target instanceof SVG.Stop&&(null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},loop:function(t){return this._loop=t||!0,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),this.attrs={},this.trans={},this.styles={},this.situation={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._plot,delete this._loop,delete this._after,delete this._during,delete this._viewbox,this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var t=(new Date).getTime()-this.situation.pause;this.situation.finish+=t,this.situation.start+=t,this.situation.play=!0}return this},_at:function(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:SVG.regex.unit.test(t.to)?new SVG.Number(t.to).minus(new SVG.Number(t.from)).times(e).plus(new SVG.Number(t.from)):t.to&&(t.to.r||SVG.Color.test(t.to))?this._color(t,e):1>e?t.from:t.to},_color:function(t,e){var i,n;return e=0>e?0:e>1?1:e,i=new SVG.Color(t.from),n=new SVG.Color(t.to),new SVG.Color({r:~~(i.r+(n.r-i.r)*e),g:~~(i.g+(n.g-i.g)*e),b:~~(i.b+(n.b-i.b)*e)}).toHex()}}),SVG.extend(SVG.Element,{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.Defs=function(){this.constructor.call(this,SVG.create("defs"))},SVG.Defs.prototype=new SVG.Container,SVG.G=function(){this.constructor.call(this,SVG.create("g"))},SVG.G.prototype=new SVG.Container,SVG.extend(SVG.G,{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)}}),SVG.extend(SVG.Container,{group:function(){return this.put(new SVG.G)}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){var t=this.siblings();return t.indexOf(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position();return this.parent.removeElement(this).put(this,t+1)},backward:function(){var t=this.position();return t>0&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.position()>0&&this.parent.removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent.add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent.add(t,e+1),this}}),SVG.Mask=function(){this.constructor.call(this,SVG.create("mask")),this.targets=[]},SVG.Mask.prototype=new SVG.Container,SVG.extend(SVG.Mask,{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return delete this.targets,this.parent.removeElement(this),this}}),SVG.extend(SVG.Element,{maskWith:function(t){return this.masker=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),SVG.extend(SVG.Container,{mask:function(){return this.defs().put(new SVG.Mask)}}),SVG.Clip=function(){this.constructor.call(this,SVG.create("clipPath")),this.targets=[]},SVG.Clip.prototype=new SVG.Container,SVG.extend(SVG.Clip,{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return delete this.targets,this.parent.removeElement(this),this}}),SVG.extend(SVG.Element,{clipWith:function(t){return this.clipper=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),SVG.extend(SVG.Container,{clip:function(){return this.defs().put(new SVG.Clip)}}),SVG.Gradient=function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},SVG.Gradient.prototype=new SVG.Container,SVG.extend(SVG.Gradient,{from:function(t,e){return"radial"==this.type?this.attr({fx:new SVG.Number(t),fy:new SVG.Number(e)}):this.attr({x1:new SVG.Number(t),y1:new SVG.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new SVG.Number(t),cy:new SVG.Number(e)}):this.attr({x2:new SVG.Number(t),y2:new SVG.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new SVG.Number(t)}):this},at:function(t){return this.put(new SVG.Stop(t))},update:function(t){return this.clear(),t(this),this},fill:function(){return"url(#"+this.attr("id")+")"},toString:function(){return this.fill()}}),SVG.extend(SVG.Defs,{gradient:function(t,e){var i=this.put(new SVG.Gradient(t));return e(i),i}}),SVG.extend(SVG.Container,{gradient:function(t,e){return this.defs().gradient(t,e)}}),SVG.Stop=function(t){this.constructor.call(this,SVG.create("stop")),this.update(t)},SVG.Stop.prototype=new SVG.Element,SVG.extend(SVG.Stop,{update:function(t){return null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset)),this}}),SVG.Doc=function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xlink",SVG.xlink,SVG.ns),this._defs=new SVG.Defs,this._defs.parent=this,this.node.appendChild(this._defs.node),this.doSubPixelOffsetFix=!1,"svg"!=this.parent.nodeName&&this.stage()},SVG.Doc.prototype=new SVG.Container,SVG.extend(SVG.Doc,{stage:function(){var t,e=this,i=document.createElement("div");return i.style.cssText="position:relative;height:100%;",e.parent.appendChild(i),i.appendChild(e.node),t=function(){"complete"===document.readyState?(e.style("position:absolute;"),setTimeout(function(){e.style("position:relative;overflow:hidden;"),e.parent.removeChild(e.node.parentNode),e.node.parentNode.removeChild(e.node),e.parent.appendChild(e.node),e.subPixelOffsetFix(),SVG.on(window,"resize",function(){e.subPixelOffsetFix()})},5)):setTimeout(t,10)},t(),this},defs:function(){return this._defs},subPixelOffsetFix:function(){if(this.doSubPixelOffsetFix){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){return this.doSubPixelOffsetFix=!0,this}}),SVG.Shape=function(t){this.constructor.call(this,t)},SVG.Shape.prototype=new SVG.Element,SVG.Use=function(){this.constructor.call(this,SVG.create("use"))},SVG.Use.prototype=new SVG.Shape,SVG.extend(SVG.Use,{element:function(t){return this.target=t,this.attr("href","#"+t,SVG.xlink)}}),SVG.extend(SVG.Container,{use:function(t){return this.put(new SVG.Use).element(t)}}),SVG.Rect=function(){this.constructor.call(this,SVG.create("rect"))},SVG.Rect.prototype=new SVG.Shape,SVG.extend(SVG.Container,{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}),SVG.Ellipse=function(){this.constructor.call(this,SVG.create("ellipse"))},SVG.Ellipse.prototype=new SVG.Shape,SVG.extend(SVG.Ellipse,{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new SVG.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new SVG.Number(t).divide(this.trans.scaleY))},width:function(t){return null==t?2*this.attr("rx"):this.attr("rx",new SVG.Number(t).divide(2))},height:function(t){return null==t?2*this.attr("ry"):this.attr("ry",new SVG.Number(t).divide(2))},size:function(t,e){return this.attr({rx:new SVG.Number(t).divide(2),ry:new SVG.Number(e).divide(2)})}}),SVG.extend(SVG.Container,{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put(new SVG.Ellipse).size(t,e).move(0,0)}}),SVG.Line=function(){this.constructor.call(this,SVG.create("line"))},SVG.Line.prototype=new SVG.Shape,SVG.extend(SVG.Line,{x:function(t){var e=this.bbox();return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},width:function(t){var e=this.bbox();return null==t?e.width:this.attr(this.attr("x1")e;e++)this.tspan(n[e]).newLine();this.rebuild()}return this},tspan:function(t){var e=this.textPath?this.textPath.node:this.node,i=(new SVG.TSpan).text(t),n=this.style();return e.appendChild(i.node),this.lines.add(i),SVG.regex.isBlank.test(n)||i.style(n),this.content+=t,i.parent=this,i},size:function(t){return this.attr("font-size",t)},leading:function(t){return null==t?this._leading:(t=new SVG.Number(t),this._leading=t,this.lines.each(function(){this.newLined&&this.attr("dy",t)}),this)},rebuild:function(){return this._rebuild&&this.lines.attr({x:this.attr("x"),dy:this._leading,style:this.style()}),this},clear:function(){for(var t=this.textPath?this.textPath.node:this.node;t.hasChildNodes();)t.removeChild(t.lastChild);return delete this.lines,this.lines=new SVG.Set,this.content="",this}}),SVG.extend(SVG.Container,{text:function(t){return this.put(new SVG.Text).text(t)}}),SVG.TSpan=function(){this.constructor.call(this,SVG.create("tspan"))},SVG.TSpan.prototype=new SVG.Shape,SVG.extend(SVG.TSpan,{text:function(t){return this.node.appendChild(document.createTextNode(t)),this},dx:function(t){return this.attr("dx",t) -},dy:function(t){return this.attr("dy",t)},newLine:function(){return this.newLined=!0,this.parent.content+="\n",this.dy(this.parent._leading),this.attr("x",this.parent.x())}}),SVG.TextPath=function(){this.constructor.call(this,SVG.create("textPath"))},SVG.TextPath.prototype=new SVG.Element,SVG.extend(SVG.Text,{path:function(t){for(this.textPath=new SVG.TextPath;this.node.hasChildNodes();)this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t,!0),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,SVG.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}),SVG.Nested=function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},SVG.Nested.prototype=new SVG.Container,SVG.extend(SVG.Container,{nested:function(){return this.put(new SVG.Nested)}}),SVG.A=function(){this.constructor.call(this,SVG.create("a"))},SVG.A.prototype=new SVG.Container,SVG.extend(SVG.A,{to:function(t){return this.attr("href",t,SVG.xlink)},show:function(t){return this.attr("show",t,SVG.xlink)},target:function(t){return this.attr("target",t)}}),SVG.extend(SVG.Container,{link:function(t){return this.put(new SVG.A).to(t)}}),SVG.extend(SVG.Element,{linkTo:function(t){var e=new SVG.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent.put(e).put(this)}}),SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var e=function(t,e){return"color"==e?t:t+"-"+e};["fill","stroke"].forEach(function(t){var i={};i[t]=function(i){if("string"==typeof i||SVG.Color.isRgb(i)||i&&"function"==typeof i.fill)this.attr(t,i);else for(index=SVG["_"+t].length-1;index>=0;index--)null!=i[SVG["_"+t][index]]&&this.attr(e(t,SVG["_"+t][index]),i[SVG["_"+t][index]]);return this},SVG.extend(SVG.Element,SVG.FX,i)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.extend(SVG.Rect,SVG.Ellipse,{radius:function(t,e){return this.attr({rx:t,ry:e||t})}}),SVG.Text&&SVG.extend(SVG.Text,SVG.FX,{font:function(e){for(var i in e)"anchor"==i?this.attr("text-anchor",e[i]):t.indexOf(i)>-1?this.attr("font-"+i,e[i]):this.attr(i,e[i]);return this}}),SVG.Set=function(){this.clear()},SVG.SetFX=function(t){this.set=t},SVG.extend(SVG.Set,{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;e>t;t++)this.members.push(i[t]);return this},remove:function(t){var e=this.members.indexOf(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;i>e;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},has:function(t){return this.members.indexOf(t)>=0},get:function(t){return this.members[t]},valueOf:function(){return this.members}}),SVG.Set.inherit=function(){var t,e=[];for(var t in SVG.Shape.prototype)"function"==typeof SVG.Shape.prototype[t]&&"function"!=typeof SVG.Set.prototype[t]&&e.push(t);e.forEach(function(t){SVG.Set.prototype[t]=function(){for(var e=0,i=this.members.length;i>e;e++)this.members[e]&&"function"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return"animate"==t?this.fx||(this.fx=new SVG.SetFX(this)):this}}),e=[];for(var t in SVG.FX.prototype)"function"==typeof SVG.FX.prototype[t]&&"function"!=typeof SVG.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){SVG.SetFX.prototype[t]=function(){for(var e=0,i=this.set.members.length;i>e;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},SVG.extend(SVG.Container,{set:function(){return new SVG.Set}}),SVG.extend(SVG.Element,{remember:function(t,e){if("object"==typeof arguments[0])for(var e in t)this.remember(e,t[e]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),"function"==typeof define&&define.amd?define(function(){return SVG}):"undefined"!=typeof exports&&(exports.SVG=SVG)}).call(this); \ No newline at end of file +(function(){if(this.SVG=function(t){return SVG.supported?new SVG.Doc(t):void 0},SVG.ns="http://www.w3.org/2000/svg",SVG.xmlns="http://www.w3.org/2000/xmlns/",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];SVG.Set&&SVG.Set.inherit&&SVG.Set.inherit()},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.parser=function(){var t=document.getElementsByTagName("body")[0]||document.getElementsByTagName("svg")[0],e=SVG.create("svg"),i=SVG.create("polygon"),n=SVG.create("path");return e.setAttributeNS(SVG.xmlns,"xmlns:xlink",SVG.xlink),e.setAttribute("style","opacity:0;position:fixed;left:100%;top:100%"),e.setAttribute("width","2"),e.setAttribute("height","2"),t.appendChild(e),e.appendChild(i),e.appendChild(n),{body:t,doc:e,poly:i,path:n}}(),SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/},SVG.defaults={matrix:"1 0 0 1 0 0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000"},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r},SVG.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},SVG.extend(SVG.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.lengtht;t++)-1==i.indexOf(this.value[t])&&i.push(this.value[t]);return this.value=i},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push(this.value[e]+(this.destination[e]-this.value[e])*t);return new SVG.Array(n)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)},split:function(t){return t.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")}}),SVG.PointArray=function(){this.constructor.apply(this,arguments)},SVG.PointArray.prototype=new SVG.Array,SVG.extend(SVG.PointArray,{toString:function(){for(var t=0,e=this.value.length,i=[];e>t;t++)i.push(this.value[t].join(","));return i.join(" ")},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push([this.value[e][0]+(this.destination[e][0]-this.value[e][0])*t,this.value[e][1]+(this.destination[e][1]-this.value[e][1])*t]);return new SVG.PointArray(n)},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;t=this.split(t);for(var e,i=0,n=t.length,r=[];n>i;i++)e=t[i].split(","),r.push([parseFloat(e[0]),parseFloat(e[1])]);return r},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.x;return this},bbox:function(){return this._cachedBBox?this._cachedBBox:(SVG.parser.poly.setAttribute("points",this.toString()),SVG.parser.poly.getBBox())}}),SVG.PathArray=function(t,e){this.constructor.call(this,t,e)},SVG.PathArray.prototype=new SVG.Array,SVG.extend(SVG.PathArray,{toString:function(){for(var t,e=0,i=this.value.length,n=[];i>e;e++){switch(t=[this.value[e].type],this.value[e].type){case"H":t.push(this.value[e].x);break;case"V":t.push(this.value[e].y);break;case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(this.value[e].type)&&t.push(this.value[e].x1,this.value[e].y1),/[CS]/.test(this.value[e].type)&&t.push(this.value[e].x2,this.value[e].y2),t.push(this.value[e].x,this.value[e].y);break;case"A":t.push(this.value[e].rx,this.value[e].ry,this.value[e].angle,this.value[e].largeArcFlag,this.value[e].sweepFlag,this.value[e].x,this.value[e].y)}n.push(t.join(" "))}return n.join(" ")},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x+=t;break;case"V":this.value[n].y+=e;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x+=t,this.value[n].y+=e,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1+=t,this.value[n].y1+=e),/[CS]/.test(this.value[n].type)&&(this.value[n].x2+=t,this.value[n].y2+=e);break;case"A":this.value[n].x+=t,this.value[n].y+=e}return this},size:function(t,e){for(var i=this.bbox(),n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x;break;case"V":this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x,this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1=(this.value[n].x1-i.x)*t/i.width+i.x,this.value[n].y1=(this.value[n].y1-i.y)*e/i.height+i.y),/[CS]/.test(this.value[n].type)&&(this.value[n].x2=(this.value[n].x2-i.x)*t/i.width+i.x,this.value[n].y2=(this.value[n].y2-i.y)*e/i.height+i.y);break;case"A":this.value[n].values.rx=this.value[n].values.rx*t/i.width,this.value[n].values.ry=this.value[n].values.ry*e/i.height,this.value[n].values.x=(this.value[n].values.x-i.x)*t/i.width+i.x,this.value[n].values.y=(this.value[n].values.y-i.y)*e/i.height+i.y}return this},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;var e,i,n,r,s,h,o,a,u,l,c,f=0,p=0;for(SVG.parser.path.setAttribute("d",t),c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){if(l=c.getItem(e),u=l.pathSegTypeAsLetter,/[MLHVCSQTA]/.test(u))"x"in l&&(f=l.x),"y"in l&&(p=l.y);else switch("x1"in l&&(s=f+l.x1),"x2"in l&&(o=f+l.x2),"y1"in l&&(h=p+l.y1),"y2"in l&&(a=p+l.y2),"x"in l&&(f+=l.x),"y"in l&&(p+=l.y),u){case"m":c.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(f,p),e);break;case"l":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(f,p),e);break;case"h":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(f),e);break;case"v":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(p),e);break;case"c":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(f,p,s,h,o,a),e);break;case"s":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(f,p,o,a),e);break;case"q":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(f,p,s,h),e);break;case"t":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(f,p),e);break;case"a":c.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(f,p,l.r1,l.r2,l.angle,l.largeArcFlag,l.sweepFlag),e);break;case"z":case"Z":f=n,p=r}/[Mm]/.test(u)&&(n=f,r=p)}for(t=[],c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){switch(l=c.getItem(e),u={},l.pathSegTypeAsLetter){case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(l.pathSegTypeAsLetter)&&(u.x1=l.x1,u.y1=l.y1),/[SC]/.test(l.pathSegTypeAsLetter)&&(u.x2=l.x2,u.y2=l.y2);break;case"A":u={r1:l.r1,r2:l.r2,angle:l.angle,largeArcFlag:l.largeArcFlag,sweepFlag:l.sweepFlag}}u.type=l.pathSegTypeAsLetter,u.x=l.x,u.y=l.y,t.push(u)}return t},bbox:function(){return this._cachedBBox?this._cachedBBox:(SVG.parser.path.setAttribute("d",this.toString()),SVG.parser.path.getBBox())}}),SVG.extend(SVG.PointArray,SVG.PathArray,{cache:function(){return this._cachedBBox=this.uncache().bbox(),this},uncache:function(){return delete this._cachedBBox,this}}),SVG.Number=function(t){switch(this.value=0,this.unit="",typeof t){case"number":this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;break;case"string":var e=t.match(SVG.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]&&(this.value/=100),this.unit=e[2]);break;default:t instanceof SVG.Number&&(this.value=t.value,this.unit=t.unit)}},SVG.extend(SVG.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:this.value)+this.unit},valueOf:function(){return this.value},to:function(t){return"string"==typeof t&&(this.unit=t),this},plus:function(t){return this.value=this+new SVG.Number(t),this},minus:function(t){return this.plus(-new SVG.Number(t))},times:function(t){return this.value=this*new SVG.Number(t),this},divide:function(t){return this.value=this/new SVG.Number(t),this}}),SVG.ViewBox=function(t){var e,i,n,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);for(n=new SVG.Number(t.width()),r=new SVG.Number(t.height());"%"==n.unit;)s*=n.value,n=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetWidth:t.width());for(;"%"==r.unit;)h*=r.value,r=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetHeight:t.height());this.x=o.x,this.y=o.y,this.width=n*s,this.height=r*h,this.zoom=1,a&&(e=parseFloat(a[0]),i=parseFloat(a[1]),n=parseFloat(a[2]),r=parseFloat(a[3]),this.zoom=this.width/this.height>n/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.RBox,{merge:function(t){var e=new SVG.RBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.Element=function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},SVG.extend(SVG.Element,{x:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({width:new SVG.Number(i.width),height:new SVG.Number(i.height)})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return this._isStyle(t)?"text"==t?this.content:"leading"==t&&this.leading?this.leading():this.style(t):(e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e);if("style"==t)return this.style(e);if("x"==t&&Array.isArray(this.lines))for(i=this.lines.length-1;i>=0;i--)this.lines[i].attr(t,e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),SVG.Color.test(e)||SVG.Color.isRgb(e)?e=new SVG.Color(e):"number"==typeof e?e=new SVG.Number(e):Array.isArray(e)&&(e=new SVG.Array(e)),null!=i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),this._isStyle(t)&&("text"==t?this.text(e):"leading"==t&&this.leading?this.leading(e):this.style(t,e),this.rebuild&&this.rebuild(t,e))}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(2>arguments.length)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+" "+this.trans.b+" "+this.trans.c+" "+this.trans.d+" "+this.trans.e+" "+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+" "+(null==t.cx?this.bbox().cx:t.cx)+" "+(null==t.cy?this.bbox().cy:t.cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+" "+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+new SVG.Number(t.x/t.scaleX)+" "+new SVG.Number(t.y/t.scaleY)+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(2>arguments.length)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;t.length>i;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&i.x+i.width>t&&i.y+i.height>e},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},toString:function(){return this.attr("id")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_isStyle:function(t){return"string"==typeof t?SVG.regex.test(t,"isStyle"):!1},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t},_proportionalSize:function(t,e){if(null==t||null==e){var i=this.bbox();null==e?e=i.height/i.width*t:null==t&&(t=i.width/i.height*e)}return{width:t,height:e}}}),SVG.Parent=function(t){this.constructor.call(this,t)},SVG.Parent.prototype=new SVG.Element,SVG.extend(SVG.Parent,{children:function(){return this._children||(this._children=[])},add:function(t,e){if(!this.has(t)){if(e=null==e?this.children().length:e,t.parent){var i=t.parent.children().indexOf(t);t.parent.children().splice(i,1)}this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this}return this._defs&&(this.node.removeChild(this._defs.node),this.node.appendChild(this._defs.node)),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.children().indexOf(t)>=0},get:function(t){return this.children()[t]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;n>i;i++)r[i]instanceof SVG.Element&&t.apply(r[i],[i,r]),e&&r[i]instanceof SVG.Container&&r[i].each(t,e);return this},removeElement:function(t){var e=this.children().indexOf(t);return this.children().splice(e,1),this.node.removeChild(t.node),t.parent=null,this},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&this._defs.clear(),this},defs:function(){return this.doc().defs()}}),SVG.Container=function(t){this.constructor.call(this,t)},SVG.Container.prototype=new SVG.Parent,SVG.extend(SVG.Container,{viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}),SVG.FX=function(t){this.target=t},SVG.extend(SVG.FX,{animate:function(t,e,i){var n,r,s,h,o=this.target,a=this;return"object"==typeof t&&(i=t.delay,e=t.ease,t=t.duration),t=null==t?1e3:t,e=e||"<>",a.to=function(t){var i;if(t=0>t?0:t>1?1:t,null==n){n=[];for(h in a.attrs)n.push(h);if(o.morphArray&&n.indexOf("points")>-1){var u,l=new o.morphArray(a._plot||o.array);a._size&&l.size(a._size.width.to,a._size.height.to),u=l.bbox(),a._x?l.move(a._x.to,u.y):a._cx&&l.move(a._cx.to-u.width/2,u.y),u=l.bbox(),a._y?l.move(u.x,a._y.to):a._cy&&l.move(u.x,a._cy.to-u.height/2),delete a._x,delete a._y,delete a._cx,delete a._cy,delete a._size,a._plot=o.array.morph(l)}}if(null==r){r=[];for(h in a.trans)r.push(h)}if(null==s){s=[];for(h in a.styles)s.push(h)}for(t="<>"==e?-Math.cos(t*Math.PI)/2+.5:">"==e?Math.sin(t*Math.PI/2):"<"==e?-Math.cos(t*Math.PI/2)+1:"-"==e?t:"function"==typeof e?e(t):t,a._plot?o.plot(a._plot.at(t)):(o.array&&o.array.cache(),a._x?o.x(a._at(a._x,t)):a._cx&&o.cx(a._at(a._cx,t)),a._y?o.y(a._at(a._y,t)):a._cy&&o.cy(a._at(a._cy,t)),a._size&&o.size(a._at(a._size.width,t),a._at(a._size.height,t)),o.array&&o.array.uncache()),a._viewbox&&o.viewbox(a._at(a._viewbox.x,t),a._at(a._viewbox.y,t),a._at(a._viewbox.width,t),a._at(a._viewbox.height,t)),i=n.length-1;i>=0;i--)o.attr(n[i],a._at(a.attrs[n[i]],t));for(i=r.length-1;i>=0;i--)o.transform(r[i],a._at(a.trans[r[i]],t));for(i=s.length-1;i>=0;i--)o.style(s[i],a._at(a.styles[s[i]],t));a._during&&a._during.call(o,t,function(e,i){return a._at({from:e,to:i},t)})},"number"==typeof t&&(this.timeout=setTimeout(function(){var n=(new Date).getTime();a.situation={interval:1e3/60,start:n,play:!0,finish:n+t,duration:t},a.render=function(){if(a.situation.play===!0){var n=(new Date).getTime(),r=n>a.situation.finish?1:(n-a.situation.start)/t;a.to(r),n>a.situation.finish?(a._plot&&o.plot(new SVG.PointArray(a._plot.destination).settle()),a._loop===!0||"number"==typeof a._loop&&a._loop>1?("number"==typeof a._loop&&--a._loop,a.animate(t,e,i)):a._after?a._after.apply(o,[a]):a.stop()):requestAnimFrame(a.render)}else requestAnimFrame(a.render)},a.render()},i||0)),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else this.attrs[t]={from:this.target.attr(t),to:e};return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},plot:function(t){return this._plot=t,this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var r=this.target.viewbox();this._viewbox={x:{from:r.x,to:t},y:{from:r.y,to:e},width:{from:r.width,to:i},height:{from:r.height,to:n}}}return this},update:function(t){return this.target instanceof SVG.Stop&&(null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},loop:function(t){return this._loop=t||!0,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),this.attrs={},this.trans={},this.styles={},this.situation={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._plot,delete this._loop,delete this._after,delete this._during,delete this._viewbox,this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var t=(new Date).getTime()-this.situation.pause;this.situation.finish+=t,this.situation.start+=t,this.situation.play=!0}return this},_at:function(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:SVG.regex.unit.test(t.to)?new SVG.Number(t.to).minus(new SVG.Number(t.from)).times(e).plus(new SVG.Number(t.from)):t.to&&(t.to.r||SVG.Color.test(t.to))?this._color(t,e):1>e?t.from:t.to},_color:function(t,e){var i,n;return e=0>e?0:e>1?1:e,i=new SVG.Color(t.from),n=new SVG.Color(t.to),new SVG.Color({r:~~(i.r+(n.r-i.r)*e),g:~~(i.g+(n.g-i.g)*e),b:~~(i.b+(n.b-i.b)*e)}).toHex()}}),SVG.extend(SVG.Element,{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.Defs=function(){this.constructor.call(this,SVG.create("defs"))},SVG.Defs.prototype=new SVG.Container,SVG.G=function(){this.constructor.call(this,SVG.create("g"))},SVG.G.prototype=new SVG.Container,SVG.extend(SVG.G,{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)}}),SVG.extend(SVG.Container,{group:function(){return this.put(new SVG.G)}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){var t=this.siblings();return t.indexOf(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position();return this.parent.removeElement(this).put(this,t+1)},backward:function(){var t=this.position();return t>0&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.position()>0&&this.parent.removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent.add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent.add(t,e+1),this}}),SVG.Mask=function(){this.constructor.call(this,SVG.create("mask")),this.targets=[]},SVG.Mask.prototype=new SVG.Container,SVG.extend(SVG.Mask,{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return delete this.targets,this.parent.removeElement(this),this}}),SVG.extend(SVG.Element,{maskWith:function(t){return this.masker=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),SVG.extend(SVG.Container,{mask:function(){return this.defs().put(new SVG.Mask)}}),SVG.Clip=function(){this.constructor.call(this,SVG.create("clipPath")),this.targets=[]},SVG.Clip.prototype=new SVG.Container,SVG.extend(SVG.Clip,{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return delete this.targets,this.parent.removeElement(this),this}}),SVG.extend(SVG.Element,{clipWith:function(t){return this.clipper=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),SVG.extend(SVG.Container,{clip:function(){return this.defs().put(new SVG.Clip)}}),SVG.Gradient=function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},SVG.Gradient.prototype=new SVG.Container,SVG.extend(SVG.Gradient,{from:function(t,e){return"radial"==this.type?this.attr({fx:new SVG.Number(t),fy:new SVG.Number(e)}):this.attr({x1:new SVG.Number(t),y1:new SVG.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new SVG.Number(t),cy:new SVG.Number(e)}):this.attr({x2:new SVG.Number(t),y2:new SVG.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new SVG.Number(t)}):this},at:function(t){return this.put(new SVG.Stop(t))},update:function(t){return this.clear(),t(this),this},fill:function(){return"url(#"+this.attr("id")+")"},toString:function(){return this.fill()}}),SVG.extend(SVG.Defs,{gradient:function(t,e){var i=this.put(new SVG.Gradient(t));return e(i),i}}),SVG.extend(SVG.Container,{gradient:function(t,e){return this.defs().gradient(t,e)}}),SVG.Stop=function(t){this.constructor.call(this,SVG.create("stop")),this.update(t)},SVG.Stop.prototype=new SVG.Element,SVG.extend(SVG.Stop,{update:function(t){return null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset)),this}}),SVG.Doc=function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xmlns:xlink",SVG.xlink,SVG.xmlns),this._defs=new SVG.Defs,this._defs.parent=this,this.node.appendChild(this._defs.node),this.doSubPixelOffsetFix=!1,"svg"!=this.parent.nodeName&&this.stage()},SVG.Doc.prototype=new SVG.Container,SVG.extend(SVG.Doc,{stage:function(){var t,e=this,i=document.createElement("div");return i.style.cssText="position:relative;height:100%;",e.parent.appendChild(i),i.appendChild(e.node),t=function(){"complete"===document.readyState?(e.style("position:absolute;"),setTimeout(function(){e.style("position:relative;overflow:hidden;"),e.parent.removeChild(e.node.parentNode),e.node.parentNode.removeChild(e.node),e.parent.appendChild(e.node),e.subPixelOffsetFix(),SVG.on(window,"resize",function(){e.subPixelOffsetFix()})},5)):setTimeout(t,10)},t(),this},defs:function(){return this._defs},subPixelOffsetFix:function(){if(this.doSubPixelOffsetFix){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){return this.doSubPixelOffsetFix=!0,this}}),SVG.Shape=function(t){this.constructor.call(this,t)},SVG.Shape.prototype=new SVG.Element,SVG.Use=function(){this.constructor.call(this,SVG.create("use"))},SVG.Use.prototype=new SVG.Shape,SVG.extend(SVG.Use,{element:function(t){return this.target=t,this.attr("href","#"+t,SVG.xlink)}}),SVG.extend(SVG.Container,{use:function(t){return this.put(new SVG.Use).element(t)}}),SVG.Rect=function(){this.constructor.call(this,SVG.create("rect"))},SVG.Rect.prototype=new SVG.Shape,SVG.extend(SVG.Container,{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}),SVG.Ellipse=function(){this.constructor.call(this,SVG.create("ellipse"))},SVG.Ellipse.prototype=new SVG.Shape,SVG.extend(SVG.Ellipse,{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new SVG.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new SVG.Number(t).divide(this.trans.scaleY))},width:function(t){return null==t?2*this.attr("rx"):this.attr("rx",new SVG.Number(t).divide(2))},height:function(t){return null==t?2*this.attr("ry"):this.attr("ry",new SVG.Number(t).divide(2))},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({rx:new SVG.Number(i.width).divide(2),ry:new SVG.Number(i.height).divide(2)})}}),SVG.extend(SVG.Container,{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put(new SVG.Ellipse).size(t,e).move(0,0)}}),SVG.Line=function(){this.constructor.call(this,SVG.create("line"))},SVG.Line.prototype=new SVG.Shape,SVG.extend(SVG.Line,{x:function(t){var e=this.bbox();return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},width:function(t){var e=this.bbox();return null==t?e.width:this.attr(this.attr("x1")e;e++)this.tspan(n[e]).newLine();this.rebuild()}return this},tspan:function(t){var e=this.textPath?this.textPath.node:this.node,i=(new SVG.TSpan).text(t),n=this.style();return e.appendChild(i.node),this.lines.add(i),SVG.regex.isBlank.test(n)||i.style(n),this.content+=t,i.parent=this,i},size:function(t){return this.attr("font-size",t)},leading:function(t){return null==t?this._leading:(t=new SVG.Number(t),this._leading=t,this.lines.each(function(){this.newLined&&this.attr("dy",t)}),this)},rebuild:function(){return this._rebuild&&this.lines.attr({x:this.attr("x"),dy:this._leading,style:this.style()}),this},clear:function(){for(var t=this.textPath?this.textPath.node:this.node;t.hasChildNodes();)t.removeChild(t.lastChild);return delete this.lines,this.lines=new SVG.Set,this.content="",this}}),SVG.extend(SVG.Container,{text:function(t){return this.put(new SVG.Text).text(t)}}),SVG.TSpan=function(){this.constructor.call(this,SVG.create("tspan"))},SVG.TSpan.prototype=new SVG.Shape,SVG.extend(SVG.TSpan,{text:function(t){return this.node.appendChild(document.createTextNode(t)),this},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){return this.newLined=!0,this.parent.content+="\n",this.dy(this.parent._leading),this.attr("x",this.parent.x())}}),SVG.TextPath=function(){this.constructor.call(this,SVG.create("textPath"))},SVG.TextPath.prototype=new SVG.Element,SVG.extend(SVG.Text,{path:function(t){for(this.textPath=new SVG.TextPath;this.node.hasChildNodes();)this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t,!0),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,SVG.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}),SVG.Nested=function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},SVG.Nested.prototype=new SVG.Container,SVG.extend(SVG.Container,{nested:function(){return this.put(new SVG.Nested)}}),SVG.A=function(){this.constructor.call(this,SVG.create("a"))},SVG.A.prototype=new SVG.Container,SVG.extend(SVG.A,{to:function(t){return this.attr("href",t,SVG.xlink)},show:function(t){return this.attr("show",t,SVG.xlink)},target:function(t){return this.attr("target",t)}}),SVG.extend(SVG.Container,{link:function(t){return this.put(new SVG.A).to(t)}}),SVG.extend(SVG.Element,{linkTo:function(t){var e=new SVG.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent.put(e).put(this)}}),SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var e=function(t,e){return"color"==e?t:t+"-"+e};["fill","stroke"].forEach(function(t){var i={};i[t]=function(i){if("string"==typeof i||SVG.Color.isRgb(i)||i&&"function"==typeof i.fill)this.attr(t,i);else for(index=SVG["_"+t].length-1;index>=0;index--)null!=i[SVG["_"+t][index]]&&this.attr(e(t,SVG["_"+t][index]),i[SVG["_"+t][index]]);return this},SVG.extend(SVG.Element,SVG.FX,i)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.extend(SVG.Rect,SVG.Ellipse,{radius:function(t,e){return this.attr({rx:t,ry:e||t})}}),SVG.Text&&SVG.extend(SVG.Text,SVG.FX,{font:function(e){for(var i in e)"anchor"==i?this.attr("text-anchor",e[i]):t.indexOf(i)>-1?this.attr("font-"+i,e[i]):this.attr(i,e[i]);return this}}),SVG.Set=function(){this.clear()},SVG.SetFX=function(t){this.set=t},SVG.extend(SVG.Set,{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;e>t;t++)this.members.push(i[t]);return this},remove:function(t){var e=this.members.indexOf(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;i>e;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},has:function(t){return this.members.indexOf(t)>=0},get:function(t){return this.members[t]},valueOf:function(){return this.members}}),SVG.Set.inherit=function(){var t,e=[];for(var t in SVG.Shape.prototype)"function"==typeof SVG.Shape.prototype[t]&&"function"!=typeof SVG.Set.prototype[t]&&e.push(t);e.forEach(function(t){SVG.Set.prototype[t]=function(){for(var e=0,i=this.members.length;i>e;e++)this.members[e]&&"function"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return"animate"==t?this.fx||(this.fx=new SVG.SetFX(this)):this}}),e=[];for(var t in SVG.FX.prototype)"function"==typeof SVG.FX.prototype[t]&&"function"!=typeof SVG.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){SVG.SetFX.prototype[t]=function(){for(var e=0,i=this.set.members.length;i>e;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},SVG.extend(SVG.Container,{set:function(){return new SVG.Set}}),SVG.extend(SVG.Element,{data:function(t,e,i){if("object"==typeof t)for(e in t)this.data(e,t[e]);else if(2>arguments.length)try{return JSON.parse(this.attr("data-"+t))}catch(n){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:i===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this}}),SVG.extend(SVG.Element,{remember:function(t,e){if("object"==typeof arguments[0])for(var e in t)this.remember(e,t[e]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),"function"==typeof define&&define.amd?define(function(){return SVG}):"undefined"!=typeof exports&&(exports.SVG=SVG)}).call(this); \ No newline at end of file diff --git a/spec/index.html b/spec/index.html index b5e4d99..f7e01ec 100644 --- a/spec/index.html +++ b/spec/index.html @@ -8,10 +8,11 @@ @@ -27,7 +28,6 @@ - @@ -84,4 +84,6 @@ })(); + + diff --git a/spec/spec/element.js b/spec/spec/element.js index f1edb01..22b09bf 100644 --- a/spec/spec/element.js +++ b/spec/spec/element.js @@ -158,23 +158,34 @@ describe('Element', function() { }) describe('data()', function() { - it('should set a data attribute and convert value to json', function() { + it('sets a data attribute and convert value to json', function() { var rect = draw.rect(100,100).data('test', 'value') expect(rect.node.getAttribute('data-test')).toBe('value') }) - it('should set a data attribute and not convert value to json if flagged raw', function() { + it('sets a data attribute and not convert value to json if flagged raw', function() { var rect = draw.rect(100,100).data('test', 'value', true) expect(rect.node.getAttribute('data-test')).toBe('value') }) - it('should get data value in ony one argument is passed', function() { + it('sets multiple data attributes and convert values to json when an object is passed', function() { + var rect = draw.rect(100,100).data({ + forbidden: 'fruit' + , multiple: { + values: 'in' + , an: 'object' + } + }) + expect(rect.node.getAttribute('data-forbidden')).toBe('fruit') + expect(rect.node.getAttribute('data-multiple')).toEqual('{"values":"in","an":"object"}') + }) + it('gets data value if only one argument is passed', function() { var rect = draw.rect(100,100).data('test', 101) expect(rect.data('test')).toBe(101) }) - it('should maintain data type for a number', function() { + it('maintains data type for a number', function() { var rect = draw.rect(100,100).data('test', 101) expect(typeof rect.data('test')).toBe('number') }) - it('should maintain data type for an object', function() { + it('maintains data type for an object', function() { var rect = draw.rect(100,100).data('test', { string: 'value', array: [1,2,3] }) expect(typeof rect.data('test')).toBe('object') expect(Array.isArray(rect.data('test').array)).toBe(true) diff --git a/spec/spec/ellipse.js b/spec/spec/ellipse.js index 27d1840..7d7101a 100644 --- a/spec/spec/ellipse.js +++ b/spec/spec/ellipse.js @@ -110,6 +110,18 @@ describe('Ellipse', function() { expect(ellipse.node.getAttribute('rx')).toBe((987 / 2).toString()) expect(ellipse.node.getAttribute('ry')).toBe((654 / 2).toString()) }) + it('defines the width and height proportionally with only the width value given', function() { + var box = ellipse.bbox() + ellipse.size(500) + expect(ellipse.width()).toBe(500) + expect(ellipse.width() / ellipse.height()).toBe(box.width / box.height) + }) + it('defines the width and height proportionally with only the height value given', function() { + var box = ellipse.bbox() + ellipse.size(null, 525) + expect(ellipse.height()).toBe(525) + expect(ellipse.width() / ellipse.height()).toBe(box.width / box.height) + }) }) describe('scale()', function() { diff --git a/spec/spec/gradient.js b/spec/spec/gradient.js index e13e113..7072164 100644 --- a/spec/spec/gradient.js +++ b/spec/spec/gradient.js @@ -1,9 +1,18 @@ describe('Gradient', function() { - var rect = draw.rect(100,100) - , gradient = draw.gradient('linear', function(stop) { + var rect, gradient + + beforeEach(function() { + rect = draw.rect(100,100) + gradient = draw.gradient('linear', function(stop) { stop.at({ offset: 0, color: '#333', opacity: 1 }) stop.at({ offset: 1, color: '#fff', opacity: 1 }) }) + }) + + afterEach(function() { + rect.remove() + gradient.remove() + }) it('is an instance of SVG.Gradient', function() { expect(gradient instanceof SVG.Gradient).toBe(true) diff --git a/spec/spec/helper.js b/spec/spec/helper.js index 7394754..552ca6c 100644 --- a/spec/spec/helper.js +++ b/spec/spec/helper.js @@ -1,8 +1,8 @@ /* create canavs */ -var canvas = document.createElement('div') -canvas.id = 'canvas' -document.getElementsByTagName('body')[0].appendChild(canvas) -draw = SVG(canvas).size(100,100) +var drawing = document.createElement('div') +drawing.id = 'drawing' +document.getElementsByTagName('body')[0].appendChild(drawing) +draw = SVG(drawing).size(100,100) /* raw path data */ svgPath = 'M88.006,61.994c3.203,0,6.216-1.248,8.481-3.514C98.752,56.215,100,53.203,100,50c0-3.204-1.248-6.216-3.513-8.481 c-2.266-2.265-5.278-3.513-8.481-3.513c-2.687,0-5.237,0.877-7.327,2.496h-7.746l5.479-5.479 c5.891-0.757,10.457-5.803,10.457-11.896c0-6.614-5.381-11.995-11.994-11.995c-6.093,0-11.14,4.567-11.896,10.457l-5.479,5.479 v-7.747c1.618-2.089,2.495-4.641,2.495-7.327c0-3.204-1.247-6.216-3.513-8.481C56.216,1.248,53.204,0,50,0 c-3.204,0-6.216,1.248-8.481,3.513c-2.265,2.265-3.513,5.277-3.513,8.481c0,2.686,0.877,5.237,2.495,7.327v7.747l-5.479-5.479 c-0.757-5.89-5.803-10.457-11.896-10.457c-6.614,0-11.995,5.381-11.995,11.995c0,6.093,4.567,11.139,10.458,11.896l5.479,5.479 h-7.747c-2.089-1.619-4.641-2.496-7.327-2.496c-3.204,0-6.216,1.248-8.481,3.513C1.248,43.784,0,46.796,0,50 c0,3.203,1.248,6.216,3.513,8.48c2.265,2.266,5.277,3.514,8.481,3.514c2.686,0,5.237-0.877,7.327-2.496h7.747l-5.479,5.479 c-5.891,0.757-10.458,5.804-10.458,11.896c0,6.614,5.381,11.994,11.995,11.994c6.093,0,11.139-4.566,11.896-10.457l5.479-5.479 v7.749c-3.63,4.7-3.291,11.497,1.018,15.806C43.784,98.752,46.796,100,50,100c3.204,0,6.216-1.248,8.481-3.514 c4.309-4.309,4.647-11.105,1.018-15.806v-7.749l5.479,5.479c0.757,5.891,5.804,10.457,11.896,10.457 c6.613,0,11.994-5.38,11.994-11.994c0-6.093-4.566-11.14-10.457-11.896l-5.479-5.479h7.746 C82.769,61.117,85.319,61.994,88.006,61.994z M76.874,68.354c4.705,0,8.52,3.814,8.52,8.521c0,4.705-3.814,8.52-8.52,8.52 s-8.52-3.814-8.52-8.52l-12.33-12.33V81.98c3.327,3.328,3.327,8.723,0,12.049c-3.327,3.328-8.722,3.328-12.049,0 c-3.327-3.326-3.327-8.721,0-12.049V64.544l-12.33,12.33c0,4.705-3.814,8.52-8.52,8.52s-8.52-3.814-8.52-8.52 c0-4.706,3.814-8.521,8.52-8.521l12.33-12.33H18.019c-3.327,3.328-8.722,3.328-12.049,0c-3.327-3.326-3.327-8.721,0-12.048 s8.722-3.327,12.049,0h17.438l-12.33-12.33c-4.706,0-8.52-3.814-8.52-8.52c0-4.706,3.814-8.52,8.52-8.52s8.52,3.814,8.52,8.52 l12.33,12.33V18.019c-3.327-3.327-3.327-8.722,0-12.049s8.722-3.327,12.049,0s3.327,8.722,0,12.049v17.438l12.33-12.33 c0-4.706,3.814-8.52,8.52-8.52s8.52,3.814,8.52,8.52c0,4.705-3.814,8.52-8.52,8.52l-12.33,12.33h17.438 c3.327-3.327,8.722-3.327,12.049,0s3.327,8.722,0,12.048c-3.327,3.328-8.722,3.328-12.049,0H64.544L76.874,68.354z' diff --git a/spec/spec/image.js b/spec/spec/image.js index 798ea08..b24835b 100644 --- a/spec/spec/image.js +++ b/spec/spec/image.js @@ -96,6 +96,18 @@ describe('Image', function() { expect(image.node.getAttribute('width')).toBe('987') expect(image.node.getAttribute('height')).toBe('654') }) + it('defines the width and height proportionally with only the width value given', function() { + var box = image.bbox() + image.size(500) + expect(image.width()).toBe(500) + expect(image.width() / image.height()).toBe(box.width / box.height) + }) + it('defines the width and height proportionally with only the height value given', function() { + var box = image.bbox() + image.size(null, 525) + expect(image.height()).toBe(525) + expect(image.width() / image.height()).toBe(box.width / box.height) + }) }) describe('scale()', function() { diff --git a/spec/spec/line.js b/spec/spec/line.js index 1ed45fb..cb9d6f6 100644 --- a/spec/spec/line.js +++ b/spec/spec/line.js @@ -112,6 +112,18 @@ describe('Line', function() { expect(box.x + box.width).toBe(987) expect(box.y).toBe(0) }) + it('defines the width and height proportionally with only the width value given', function() { + var box = line.bbox() + line.size(500) + expect(line.width()).toBe(500) + expect(line.width() / line.height()).toBe(box.width / box.height) + }) + it('defines the width and height proportionally with only the height value given', function() { + var box = line.bbox() + line.size(null, 525) + expect(line.height()).toBe(525) + expect(line.width() / line.height()).toBe(box.width / box.height) + }) }) describe('scale()', function() { diff --git a/spec/spec/path.js b/spec/spec/path.js index a64f8aa..9503e3b 100644 --- a/spec/spec/path.js +++ b/spec/spec/path.js @@ -102,12 +102,24 @@ describe('Path', function() { }) describe('size()', function() { - it('should define the width and height of the element', function() { + it('defines the width and height of the element', function() { path.size(987,654) var box = path.bbox() expect(approximately(box.width, 0.1)).toBe(987) expect(approximately(box.height, 0.1)).toBe(654) }) + it('defines the width and height proportionally with only the width value given', function() { + var box = path.bbox() + path.size(500) + expect(path.width()).toBe(500) + expect(path.width() / path.height()).toBe(box.width / box.height) + }) + it('defines the width and height proportionally with only the height value given', function() { + var box = path.bbox() + path.size(null, 525) + expect(path.height()).toBe(525) + expect(path.width() / path.height()).toBe(box.width / box.height) + }) }) describe('scale()', function() { @@ -137,7 +149,7 @@ describe('Path', function() { describe('plot()', function() { it('falls back to a single point without an argument', function() { path = draw.path() - expect(path.node.getAttribute('d')).toBe('M0,0') + expect(path.node.getAttribute('d')).toBe('M 0 0') }) }) diff --git a/spec/spec/polygon.js b/spec/spec/polygon.js index a124796..e94facd 100644 --- a/spec/spec/polygon.js +++ b/spec/spec/polygon.js @@ -102,6 +102,18 @@ describe('Polygon', function() { expect(approximately(box.width, 0.1)).toBe(987) expect(approximately(box.height, 0.1)).toBe(654) }) + it('defines the width and height proportionally with only the width value given', function() { + var box = polygon.bbox() + polygon.size(500) + expect(polygon.width()).toBe(500) + expect(polygon.width() / polygon.height()).toBe(box.width / box.height) + }) + it('defines the width and height proportionally with only the height value given', function() { + var box = polygon.bbox() + polygon.size(null, 525) + expect(polygon.height()).toBe(525) + expect(polygon.width() / polygon.height()).toBe(box.width / box.height) + }) }) describe('scale()', function() { diff --git a/spec/spec/polyline.js b/spec/spec/polyline.js index 9272d78..204cfa7 100644 --- a/spec/spec/polyline.js +++ b/spec/spec/polyline.js @@ -102,6 +102,18 @@ describe('Polyline', function() { expect(approximately(box.width, 0.1)).toBe(987) expect(approximately(box.height, 0.1)).toBe(654) }) + it('defines the width and height proportionally with only the width value given', function() { + var box = polyline.bbox() + polyline.size(500) + expect(polyline.width()).toBe(500) + expect(polyline.width() / polyline.height()).toBe(box.width / box.height) + }) + it('defines the width and height proportionally with only the height value given', function() { + var box = polyline.bbox() + polyline.size(null, 525) + expect(polyline.height()).toBe(525) + expect(polyline.width() / polyline.height()).toBe(box.width / box.height) + }) }) describe('scale()', function() { diff --git a/spec/spec/rect.js b/spec/spec/rect.js index 4b9ef2f..36d4925 100644 --- a/spec/spec/rect.js +++ b/spec/spec/rect.js @@ -109,6 +109,18 @@ describe('Rect', function() { expect(rect.node.getAttribute('width')).toBe('987') expect(rect.node.getAttribute('height')).toBe('654') }) + it('defines the width and height proportionally with only the width value given', function() { + var box = rect.bbox() + rect.size(500) + expect(rect.width()).toBe(500) + expect(rect.width() / rect.height()).toBe(box.width / box.height) + }) + it('defines the width and height proportionally with only the height value given', function() { + var box = rect.bbox() + rect.size(null, 525) + expect(rect.height()).toBe(525) + expect(rect.width() / rect.height()).toBe(box.width / box.height) + }) }) describe('scale()', function() { diff --git a/src/array.js b/src/array.js index 671c8e8..b239c78 100755 --- a/src/array.js +++ b/src/array.js @@ -30,10 +30,8 @@ SVG.extend(SVG.Array, { } // Clean up any duplicate points , settle: function() { - var i, seen = [] - /* find all unique values */ - for (i = this.value.length - 1; i >= 0; i--) + for (var i = 0, il = this.value.length, seen = []; i < il; i++) if (seen.indexOf(this.value[i]) == -1) seen.push(this.value[i]) diff --git a/src/arraycache.js b/src/arraycache.js new file mode 100644 index 0000000..41fdbcd --- /dev/null +++ b/src/arraycache.js @@ -0,0 +1,14 @@ +SVG.extend(SVG.PointArray, SVG.PathArray, { + // Cache bbox + cache: function() { + this._cachedBBox = this.uncache().bbox() + + return this + } + // Remove cache +, uncache: function() { + delete this._cachedBBox + return this + } + +}) \ No newline at end of file diff --git a/src/data.js b/src/data.js new file mode 100644 index 0000000..1adbd52 --- /dev/null +++ b/src/data.js @@ -0,0 +1,29 @@ +// +SVG.extend(SVG.Element, { + // Store data values on svg nodes + data: function(a, v, r) { + if (typeof a == 'object') { + for (v in a) + this.data(v, a[v]) + + } else if (arguments.length < 2) { + try { + return JSON.parse(this.attr('data-' + a)) + } catch(e) { + return this.attr('data-' + a) + } + + } else { + this.attr( + 'data-' + a + , v === null ? + null : + r === true || typeof v === 'string' || typeof v === 'number' ? + v : + JSON.stringify(v) + ) + } + + return this + } +}) \ No newline at end of file diff --git a/src/doc.js b/src/doc.js index 462fd84..7e9eef3 100755 --- a/src/doc.js +++ b/src/doc.js @@ -15,7 +15,7 @@ SVG.Doc = function(element) { /* set svg element attributes */ this .attr({ xmlns: SVG.ns, version: '1.1', width: '100%', height: '100%' }) - .attr('xlink', SVG.xlink, SVG.ns) + .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) /* create the node */ this._defs = new SVG.Defs diff --git a/src/element.js b/src/element.js index ad0fc4a..50edc85 100755 --- a/src/element.js +++ b/src/element.js @@ -63,9 +63,11 @@ SVG.extend(SVG.Element, { } // Set element size to given width and height , size: function(width, height) { + var p = this._proportionalSize(width, height) + return this.attr({ - width: new SVG.Number(width) - , height: new SVG.Number(height) + width: new SVG.Number(p.width) + , height: new SVG.Number(p.height) }) } // Clone element @@ -277,10 +279,6 @@ SVG.extend(SVG.Element, { if (o.x != 0 || o.y != 0) transform.push('translate(' + new SVG.Number(o.x / o.scaleX) + ' ' + new SVG.Number(o.y / o.scaleY) + ')') - /* add offset translation */ - if (this._offset && this._offset.x != 0 && this._offset.y != 0) - transform.push('translate(' + (-this._offset.x) + ' ' + (-this._offset.y) + ')') - /* update transformations, even if there are none */ if (transform.length == 0) this.node.removeAttribute('transform') @@ -338,28 +336,6 @@ SVG.extend(SVG.Element, { return this } - // Store data values on svg nodes -, data: function(a, v, r) { - if (arguments.length < 2) { - try { - return JSON.parse(this.attr('data-' + a)) - } catch(e) { - return this.attr('data-' + a) - } - - } else { - this.attr( - 'data-' + a - , v === null ? - null : - r === true || typeof v === 'string' || typeof v === 'number' ? - v : - JSON.stringify(v) - ) - } - - return this - } // Get bounding box , bbox: function() { return new SVG.BBox(this) @@ -425,5 +401,21 @@ SVG.extend(SVG.Element, { return o } + // Private: calculate proportional width and height values when necessary +, _proportionalSize: function(width, height) { + if (width == null || height == null) { + var box = this.bbox() + + if (height == null) + height = box.height / box.width * width + else if (width == null) + width = box.width / box.height * height + } + + return { + width: width + , height: height + } + } }) \ No newline at end of file diff --git a/src/ellipse.js b/src/ellipse.js index 86186c9..237a5ca 100755 --- a/src/ellipse.js +++ b/src/ellipse.js @@ -34,9 +34,11 @@ SVG.extend(SVG.Ellipse, { } // Custom size function , size: function(width, height) { + var p = this._proportionalSize(width, height) + return this.attr({ - rx: new SVG.Number(width).divide(2) - , ry: new SVG.Number(height).divide(2) + rx: new SVG.Number(p.width).divide(2) + , ry: new SVG.Number(p.height).divide(2) }) } diff --git a/src/fx.js b/src/fx.js index acaeea5..779d038 100755 --- a/src/fx.js +++ b/src/fx.js @@ -36,10 +36,10 @@ SVG.extend(SVG.FX, { akeys.push(key) /* make sure morphable elements are scaled, translated and morphed all together */ - if (element.morphArray) { + if (element.morphArray && akeys.indexOf('points') > -1) { /* get destination */ var box - , p = new element.morphArray(fx._plot || element.points.toString()) + , p = new element.morphArray(fx._plot || element.array) /* add size */ if (fx._size) p.size(fx._size.width.to, fx._size.height.to) @@ -60,7 +60,7 @@ SVG.extend(SVG.FX, { delete fx._cy delete fx._size - fx._plot = element.points.morph(p) + fx._plot = element.array.morph(p) } } @@ -90,26 +90,34 @@ SVG.extend(SVG.FX, { typeof ease == 'function' ? ease(pos) : pos + + /* run plot function */ + if (fx._plot) { + element.plot(fx._plot.at(pos)) - /* run all x-position properties */ - if (fx._x) - element.x(fx._at(fx._x, pos)) - else if (fx._cx) - element.cx(fx._at(fx._cx, pos)) + } else { + if (element.array) + element.array.cache() - /* run all y-position properties */ - if (fx._y) - element.y(fx._at(fx._y, pos)) - else if (fx._cy) - element.cy(fx._at(fx._cy, pos)) + /* run all x-position properties */ + if (fx._x) + element.x(fx._at(fx._x, pos)) + else if (fx._cx) + element.cx(fx._at(fx._cx, pos)) - /* run all size properties */ - if (fx._size) - element.size(fx._at(fx._size.width, pos), fx._at(fx._size.height, pos)) + /* run all y-position properties */ + if (fx._y) + element.y(fx._at(fx._y, pos)) + else if (fx._cy) + element.cy(fx._at(fx._cy, pos)) - /* run plot function */ - if (fx._plot) - element.plot(fx._plot.at(pos)) + /* run all size properties */ + if (fx._size) + element.size(fx._at(fx._size.width, pos), fx._at(fx._size.height, pos)) + + if (element.array) + element.array.uncache() + } /* run all viewbox properties */ if (fx._viewbox) diff --git a/src/line.js b/src/line.js index a5a9b09..e049ccb 100755 --- a/src/line.js +++ b/src/line.js @@ -49,7 +49,9 @@ SVG.extend(SVG.Line, { } // Set line size by width and height , size: function(width, height) { - return this.width(width).height(height) + var p = this._proportionalSize(width, height) + + return this.width(p.width).height(p.height) } // Set path data , plot: function(x1, y1, x2, y2) { diff --git a/src/path.js b/src/path.js index e9f4735..60e6c73 100755 --- a/src/path.js +++ b/src/path.js @@ -1,75 +1,49 @@ -SVG.Path = function(unbiased) { +SVG.Path = function() { this.constructor.call(this, SVG.create('path')) - - this.unbiased = !!unbiased } // Inherit from SVG.Shape SVG.Path.prototype = new SVG.Shape SVG.extend(SVG.Path, { - // Move over x-axis - x: function(x) { - return x == null ? this.bbox().x : this.transform('x', x) + // Plot new poly points + plot: function(p) { + return this.attr('d', (this.array = new SVG.PathArray(p, [{ type:'M',x:0,y:0 }]))) } - // Move over y-axis -, y: function(y) { - return y == null ? this.bbox().y : this.transform('y', y) + // Move by left top corner +, move: function(x, y) { + return this.attr('d', this.array.move(x, y)) } - // Set width of element -, width: function(width) { - var b = this.bbox() - - return width == null ? b.width : this.size(width, b.height) + // Move by left top corner over x-axis +, x: function(x) { + return x == null ? this.bbox().x : this.move(x, this.bbox().y) } - // Set height of element -, height: function(height) { - var b = this.bbox() - - return height == null ? b.height : this.size(b.width, height) + // Move by left top corner over y-axis +, y: function(y) { + return y == null ? this.bbox().y : this.move(this.bbox().x, y) } - // Set the actual size in pixels + // Set element size to given width and height , size: function(width, height) { - var scale = width / this._offset.width + var p = this._proportionalSize(width, height) - return this.transform({ - scaleX: scale - , scaleY: height != null ? height / this._offset.height : scale - }) + return this.attr('d', this.array.size(p.width, p.height)) } - // Set path data -, plot: function(data) { - var x = this.trans.scaleX - , y = this.trans.scaleY - - /* native plot */ - this._plot(data) - - /* store offset */ - this._offset = this.transform({ scaleX: 1, scaleY: 1 }).bbox() - - /* get and store the actual offset of the element */ - if (this.unbiased) { - this._offset.x = this._offset.y = 0 - } else { - this._offset.x -= this.trans.x - this._offset.y -= this.trans.y - } - - return this.transform({ scaleX: x, scaleY: y }) + // Set width of element +, width: function(width) { + return width == null ? this.bbox().width : this.size(width, this.bbox().height) } - // Private: Native plot -, _plot: function(data) { - return this.attr('d', data || 'M0,0') + // Set height of element +, height: function(height) { + return height == null ? this.bbox().height : this.size(this.bbox().width, height) } - + }) // SVG.extend(SVG.Container, { // Create a wrapped path element - path: function(data, unbiased) { - return this.put(new SVG.Path(unbiased)).plot(data) + path: function(d) { + return this.put(new SVG.Path).plot(d) } }) \ No newline at end of file diff --git a/src/patharray.js b/src/patharray.js new file mode 100644 index 0000000..68252c8 --- /dev/null +++ b/src/patharray.js @@ -0,0 +1,296 @@ +// Path points array +SVG.PathArray = function(array, fallback) { + this.constructor.call(this, array, fallback) +} + +// Inherit from SVG.Array +SVG.PathArray.prototype = new SVG.Array + +SVG.extend(SVG.PathArray, { + // Convert array to string + toString: function() { + for (var s, i = 0, il = this.value.length, array = []; i < il; i++) { + s = [this.value[i].type] + + switch(this.value[i].type) { + case 'H': + s.push(this.value[i].x) + break + case 'V': + s.push(this.value[i].y) + break + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + if (/[QC]/.test(this.value[i].type)) + s.push(this.value[i].x1, this.value[i].y1) + if (/[CS]/.test(this.value[i].type)) + s.push(this.value[i].x2, this.value[i].y2) + + s.push(this.value[i].x, this.value[i].y) + + break + case 'A': + s.push( + this.value[i].rx + , this.value[i].ry + , this.value[i].angle + , this.value[i].largeArcFlag + , this.value[i].sweepFlag + , this.value[i].x + , this.value[i].y + ) + break + } + + /* add to array */ + array.push(s.join(' ')) + } + + return array.join(' ') + } + // Move path string +, move: function(x, y) { + /* get bounding box of current situation */ + var box = this.bbox() + + /* get relative offset */ + x -= box.x + y -= box.y + + if (!isNaN(x) && !isNaN(y)) { + /* move every point */ + for (var i = this.value.length - 1; i >= 0; i--) { + switch (this.value[i].type) { + case 'H': + /* move along x axis only */ + this.value[i].x += x + break + case 'V': + /* move along y axis only */ + this.value[i].y += y + break + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + /* move first point along x and y axes */ + this.value[i].x += x + this.value[i].y += y + + /* move third points along x and y axes */ + if (/[CQ]/.test(this.value[i].type)) { + this.value[i].x1 += x + this.value[i].y1 += y + } + + /* move second points along x and y axes */ + if (/[CS]/.test(this.value[i].type)) { + this.value[i].x2 += x + this.value[i].y2 += y + } + + break + case 'A': + /* only move position values */ + this.value[i].x += x + this.value[i].y += y + break + } + } + } + + return this + } + // Resize path string +, size: function(width, height) { + /* get bounding box of current situation */ + var box = this.bbox() + + /* recalculate position of all points according to new size */ + for (var i = this.value.length - 1; i >= 0; i--) { + switch (this.value[i].type) { + case 'H': + /* move along x axis only */ + this.value[i].x = ((this.value[i].x - box.x) * width) / box.width + box.x + break + case 'V': + /* move along y axis only */ + this.value[i].y = ((this.value[i].y - box.y) * height) / box.height + box.y + break + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + this.value[i].x = ((this.value[i].x - box.x) * width) / box.width + box.x + this.value[i].y = ((this.value[i].y - box.y) * height) / box.height + box.y + + /* move third points along x and y axes */ + if (/[CQ]/.test(this.value[i].type)) { + this.value[i].x1 = ((this.value[i].x1 - box.x) * width) / box.width + box.x + this.value[i].y1 = ((this.value[i].y1 - box.y) * height) / box.height + box.y + } + + /* move second points along x and y axes */ + if (/[CS]/.test(this.value[i].type)) { + this.value[i].x2 = ((this.value[i].x2 - box.x) * width) / box.width + box.x + this.value[i].y2 = ((this.value[i].y2 - box.y) * height) / box.height + box.y + } + + break + case 'A': + /* resize radii */ + this.value[i].values.rx = (this.value[i].values.rx * width) / box.width + this.value[i].values.ry = (this.value[i].values.ry * height) / box.height + + /* move position values */ + this.value[i].values.x = ((this.value[i].values.x - box.x) * width) / box.width + box.x + this.value[i].values.y = ((this.value[i].values.y - box.y) * height) / box.height + box.y + break + } + } + + return this + } + // Absolutize and parse path to array +, parse: function(array) { + array = array.valueOf() + + /* if already is an array, no need to parse it */ + if (Array.isArray(array)) return array + + /* prepare for parsing */ + var i, il, x0, y0, x1, y1, x2, y2, s, seg, segs + , x = 0 + , y = 0 + + /* populate working path */ + SVG.parser.path.setAttribute('d', array) + + /* get segments */ + segs = SVG.parser.path.pathSegList + + for (i = 0, il = segs.numberOfItems; i < il; ++i) { + seg = segs.getItem(i) + s = seg.pathSegTypeAsLetter + + if (/[MLHVCSQTA]/.test(s)) { + if ('x' in seg) x = seg.x + if ('y' in seg) y = seg.y + + } else { + if ('x1' in seg) x1 = x + seg.x1 + if ('x2' in seg) x2 = x + seg.x2 + if ('y1' in seg) y1 = y + seg.y1 + if ('y2' in seg) y2 = y + seg.y2 + if ('x' in seg) x += seg.x + if ('y' in seg) y += seg.y + + switch(s){ + case 'm': + segs.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(x, y), i) + break + case 'l': + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(x, y), i) + break + case 'h': + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(x), i) + break + case 'v': + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(y), i) + break + case 'c': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i) + break + case 's': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i) + break + case 'q': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i) + break + case 't': + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i) + break + case 'a': + segs.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i) + break + case 'z': + case 'Z': + x = x0 + y = y0 + break + } + } + + /* record the start of a subpath */ + if (/[Mm]/.test(s)) { + x0 = x + y0 = y + } + } + + /* build internal representation */ + array = [] + segs = SVG.parser.path.pathSegList + + for (i = 0, il = segs.numberOfItems; i < il; ++i) { + seg = segs.getItem(i) + s = {} + + switch (seg.pathSegTypeAsLetter) { + case 'M': + case 'L': + case 'T': + case 'S': + case 'Q': + case 'C': + if (/[QC]/.test(seg.pathSegTypeAsLetter)) { + s.x1 = seg.x1 + s.y1 = seg.y1 + } + + if (/[SC]/.test(seg.pathSegTypeAsLetter)) { + s.x2 = seg.x2 + s.y2 = seg.y2 + } + + break + case 'A': + s = { + r1: seg.r1 + , r2: seg.r2 + , angle: seg.angle + , largeArcFlag: seg.largeArcFlag + , sweepFlag: seg.sweepFlag + } + break + } + + /* make the letter, x and y values accessible as key/values */ + s.type = seg.pathSegTypeAsLetter + s.x = seg.x + s.y = seg.y + + /* store segment */ + array.push(s) + } + + return array + } + // Get bounding box of path +, bbox: function() { + if (this._cachedBBox) return this._cachedBBox + + SVG.parser.path.setAttribute('d', this.toString()) + + return SVG.parser.path.getBBox() + } + +}) \ No newline at end of file diff --git a/src/pointarray.js b/src/pointarray.js index 8df49ae..28dbf3e 100644 --- a/src/pointarray.js +++ b/src/pointarray.js @@ -76,31 +76,11 @@ SVG.extend(SVG.PointArray, { } // Get bounding box of points , bbox: function() { - if (this.value.length == 0) - return { x: 0, y: 0, width: 0, height: 0 } - - var i - , x = this.value[0][0] - , y = this.value[0][1] - , box = { x: x, y: y } - - /* find position */ - for (i = this.value.length - 1; i >= 0; i--) { - if (this.value[i][0] < box.x) - box.x = this.value[i][0] - if (this.value[i][1] < box.y) - box.y = this.value[i][1] - if (this.value[i][0] > x) - x = this.value[i][0] - if (this.value[i][1] > y) - y = this.value[i][1] - } + if (this._cachedBBox) return this._cachedBBox - /* calculate size */ - box.width = x - box.x - box.height = y - box.y + SVG.parser.poly.setAttribute('points', this.toString()) - return box + return SVG.parser.poly.getBBox() } }) \ No newline at end of file diff --git a/src/poly.js b/src/poly.js index 93a40fd..247ab8f 100755 --- a/src/poly.js +++ b/src/poly.js @@ -18,11 +18,11 @@ SVG.extend(SVG.Polyline, SVG.Polygon, { morphArray: SVG.PointArray // Plot new path , plot: function(p) { - return this.attr('points', (this.points = new SVG.PointArray(p, [[0,0]]))) + return this.attr('points', (this.array = new SVG.PointArray(p, [[0,0]]))) } // Move by left top corner , move: function(x, y) { - return this.attr('points', this.points.move(x, y)) + return this.attr('points', this.array.move(x, y)) } // Move by left top corner over x-axis , x: function(x) { @@ -46,7 +46,9 @@ SVG.extend(SVG.Polyline, SVG.Polygon, { } // Set element size to given width and height , size: function(width, height) { - return this.attr('points', this.points.size(width, height)) + var p = this._proportionalSize(width, height) + + return this.attr('points', this.array.size(p.width, p.height)) } }) diff --git a/src/svg.js b/src/svg.js index 89c23e4..abd3bc3 100755 --- a/src/svg.js +++ b/src/svg.js @@ -13,7 +13,8 @@ this.SVG = function(element) { } // Default namespaces -SVG.ns = 'http://www.w3.org/2000/svg' +SVG.ns = 'http://www.w3.org/2000/svg' +SVG.xmlns = 'http://www.w3.org/2000/xmlns/' SVG.xlink = 'http://www.w3.org/1999/xlink' // Element id sequence @@ -67,4 +68,33 @@ SVG.supported = (function() { !! document.createElementNS(SVG.ns,'svg').createSVGRect })() -if (!SVG.supported) return false \ No newline at end of file +if (!SVG.supported) return false + +// Initialize parsing element +SVG.parser = (function() { + /* select document body and create svg element*/ + var body = document.getElementsByTagName('body')[0] || document.getElementsByTagName('svg')[0] + , svg = SVG.create('svg') + , poly = SVG.create('polygon') + , path = SVG.create('path') + + /* make svg element presently invisible to ensure geometry */ + svg.setAttributeNS(SVG.xmlns, 'xmlns:xlink', SVG.xlink) + svg.setAttribute('style', 'opacity:0;position:fixed;left:100%;top:100%') + svg.setAttribute('width', '2') + svg.setAttribute('height', '2') + + /* build node structure */ + body.appendChild(svg) + svg.appendChild(poly) + svg.appendChild(path) + + /* return parser object */ + return { + body: body + , doc: svg + , poly: poly + , path: path + } + +})() \ No newline at end of file