]> source.dussan.org Git - svg.js.git/commitdiff
Completely reworked transform system
authorwout <wout@impinc.co.uk>
Fri, 11 Jul 2014 21:36:17 +0000 (23:36 +0200)
committerwout <wout@impinc.co.uk>
Fri, 11 Jul 2014 21:36:17 +0000 (23:36 +0200)
29 files changed:
CHANGELOG.md
Gemfile
Gemfile.lock
README.md
Rakefile
dist/svg.js
dist/svg.min.js
spec/index.html
spec/spec/container.js
spec/spec/element.js
spec/spec/matrix.js [new file with mode: 0644]
spec/spec/selector.js
src/attr.js [new file with mode: 0644]
src/bbox.js [deleted file]
src/boxes.js [new file with mode: 0755]
src/element.js
src/ellipse.js
src/group.js
src/helpers.js
src/line.js
src/matrix.js
src/patharray.js
src/rbox.js [deleted file]
src/regex.js
src/style.js [new file with mode: 0644]
src/sugar.js
src/transform.js [new file with mode: 0644]
src/transporter.js [new file with mode: 0644]
src/utilities.js

index e9c9e8e5b7ecd7a93df224a2155f60a02522655c..2e91b8b83f3990cf065cf80ed9e63f12e98ceb77 100755 (executable)
 - completely reworked `clone()` method to use the adoption system
 - added support to clone manually built text elements
 - added `svg.wiml.js` plugin to plugins list
+- added `ctm()` method to for matrix-centric transformations -> __TODO!__
 - completely reworked transformations to be chainable and more true to their nature -> __TODO!__
 - changed `lines` reference to `lines()` on `SVG.Text` -> __TODO!__
 - changed `track` reference to `track()` on `SVG.Text` -> __TODO!__
 - fixed a bug in clipping and masking where empty nodes persists after removal -> __TODO!__
 - added raw svg import functionality with the `svg()` method -> __TODO!__
+- moved sup-pixel offset fix to a separate plugin -> __TODO!__
+- added `SVG.Title` -> __TODO!__
+- added `native()` method to elements and matrix to get to the native api -> __TODO!__
+- added `untransform()` method to remove all transformations -> __TODO!__
 
 # 1.0.0-rc.9 (17/06/2014)
 
diff --git a/Gemfile b/Gemfile
index dd154696a53c962bc1739e6939b600e41c0c9771..ddbcde0a055d8629b20e730ed723eebd9af18f66 100755 (executable)
--- a/Gemfile
+++ b/Gemfile
@@ -1,4 +1,4 @@
-source :rubygems
+source 'https://rubygems.org'
 
 gem 'rake'
 gem 'uglifier'
\ No newline at end of file
index d39262a73588ff94e4703bf613a813c929095730..4650b9b5d6735814201edb033caeb413bd055b5c 100755 (executable)
@@ -1,5 +1,5 @@
 GEM
-  remote: http://rubygems.org/
+  remote: https://rubygems.org/
   specs:
     execjs (1.4.0)
       multi_json (~> 1.0)
index 06e27466817ecd61c125718bba52bf7a921ebfb8..ffcc79672652eb1d50d5c279da8522d52f65a695 100755 (executable)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# svg.js
+# SVG.js
 
 A lightweight library for manipulating and animating SVG.
 
@@ -6,7 +6,7 @@ Svg.js has no dependencies and aims to be as small as possible.
 
 Svg.js is licensed under the terms of the MIT License.
 
-See [svgjs.com](http://svgjs.com) for an introduction, [documentation](http://documentup.com/wout/svg.js) and [some action](http://svgjs.com/test).
+See [svgjs.com](http://svgjs.com) for an introduction, [documentation](http://documentup.com/wout/SVG.js) and [some action](http://svgjs.com/test).
 
 [![Wout on Gittip](http://files.wout.co.uk/github/gittip.png)](https://www.gittip.com/wout/)
 
@@ -77,7 +77,7 @@ var draw = SVG('drawing').fixSubPixelOffset()
 ## Parent elements
 
 ### Main svg document
-The main svg.js initializer function creates a root svg node in the given element and retuns an instance of `SVG.Doc`:
+The main SVG.js initializer function creates a root svg node in the given element and retuns an instance of `SVG.Doc`:
 
 ```javascript
 var draw = SVG('drawing')
@@ -101,7 +101,7 @@ __`returns`: `SVG.Nested`__
 _Javascript inheritance stack: `SVG.Nested` < `SVG.Container` < `SVG.Parent`_
 
 ### Groups
-Grouping elements is useful if you want to transform a set of elements as if it were one. All element within a group maintain their position relative to the group they belong to. A group has all the same element methods as the root svg document: 
+Grouping elements is useful if you want to transform a set of elements as if it were one. All element within a group maintain their position relative to the group they belong to. A group has all the same element methods as the root svg document:
 
 ```javascript
 var group = draw.group()
@@ -114,6 +114,8 @@ Existing elements from the svg document can also be added to a group:
 group.add(rect)
 ```
 
+__Note:__ Groups do not have a geometry of their own, it's inherited from their content. Therefore groups do not listen to `x`, `y`, `width` and `height` attributes. If that is what you are looking for, use a `nested()` svg instead.
+
 __`returns`: `SVG.G`__
 
 _Javascript inheritance stack: `SVG.G` < `SVG.Container` < `SVG.Parent`_
@@ -458,7 +460,7 @@ __`returns`: `itself`__
 
 
 ## Text
-Unlike html, text in svg is much harder to tame. There is no way to create flowing text, so newlines should be entered manually. In svg.js there are two ways to create text elements.
+Unlike html, text in svg is much harder to tame. There is no way to create flowing text, so newlines should be entered manually. In SVG.js there are two ways to create text elements.
 
 The first and easiest method is to provide a string of text, split by newlines:
 
@@ -543,7 +545,7 @@ text.font({
 __`returns`: `itself`__
 
 ### leading()
-As opposed to html, where leading is defined by `line-height`, svg does not have a natural leading equivalent. In svg, lines are not defined naturally. They are defined by `<tspan>` nodes with a `dy` attribute defining the line height and a `x` value resetting the line to the `x` position of the parent text element. But you can also have many nodes in one line defining a different `y`, `dy`, `x` or even `dx` value. This gives us a lot of freedom, but also a lot more responsibility. We have to decide when a new line is defined, where it starts, what its offset is and what it's height is. The `leading()` method in svg.js tries to ease the pain by giving you behaviour that is much closer to html. In combination with newline separated text, it works just like html:
+As opposed to html, where leading is defined by `line-height`, svg does not have a natural leading equivalent. In svg, lines are not defined naturally. They are defined by `<tspan>` nodes with a `dy` attribute defining the line height and a `x` value resetting the line to the `x` position of the parent text element. But you can also have many nodes in one line defining a different `y`, `dy`, `x` or even `dx` value. This gives us a lot of freedom, but also a lot more responsibility. We have to decide when a new line is defined, where it starts, what its offset is and what it's height is. The `leading()` method in SVG.js tries to ease the pain by giving you behaviour that is much closer to html. In combination with newline separated text, it works just like html:
 
 ```javascript
 var text = draw.text("Lorem ipsum dolor sit amet consectetur.\nCras sodales imperdiet auctor.")
@@ -617,7 +619,7 @@ text.on('rebuild', function() {
 ```
 
 ## Tspan
-The tspan elements are only available inside text elements or inside other tspan elements. In svg.js they have a class of their own:
+The tspan elements are only available inside text elements or inside other tspan elements. In SVG.js they have a class of their own:
 
 _Javascript inheritance stack: `SVG.Tspan` < `SVG.Shape` < `SVG.Element`_
 
@@ -799,7 +801,7 @@ _Javascript inheritance stack: `SVG.Use` < `SVG.Container` < `SVG.Symbol`_
 ## Referencing elements
 
 ### By id
-If you want to get an element created by svg.js by its id, you can use the `SVG.get()` method:
+If you want to get an element created by SVG.js by its id, you can use the `SVG.get()` method:
 
 ```javascript
 var element = SVG.get('my_element')
@@ -840,7 +842,7 @@ var elements = $('#drawing g.my-group .my-element').each(function() {
 ```
 
 ## Circular reference
-Every element instance within svg.js has a reference to the actual `node`:
+Every element instance within SVG.js has a reference to the actual `node`:
 
 ### node
 ```javascript
@@ -849,7 +851,7 @@ element.node
 __`returns`: `node`__
 
 ### instance
-Similarly, the node carries a reference to the svg.js `instance`:
+Similarly, the node carries a reference to the SVG.js `instance`:
 
 ```javascript
 node.instance
@@ -1055,7 +1057,7 @@ The `transform()` can act both as a getter or a setter. Let's start with the for
 
 Transforms are cascading sequentially. Every transform you add will build further on the effects of all the previous transforms together. So for example, when you add the `translate(10, 10)` transform three times, the total translation will equal `translate(30, 30)`.
 
-We are going to use one node as an examle for this section:
+We are going to use one node as an example for this section:
 
 ```xml
 <rect width="100" height="100" transform="translate(10, 10) rotate(45, 100, 100) scale(0.5, 0.5) rotate(-10)" />
@@ -1549,6 +1551,15 @@ path.rbox()
 
 __`returns`: `SVG.RBox`__
 
+### ctm()
+Retreives the current transform matrix of the element:
+
+```javascript
+path.ctm()
+```
+
+__`returns`: `SVG.Matrix`__
+
 ### inside()
 To check if a given point is inside the bounding box of an element you can use the `inside()` method:
 
@@ -2735,7 +2746,7 @@ _Important: always make sure you namespace your event to avoid conflicts. Prefer
 
 ## Numbers
 
-Numbers in svg.js have a dedicated number class to be able to process string values. Creating a new number is simple:
+Numbers in SVG.js have a dedicated number class to be able to process string values. Creating a new number is simple:
 
 ```javascript
 var number = new SVG.Number('78%')
@@ -2873,7 +2884,7 @@ __`returns`: `SVG.Color`__
 
 
 ## 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.
+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:
@@ -2961,37 +2972,37 @@ path.array() //-> returns the SVG.PathArray instance
 The syntax for patharrays is very predictable. They are basically literal representations in the form of two dimentional arrays.
 
 ##### Move To
-Original syntax is `M0 0` or `m0 0`. The svg.js syntax `['M',0,0]` or `['m',0,0]`.
+Original syntax is `M0 0` or `m0 0`. The SVG.js syntax `['M',0,0]` or `['m',0,0]`.
 
 ##### Line To
-Original syntax is `L100 100` or `l100 100`. The svg.js syntax `['L',100,100]` or `['l',100,100]`.
+Original syntax is `L100 100` or `l100 100`. The SVG.js syntax `['L',100,100]` or `['l',100,100]`.
 
 ##### Horizontal line
-Original syntax is `H200` or `h200`. The svg.js syntax `['H',200]` or `['h',200]`.
+Original syntax is `H200` or `h200`. The SVG.js syntax `['H',200]` or `['h',200]`.
 
 ##### Vertical line
-Original syntax is `V300` or `v300`. The svg.js syntax `['V',300]` or `['v',300]`.
+Original syntax is `V300` or `v300`. The SVG.js syntax `['V',300]` or `['v',300]`.
 
 ##### Bezier curve
-Original syntax is `C20 20 40 20 50 10` or `c20 20 40 20 50 10`. The svg.js syntax `['C',20,20,40,20,50,10]` or `['c',20,20,40,20,50,10]`.
+Original syntax is `C20 20 40 20 50 10` or `c20 20 40 20 50 10`. The SVG.js syntax `['C',20,20,40,20,50,10]` or `['c',20,20,40,20,50,10]`.
 
 Or mirrored with `S`:
 
-Original syntax is `S40 20 50 10` or `s40 20 50 10`. The svg.js syntax `['S',40,20,50,10]` or `['s',40,20,50,10]`.
+Original syntax is `S40 20 50 10` or `s40 20 50 10`. The SVG.js syntax `['S',40,20,50,10]` or `['s',40,20,50,10]`.
 
 Or quadratic with `Q`:
 
-Original syntax is `Q20 20 50 10` or `q20 20 50 10`. The svg.js syntax `['Q',20,20,50,10]` or `['q',20,20,50,10]`.
+Original syntax is `Q20 20 50 10` or `q20 20 50 10`. The SVG.js syntax `['Q',20,20,50,10]` or `['q',20,20,50,10]`.
 
 Or a complete shortcut with `T`:
 
-Original syntax is `T50 10` or `t50 10`. The svg.js syntax `['T',50,10]` or `['t',50,10]`.
+Original syntax is `T50 10` or `t50 10`. The SVG.js syntax `['T',50,10]` or `['t',50,10]`.
 
 ##### Arc
-Original syntax is `A 30 50 0 0 1 162 163` or `a 30 50 0 0 1 162 163`. The svg.js syntax `['A',30,50,0,0,1,162,163]` or `['a',30,50,0,0,1,162,163]`.
+Original syntax is `A 30 50 0 0 1 162 163` or `a 30 50 0 0 1 162 163`. The SVG.js syntax `['A',30,50,0,0,1,162,163]` or `['a',30,50,0,0,1,162,163]`.
 
 ##### Close
-Original syntax is `Z` or `z`. The svg.js syntax `['Z']` or `['z']`.
+Original syntax is `Z` or `z`. The SVG.js syntax `['Z']` or `['z']`.
 
 The best documentation on paths can be found at https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths.
 
@@ -2999,7 +3010,7 @@ The best documentation on paths can be found at https://developer.mozilla.org/en
 _Javascript inheritance stack: `SVG.PathArray` < `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:
+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
 var array = new SVG.PointArray([[0, 0], [100, 100]])
@@ -3083,10 +3094,81 @@ Note that this method is only available on `SVG.PointArray` and `SVG.PathArray`
 __`returns`: `object`__
 
 
+## Matrices
+Matrices in SVG.js have their own class `SVG.Matrix`, wrapping the native `SVGMatrix`. They add a lot of functionality like extracting transform values, matrix morphing and improvements on the native methods.
+
+### SVG.Matrix
+In SVG.js matrices accept various values on initialization.
+
+Without a value:
+
+```javascript
+var matrix = new SVG.Matrix
+matrix.toString() //-> returns matrix(1,0,0,1,0,0)
+```
+
+Six arguments:
+
+```javascript
+var matrix = new SVG.Matrix(1, 0, 0, 1, 100, 150)
+matrix.toString() //-> returns matrix(1,0,0,1,100,150)
+```
+
+A string value:
+
+```javascript
+var matrix = new SVG.Matrix('1,0,0,1,100,150')
+matrix.toString() //-> returns matrix(1,0,0,1,100,150)
+```
+
+An object value:
+
+```javascript
+var matrix = new SVG.Matrix({ a: 1, b: 0, c: 0, d: 1, e: 100, f: 150 })
+matrix.toString() //-> returns matrix(1,0,0,1,100,150)
+```
+
+A native `SVGMatrix`:
+
+```javascript
+var svgMatrix = svgElement.getCTM()
+var matrix = new SVG.Matrix(svgMatrix)
+matrix.toString() //-> returns matrix(1,0,0,1,0,0)
+```
+
+Even an instance of `SVG.Element`:
+
+```javascript
+var rect = draw.rect(50, 25)
+var matrix = new SVG.Matrix(rect)
+matrix.toString() //-> returns matrix(1,0,0,1,0,0)
+```
+
+### extract()
+Get the calculated values of matrix:
+
+```javascript
+new SVG.Matrix().extract()
+```
+
+returns:
+```javascript
+{
+  x:        0
+, y:        0
+, skewX:    0
+, skewY:    0
+, scaleX:   1
+, scaleY:   1
+, rotation: 0
+}
+```
+
+
 ## Extending functionality
 
 ### SVG.invent()
-Creating your own custom elements with svg.js is piece of cake thanks to the `SVG.invent` function. For the sake of this example, lets "invent" a shape. We want a `rect` with rounded corners that are always proportional to the height of the element. The new shape lives in the `SVG` namespace and is called `Rounded`. Here is how we achieve that.
+Creating your own custom elements with SVG.js is piece of cake thanks to the `SVG.invent` function. For the sake of this example, lets "invent" a shape. We want a `rect` with rounded corners that are always proportional to the height of the element. The new shape lives in the `SVG` namespace and is called `Rounded`. Here is how we achieve that.
 
 ```javascript
 SVG.Rounded = SVG.invent({
@@ -3132,10 +3214,10 @@ That's it, the invention is now ready to be used!
 The `SVG.invent()` function always expectes an object. The object can have the following configuration values:
 
 - `create`: can be either a string with the node name (e.g. `rect`, `ellipse`, ...) or a custom initializer function; `[required]`
-- `inherit`: the desired svg.js class to inherit from (e.g. `SVG.Shape`, `SVG.Element`, `SVG.Container`, `SVG.Rect`, ...); `[optional but recommended]`
+- `inherit`: the desired SVG.js class to inherit from (e.g. `SVG.Shape`, `SVG.Element`, `SVG.Container`, `SVG.Rect`, ...); `[optional but recommended]`
 - `extend`: an object with the methods that should be applied to the element's prototype; `[optional]`
 - `construct`: an objects with the methods to create the element on the parent element; `[optional]`
-- `parent`: an svg.js parent class on which the methods in the passed `construct` object should be available; `[optional]`
+- `parent`: an SVG.js parent class on which the methods in the passed `construct` object should be available; `[optional]`
 
 Svg.js uses the `SVG.invent()` function to create all internal elements, so have a look at the source to see how this function is used in various ways.
 
@@ -3188,10 +3270,10 @@ SVG.extend(SVG.Ellipse, SVG.Path, SVG.Polygon, {
 
 
 ## Plugins
-Here are a few nice plugins that are available for svg.js:
+Here are a few nice plugins that are available for SVG.js:
 
 ### absorb
-[svg.absorb.js](https://github.com/wout/svg.absorb.js) absorb raw SVG data into a svg.js instance.
+[svg.absorb.js](https://github.com/wout/svg.absorb.js) absorb raw SVG data into a SVG.js instance.
 
 ### draggable
 [svg.draggable.js](https://github.com/wout/svg.draggable.js) to make elements draggable.
@@ -3243,7 +3325,7 @@ Be aware that pull requests without specs will be declined.
 
 
 ## Building
-Starting out with the default distribution of svg.js is good. Although you might want to remove some modules to keep the size at minimum.
+Starting out with the default distribution of SVG.js is good. Although you might want to remove some modules to keep the size at minimum.
 
 You will need ruby, RubyGems, and rake installed on your system.
 
@@ -3257,7 +3339,7 @@ $ rake -V
 $ gem install uglifier
 ```
 
-Build svg.js by running `rake`:
+Build SVG.js by running `rake`:
 
 ``` sh
 $ rake
@@ -3268,7 +3350,7 @@ Minified and gzipped: 4.413k, compression factor 7.289
 
 The resulting files are:
 
-1. `dist/svg.js`
+1. `dist/SVG.js`
 2. `dist/svg.min.js`
 
 To include optional modules and remove default ones, use the `concat` task. In
@@ -3301,6 +3383,6 @@ rake concat[-fx:-event:-group:-arrange:-mask:-gradient:-nested:-sugar] dist
 - Chrome for Android 18+
 - Firefox for Android 15+
 
-Visit the [svg.js test page](http://svgjs.com/test) if you want to check compatibility with different browsers.
+Visit the [SVG.js test page](http://svgjs.com/test) if you want to check compatibility with different browsers.
 
 Important: this library is still in beta, therefore the API might be subject to change in the course of development.
index 27e8d76234112eee83d00aba520f8f09b1aa4eb8..f5ce78aa655ae5e2e0eef7c370a2527fdea562f3 100755 (executable)
--- a/Rakefile
+++ b/Rakefile
@@ -1,7 +1,7 @@
 SVGJS_VERSION = '1.0.0-rc.10'
 
 # all available modules in the correct loading order
-MODULES = %w[ svg inventor adopter regex utilities default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient pattern doc spof shape symbol use rect ellipse line poly pointed path image text textpath nested hyperlink marker sugar set data memory selector loader helpers polyfill ]
+MODULES = %w[ svg inventor adopter regex utilities default color array pointarray patharray number viewbox element boxes matrix attr transform style parent container transporter fx relative event defs group arrange mask clip gradient pattern doc spof shape symbol use rect ellipse line poly pointed path image text textpath nested hyperlink marker sugar set data memory selector loader helpers polyfill ]
 
 # how many bytes in a "kilobyte"
 KILO = 1024
index a11987bbc7fa318c565add828f226f155e10b3ef..8dd3a395e52ac959a11eaa6d9d9d8014db314854 100755 (executable)
@@ -1,4 +1,4 @@
-/* svg.js 1.0.0-rc.10-17-g0d11ad2 - svg inventor adopter regex utilities default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient pattern doc spof shape symbol use rect ellipse line poly pointed path image text textpath nested hyperlink marker sugar set data memory selector loader helpers polyfill - svgjs.com/license */
+/* svg.js 1.0.0-rc.10-19-g7cc2d36 - svg inventor adopter regex utilities default color array pointarray patharray number viewbox element boxes matrix attr transform style parent container transporter fx relative event defs group arrange mask clip gradient pattern doc spof shape symbol use rect ellipse line poly pointed path image text textpath nested hyperlink marker sugar set data memory selector loader helpers polyfill - svgjs.com/license */
 ;(function() {
 
   var SVG = this.SVG = function(element) {
 
   SVG.regex = {
     /* parse unit value */
-    unit:         /^(-?[\d\.]+)([a-z%]{0,2})$/
+    unit:             /^(-?[\d\.]+)([a-z%]{0,2})$/
     
     /* parse hex value */
-  , hex:          /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
+  , hex:              /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
     
     /* parse rgb value */
-  , rgb:          /rgb\((\d+),(\d+),(\d+)\)/
+  , rgb:              /rgb\((\d+),(\d+),(\d+)\)/
     
     /* parse reference id */
-  , reference:    /#([a-z0-9\-_]+)/i
+  , reference:        /#([a-z0-9\-_]+)/i
   
     /* test hex value */
-  , isHex:        /^#[a-f0-9]{3,6}$/i
+  , isHex:            /^#[a-f0-9]{3,6}$/i
     
     /* test rgb value */
-  , isRgb:        /^rgb\(/
+  , isRgb:            /^rgb\(/
     
     /* test css declaration */
-  , isCss:        /[^:]+:[^;]+;?/
+  , isCss:            /[^:]+:[^;]+;?/
     
     /* test for blank string */
-  , isBlank:      /^(\s+)?$/
+  , isBlank:          /^(\s+)?$/
     
     /* test for numeric string */
-  , isNumber:     /^-?[\d\.]+$/
+  , isNumber:         /^-?[\d\.]+$/
   
     /* test for percent value */
-  , isPercent:    /^-?[\d\.]+%$/
+  , isPercent:        /^-?[\d\.]+%$/
   
     /* test for image url */
-  , isImage:      /\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i
+  , isImage:          /\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i
     
     /* test for namespaced event */
-  , isEvent:      /^[\w]+:[\w]+$/
+  , isEvent:          /^[\w]+:[\w]+$/
   
   }
 
       return result
     }
   
+    // Degrees to radians
+  , radians: function(d) {
+               return d % 360 * Math.PI / 180
+       }
+       // Radians to degrees
+  , degrees: function(r) {
+               return r * 180 / Math.PI % 360
+       }
+  
   }
 
   SVG.defaults = {
         else if (s == 'Q')
           x.push(seg.x1, seg.y1, seg.x, seg.y)
         else if (s == 'A')
-          x.push(seg.r1, seg.r2, seg.angle, seg.largeArcFlag|0, seg.sweepFlag|0, seg.x, seg.y)
+          x.push(seg.r1, seg.r2, seg.angle, seg.largeArcFlag | 0, seg.sweepFlag | 0, seg.x, seg.y)
   
         /* store segment */
         array.push(x)
     
   })
 
-  SVG.BBox = function(element) {
-    var box
-  
-    // Initialize zero box
-    this.x      = 0
-    this.y      = 0
-    this.width  = 0
-    this.height = 0
-    
-    // Get values if element is given
-    if (element) {
-      try {
-        // Actual, native bounding box
-        box = element.node.getBBox()
-      } catch(e) {
-        // Fallback for some browsers
-        box = {
-          x:      element.node.clientLeft
-        , y:      element.node.clientTop
-        , width:  element.node.clientWidth
-        , height: element.node.clientHeight
-        }
-      }
-      
-      // Include translations on x an y
-      this.x = box.x + element.trans.x
-      this.y = box.y + element.trans.y
-      
-      // Plain width and height
-      this.width  = box.width  * element.trans.scaleX
-      this.height = box.height * element.trans.scaleY
-    }
-  
-    // Add center, right and bottom
-    boxProperties(this)
-    
-  }
-  
-  //
-  SVG.extend(SVG.BBox, {
-    // merge bounding box with another, return a new instance
-    merge: function(box) {
-      var b = new SVG.BBox
-  
-      // Merge box
-      b.x      = Math.min(this.x, box.x)
-      b.y      = Math.min(this.y, box.y)
-      b.width  = Math.max(this.x + this.width,  box.x + box.width)  - b.x
-      b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
-  
-      return boxProperties(b)
-    }
-  
-  })
-
-  SVG.RBox = function(element) {
-    var e, zoom
-      , box = {}
-  
-    /* initialize zero box */
-    this.x      = 0
-    this.y      = 0
-    this.width  = 0
-    this.height = 0
-    
-    if (element) {
-      e = element.doc().parent()
-      zoom = element.doc().viewbox().zoom
-      
-      /* actual, native bounding box */
-      box = element.node.getBoundingClientRect()
-      
-      /* get screen offset */
-      this.x = box.left
-      this.y = box.top
-      
-      /* subtract parent offset */
-      this.x -= e.offsetLeft
-      this.y -= e.offsetTop
-      
-      while (e = e.offsetParent) {
-        this.x -= e.offsetLeft
-        this.y -= e.offsetTop
-      }
-      
-      /* calculate cumulative zoom from svg documents */
-      e = element
-      while (e.parent && (e = e.parent())) {
-        if (e.type == 'svg' && e.viewbox) {
-          zoom *= e.viewbox().zoom
-          this.x -= e.x() || 0
-          this.y -= e.y() || 0
-        }
-      }
-    }
-    
-    /* recalculate viewbox distortion */
-    this.x /= zoom
-    this.y /= zoom
-    this.width  = box.width  /= zoom
-    this.height = box.height /= zoom
-    
-    /* offset by window scroll position, because getBoundingClientRect changes when window is scrolled */
-    this.x += window.scrollX
-    this.y += window.scrollY
-  
-    /* add center, right and bottom */
-    boxProperties(this)
-    
-  }
-  
-  //
-  SVG.extend(SVG.RBox, {
-    // merge rect box with another, return a new instance
-    merge: function(box) {
-      var b = new SVG.RBox()
-  
-      /* merge box */
-      b.x      = Math.min(this.x, box.x)
-      b.y      = Math.min(this.y, box.y)
-      b.width  = Math.max(this.x + this.width,  box.x + box.width)  - b.x
-      b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
-      
-      return boxProperties(b)
-    }
-  
-  })
-
-
   SVG.Element = SVG.invent({
     // Initialize node
     create: function(node) {
       x: function(x) {
         if (x != null) {
           x = new SVG.Number(x)
-          x.value /= this.trans.scaleX
+          x.value /= this.ctm().extract().scaleX
         }
         return this.attr('x', x)
       }
     , y: function(y) {
         if (y != null) {
           y = new SVG.Number(y)
-          y.value /= this.trans.scaleY
+          y.value /= this.ctm().extract().scaleY
         }
         return this.attr('y', y)
       }
     , putIn: function(parent) {
         return parent.add(this)
       }
-      // Get parent document
-    , doc: function(type) {
-        return this.parent(type || SVG.Doc)
-      }
-      // Set svg element attribute
-    , attr: function(a, v, n) {
-        // Act as full getter
-        if (a == null) {
-          // Get an object of attributes
-          a = {}
-          v = this.node.attributes
-          for (n = v.length - 1; n >= 0; n--)
-            a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue
-          
-          return a
-          
-        } else if (typeof a == 'object') {
-          // Apply every attribute individually if an object is passed
-          for (v in a) this.attr(v, a[v])
-          
-        } else if (v === null) {
-            // Remove value
-            this.node.removeAttribute(a)
-          
-        } else if (v == null) {
-          // Act as a getter if the first and only argument is not an object
-          v = this.node.getAttribute(a)
-          return v == null ? 
-            SVG.defaults.attrs[a] :
-          SVG.regex.isNumber.test(v) ?
-            parseFloat(v) : v
-        
-        } else {
-          // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0
-          if (a == 'stroke-width')
-            this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null)
-          else if (a == 'stroke')
-            this._stroke = v
-  
-          // Convert image fill and stroke to patterns
-          if (a == 'fill' || a == 'stroke') {
-            if (SVG.regex.isImage.test(v))
-              v = this.doc().defs().image(v, 0, 0)
-  
-            if (v instanceof SVG.Image)
-              v = this.doc().defs().pattern(0, 0, function() {
-                this.add(v)
-              })
-          }
-          
-          // Ensure correct numeric values (also accepts NaN and Infinity)
-          if (typeof v === 'number')
-            v = new SVG.Number(v)
-  
-          // Ensure full hex color
-          else if (SVG.Color.isColor(v))
-            v = new SVG.Color(v)
-          
-          // Parse array values
-          else if (Array.isArray(v))
-            v = new SVG.Array(v)
-  
-          // If the passed attribute is leading...
-          if (a == 'leading') {
-            // ... call the leading method instead
-            if (this.leading)
-              this.leading(v)
-          } else {
-            // Set given attribute on node
-            typeof n === 'string' ?
-              this.node.setAttributeNS(n, a, v.toString()) :
-              this.node.setAttribute(a, v.toString())
-          }
-          
-          // Rebuild if required
-          if (this.rebuild && (a == 'font-size' || a == 'x'))
-            this.rebuild(a, v)
-        }
-        
-        return this
-      }
-      // Manage transformations
-    , transform: function(t, v) {
-        // Get a transformation at a given position
-        if (typeof t === 'number') {
-          
-        }
-          
-        return this
-      }
-      // Dynamic style generator
-    , style: function(s, v) {
-        if (arguments.length == 0) {
-          /* get full style */
-          return this.node.style.cssText || ''
-        
-        } else if (arguments.length < 2) {
-          /* apply every style individually if an object is passed */
-          if (typeof s == 'object') {
-            for (v in s) this.style(v, s[v])
-          
-          } else if (SVG.regex.isCss.test(s)) {
-            /* parse css string */
-            s = s.split(';')
-  
-            /* apply every definition individually */
-            for (var i = 0; i < s.length; i++) {
-              v = s[i].split(':')
-              this.style(v[0].replace(/\s+/g, ''), v[1])
-            }
-          } else {
-            /* act as a getter if the first and only argument is not an object */
-            return this.node.style[camelCase(s)]
-          }
-        
-        } else {
-          this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v
-        }
-        
-        return this
-      }
       // Get / set id
     , id: function(id) {
         return this.attr('id', id)
       }
-      // Get bounding box
-    , bbox: function() {
-        return new SVG.BBox(this)
-      }
-      // Get rect box
-    , rbox: function() {
-        return new SVG.RBox(this)
-      }
       // Checks whether the given point inside the bounding box of the element
     , inside: function(x, y) {
         var box = this.bbox()
       // Remove class from the node
     , removeClass: function(name) {
         if (this.hasClass(name)) {
-          var array = this.classes().filter(function(c) {
+          this.attr('class', this.classes().filter(function(c) {
             return c != name
-          })
-          this.attr('class', array.join(' '))
+          }).join(' '))
         }
   
         return this
   
         return parent
       }
+      // Get parent document
+    , doc: function(type) {
+        return this.parent(type || SVG.Doc)
+      }
+      // Returns the svg node to call native svg methods on it
+    , native: function() {
+        return this.node
+      }
+    }
+  })
+
+
+  SVG.BBox = SVG.invent({
+    // Initialize
+    create: function(element) {
+      var box
+  
+      // Initialize zero box
+      this.x      = 0
+      this.y      = 0
+      this.width  = 0
+      this.height = 0
+      
+      // Get values if element is given
+      if (element) {
+        // Get current extracted transformations
+        var t = new SVG.Matrix(element).extract()
+        
+        // Find native bbox
+        if (element.node.getBBox)
+          box = element.node.getBBox()
+        // Mimic bbox
+        else
+          box = {
+            x:      element.node.clientLeft
+          , y:      element.node.clientTop
+          , width:  element.node.clientWidth
+          , height: element.node.clientHeight
+          }
+        
+        // Include translations on x an y
+        this.x = box.x + t.x
+        this.y = box.y + t.y
+        
+        // Plain width and height
+        this.width  = box.width  * t.scaleX
+        this.height = box.height * t.scaleY
+      }
+  
+      // Add center, right and bottom
+      fullBox(this)
+    }
+  
+    // define Parent
+  , parent: SVG.Element
+  
+    // Constructor
+  , construct: {
+      // Get bounding box
+      bbox: function() {
+        return new SVG.BBox(this)
+      }
+    }
+  
+  })
+  
+  SVG.RBox = SVG.invent({
+    // Initialize
+    create: function(element) {
+      var e, zoom
+        , box = {}
+  
+      // Initialize zero box
+      this.x      = 0
+      this.y      = 0
+      this.width  = 0
+      this.height = 0
+      
+      if (element) {
+        e = element.doc().parent()
+        zoom = element.doc().viewbox().zoom
+        
+        // Actual, native bounding box
+        box = element.node.getBoundingClientRect()
+        
+        // Get screen offset
+        this.x = box.left
+        this.y = box.top
+        
+        // Subtract parent offset
+        this.x -= e.offsetLeft
+        this.y -= e.offsetTop
+        
+        while (e = e.offsetParent) {
+          this.x -= e.offsetLeft
+          this.y -= e.offsetTop
+        }
+        
+        // Calculate cumulative zoom from svg documents
+        e = element
+        while (e.parent && (e = e.parent())) {
+          if (e.viewbox) {
+            zoom *= e.viewbox().zoom
+            this.x -= e.x() || 0
+            this.y -= e.y() || 0
+          }
+        }
+      }
+      
+      // Recalculate viewbox distortion
+      this.x /= zoom
+      this.y /= zoom
+      this.width  = box.width  /= zoom
+      this.height = box.height /= zoom
+      
+      // Offset by window scroll position, because getBoundingClientRect changes when window is scrolled
+      this.x += window.scrollX
+      this.y += window.scrollY
+  
+      // Add center, right and bottom
+      fullBox(this)
+    }
+  
+    // define Parent
+  , parent: SVG.Element
+  
+    // Constructor
+  , construct: {
+      // Get rect box
+      rbox: function() {
+        return new SVG.RBox(this)
+      }
     }
+  
+  })
+  
+  // Add universal merge method
+  ;[SVG.BBox, SVG.RBox].forEach(function(c) {
+  
+    SVG.extend(c, {
+      // Merge rect box with another, return a new instance
+      merge: function(box) {
+        var b = new c()
+  
+        // Merge box
+        b.x      = Math.min(this.x, box.x)
+        b.y      = Math.min(this.y, box.y)
+        b.width  = Math.max(this.x + this.width,  box.x + box.width)  - b.x
+        b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
+        
+        return fullBox(b)
+      }
+  
+    })
+  
+  })
+
+
+  SVG.Matrix = SVG.invent({
+       // Initialize
+       create: function(source) {
+               var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0])
+  
+               // Ensure source as object
+               source = source.node && source.node.getCTM ?
+                       source.node.getCTM() :
+               typeof source === 'string' ?
+                       arrayToMatrix(source.replace(/\s/g, '').split(',')) :
+               arguments.length == 6 ?
+                       arrayToMatrix([].slice.call(arguments)) :
+               typeof source === 'object' ?
+                       source : base
+  
+               // Merge source
+               for (i = abcdef.length - 1; i >= 0; i--)
+                       this[abcdef[i]] = typeof source[abcdef[i]] === 'number' ?
+                               source[abcdef[i]] : base[abcdef[i]]
+               
+       }
+       
+       // Add methods
+  , extend: {
+               // Extract individual transformations
+         extract: function() {
+                       // Find transform points
+                       var px = deltaTransformPoint(this, { x: 0, y: 1 })
+                               , py = deltaTransformPoint(this, { x: 1, y: 0 })
+       
+                       return {
+                               // Translation
+                               x:                              this.e
+                       , y:                            this.f
+                               // Skew
+                       , skewX:                180 / Math.PI * Math.atan2(px.y, px.x) - 90
+                       , skewY:                180 / Math.PI * Math.atan2(py.y, py.x)
+                               // Scale
+                       , scaleX:       Math.sqrt(this.a * this.a + this.b * this.b)
+                       , scaleY:       Math.sqrt(this.c * this.c + this.d * this.d)
+                               // Rotation
+                       , rotation: this.skewX
+                       }
+               }
+               // Multiply
+       , multiply: function(matrix) {
+                       return new SVG.Matrix(this.native().multiply(matrix.native()))
+               }
+               // Inverse
+       , inverse: function() {
+                       return new SVG.Matrix(this.native().inverse())
+               }
+               // Translate
+       , translate: function(x, y) {
+                       return new SVG.Matrix(this.native().translate(x || 0, y || 0))  
+               }
+               // Scale
+       , scale: function(x, y, cx, cy) {
+                       if (y == null)
+                               return new SVG.Matrix(this.native().scale(x))
+                       else
+                               return new SVG.Matrix(this.native().scaleNonUniform(x, y))
+               }
+               // Rotate
+       , rotate: function(d, x, y) {
+                       // Fall back to native rotate method 
+                       if (x == null) return new SVG.Matrix(this.native().rotate(d))
+  
+                       // Convert degrees to radians
+                       d = SVG.utils.radians(d)
+  
+                       return new SVG.Matrix(1, 0, 0, 1, x, y)
+                               //.multiply(new SVG.Matrix(Math.cos(d), Math.sin(d), -Math.sin(d), Math.cos(d), 0, 0))
+                               //.multiply(new SVG.Matrix(1, 0, 0, 1, -x, -y))
+               }
+               // Flip
+       , flip: function(a) {
+                       return new SVG.Matrix(this.native()['flip' + a.toUpperCase()]())
+               }
+               // Skew
+       , skew: function(x, y) {
+                       return new SVG.Matrix(this.native().skewX(x || 0).skewY(y || 0))
+               }
+               // Convert this to SVGMatrix
+       , native: function() {
+                       // Create new matrix
+                       var i, matrix = SVG.parser.draw.node.createSVGMatrix()
+       
+                       // Update with current values
+                       for (i = abcdef.length - 1; i >= 0; i--)
+                               matrix[abcdef[i]] = this[abcdef[i]]
+  
+                       return matrix
+               }
+               // Convert array to string
+       , toString: function() {
+                       return 'matrix(' + [this.a, this.b, this.c, this.d, this.e, this.f].join() + ')'
+               }
+       }
+  
+       // Define parent
+  , parent: SVG.Element
+  
+       // Add parent method
+  , construct: {
+               // Get current matrix
+               ctm: function() {
+                       return new SVG.Matrix(this)
+               }
+       
+       }
+  
   })
 
+  SVG.extend(SVG.Element, {
+    // Set svg element attribute
+    attr: function(a, v, n) {
+      // Act as full getter
+      if (a == null) {
+        // Get an object of attributes
+        a = {}
+        v = this.node.attributes
+        for (n = v.length - 1; n >= 0; n--)
+          a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue
+        
+        return a
+        
+      } else if (typeof a == 'object') {
+        // Apply every attribute individually if an object is passed
+        for (v in a) this.attr(v, a[v])
+        
+      } else if (v === null) {
+          // Remove value
+          this.node.removeAttribute(a)
+        
+      } else if (v == null) {
+        // Act as a getter if the first and only argument is not an object
+        v = this.node.getAttribute(a)
+        return v == null ? 
+          SVG.defaults.attrs[a] :
+        SVG.regex.isNumber.test(v) ?
+          parseFloat(v) : v
+      
+      } else {
+        // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0
+        if (a == 'stroke-width')
+          this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null)
+        else if (a == 'stroke')
+          this._stroke = v
+  
+        // Convert image fill and stroke to patterns
+        if (a == 'fill' || a == 'stroke') {
+          if (SVG.regex.isImage.test(v))
+            v = this.doc().defs().image(v, 0, 0)
+  
+          if (v instanceof SVG.Image)
+            v = this.doc().defs().pattern(0, 0, function() {
+              this.add(v)
+            })
+        }
+        
+        // Ensure correct numeric values (also accepts NaN and Infinity)
+        if (typeof v === 'number')
+          v = new SVG.Number(v)
+  
+        // Ensure full hex color
+        else if (SVG.Color.isColor(v))
+          v = new SVG.Color(v)
+        
+        // Parse array values
+        else if (Array.isArray(v))
+          v = new SVG.Array(v)
+  
+        // If the passed attribute is leading...
+        if (a == 'leading') {
+          // ... call the leading method instead
+          if (this.leading)
+            this.leading(v)
+        } else {
+          // Set given attribute on node
+          typeof n === 'string' ?
+            this.node.setAttributeNS(n, a, v.toString()) :
+            this.node.setAttribute(a, v.toString())
+        }
+        
+        // Rebuild if required
+        if (this.rebuild && (a == 'font-size' || a == 'x'))
+          this.rebuild(a, v)
+      }
+      
+      return this
+    }
+  })
+
+  SVG.extend(SVG.Element, {
+       // Add transformations
+       transform: function(o) {
+               // Full getter
+               if (o == null)
+                       return this.ctm().extract()
+  
+               // Get current matrix
+               var matrix = new SVG.Matrix(this)
+  
+               // Act on matrix
+               if (o.a != null)
+                       matrix = matrix.multiply(new SVG.Matrix(o))
+               
+               // Act on rotate
+               else if (o.rotation)
+                       matrix = matrix.rotate(
+                               o.rotation
+                       , o.cx == null ? this.bbox().cx : o.cx
+                       , o.cy == null ? this.bbox().cy : o.cy
+                       )
+  
+               // Act on scale
+               else if (o.scale != null || o.scaleX != null || o.scaleY != null)
+                       matrix = matrix.scale(
+                               o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
+                       , o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
+                       , o.cx          != null ? o.cx          : this.bbox().x
+                       , o.cy          != null ? o.cy          : this.bbox().y
+                       )
+  
+               // Act on skew
+               else if (o.skewX || o.skewY)
+                       matrix = matrix.skew(o.skewX, o.skewY)
+  
+               // Act on translate
+               else if (o.x || o.y)
+                       matrix = matrix.translate(o.x, o.y)
+  console.log(o, matrix)
+               return this.attr('transform', matrix)
+       }
+       // Reset all transformations
+  , untransform: function() {
+               return this.attr('transform', null)
+       }
+  
+  })
+
+  SVG.extend(SVG.Element, {
+    // Dynamic style generator
+    style: function(s, v) {
+      if (arguments.length == 0) {
+        /* get full style */
+        return this.node.style.cssText || ''
+      
+      } else if (arguments.length < 2) {
+        /* apply every style individually if an object is passed */
+        if (typeof s == 'object') {
+          for (v in s) this.style(v, s[v])
+        
+        } else if (SVG.regex.isCss.test(s)) {
+          /* parse css string */
+          s = s.split(';')
+  
+          /* apply every definition individually */
+          for (var i = 0; i < s.length; i++) {
+            v = s[i].split(':')
+            this.style(v[0].replace(/\s+/g, ''), v[1])
+          }
+        } else {
+          /* act as a getter if the first and only argument is not an object */
+          return this.node.style[camelCase(s)]
+        }
+      
+      } else {
+        this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v
+      }
+      
+      return this
+    }
+  })
 
   SVG.Parent = SVG.invent({
     // Initialize node
     
   })
 
+  SVG.extend(SVG.Parent, SVG.Text, {
+    // Import svg SVG data
+    svg: function(svg) {
+      // create temporary div to receive svg content
+      var element = document.createElement('div')
+  
+      if (svg) {
+        // strip away newlines and properly close tags
+        svg = svg
+          .replace(/\n/, '')
+          .replace(/<(\w+)([^<]+?)\/>/g, '<$1$2></$1>')
+  
+        // ensure SVG wrapper for correct element type casting
+        element.innerHTML = '<svg>' + svg + '</svg>'
+  
+        // transplant content from well to target
+        for (var i = element.firstChild.childNodes.length - 1; i >= 0; i--)
+          if (element.firstChild.childNodes[i].nodeType == 1)
+            this.node.appendChild(element.firstChild.childNodes[i])
+  
+        return this
+  
+      } else {
+        // clone element and its contents
+        var clone  = this.node.cloneNode(true)
+  
+        // add target to clone
+        element.appendChild(clone)
+  
+        return element.innerHTML
+      }
+    }
+  })
+
   SVG.FX = SVG.invent({
     // Initialize FX object
     create: function(element) {
   , extend: {
       // Move over x-axis
       x: function(x) {
-        return x == null ? this.trans.x : this.transform('x', x)
+        return x == null ? this.ctm().x : this.transform('x', x)
       }
       // Move over y-axis
     , y: function(y) {
-        return y == null ? this.trans.y : this.transform('y', y)
+        return y == null ? this.ctm().y : this.transform('y', y)
       }
       // Move by center over x-axis
     , cx: function(x) {
       }
       // Move by center over x-axis
     , cx: function(x) {
-        return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX))
+        return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.ctm().scaleX))
       }
       // Move by center over y-axis
     , cy: function(y) {
-        return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY))
+        return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.ctm().scaleY))
       }
       // Set width of element
     , width: function(width) {
   , extend: {
       // Get array
       array: function() {
-        return (this._array = new SVG.PointArray([
+        return new SVG.PointArray([
           [ this.attr('x1'), this.attr('y1') ]
         , [ this.attr('x2'), this.attr('y2') ]
-        ]))
+        ])
       }
       // Overwrite native plot() method
     , plot: function(x1, y1, x2, y2) {
-        if (typeof x1 === 'number')
+        if (arguments.length == 4)
           x1 = { x1: x1, y1: y1, x2: x2, y2: y2 }
         else 
-          x1 = (this._array = new SVG.PointArray(x1)).toLine()
+          x1 = new SVG.PointArray(x1).toLine()
   
         return this.attr(x1)
       }
   
   SVG.extend(SVG.Element, SVG.FX, {
     // Rotation
-    rotate: function(deg, x, y) {
-      return this.transform({
-        rotation: deg || 0
-      , cx: x
-      , cy: y
-      })
+    rotate: function(d, cx, cy) {
+      return this.transform({ rotation: d, cx: cx, cy: cy })
     }
     // Skew
   , skew: function(x, y) {
-      return this.transform({
-        skewX: x || 0
-      , skewY: y || 0
-      })
+      return this.transform({ skewX: x, skewY: y })
     }
     // Scale
-  , scale: function(x, y) {
-      return this.transform({
-        scaleX: x
-      , scaleY: y == null ? x : y
-      })
+  , scale: function(x, y, cx, cy) {
+      return this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy })
     }
     // Translate
   , translate: function(x, y) {
-      return this.transform({
-        x: x
-      , y: y
-      })
+      return this.transform({ x: x, y: y })
     }
     // Matrix
   , matrix: function(m) {
-      return this.transform({ matrix: m })
+      return this.attr('transform', new SVG.Matrix(m))
     }
     // Opacity
   , opacity: function(value) {
     exports.SVG = SVG
 
   function camelCase(s) { 
-    return s.toLowerCase().replace(/-(.)/g, function(m, g) {
-      return g.toUpperCase()
-    })
+       return s.toLowerCase().replace(/-(.)/g, function(m, g) {
+               return g.toUpperCase()
+       })
   }
   
   // Capitalize first letter of a string
   function capitalize(s) {
-    return s.charAt(0).toUpperCase() + s.slice(1)
+       return s.charAt(0).toUpperCase() + s.slice(1)
   }
   
   // Ensure to six-based hex 
   function fullHex(hex) {
-    return hex.length == 4 ?
-      [ '#',
-        hex.substring(1, 2), hex.substring(1, 2)
-      , hex.substring(2, 3), hex.substring(2, 3)
-      , hex.substring(3, 4), hex.substring(3, 4)
-      ].join('') : hex
+       return hex.length == 4 ?
+               [ '#',
+                       hex.substring(1, 2), hex.substring(1, 2)
+               , hex.substring(2, 3), hex.substring(2, 3)
+               , hex.substring(3, 4), hex.substring(3, 4)
+               ].join('') : hex
   }
   
   // Component to hex value
   function compToHex(comp) {
-    var hex = comp.toString(16)
-    return hex.length == 1 ? '0' + hex : hex
+       var hex = comp.toString(16)
+       return hex.length == 1 ? '0' + hex : hex
   }
   
   // Calculate proportional width and height values when necessary
   function proportionalSize(box, width, height) {
-    if (width == null || height == null) {
-      if (height == null)
-        height = box.height / box.width * width
-      else if (width == null)
-        width = box.width / box.height * height
-    }
-    
-    return {
-      width:  width
-    , height: height
-    }
+       if (width == null || height == null) {
+               if (height == null)
+                       height = box.height / box.width * width
+               else if (width == null)
+                       width = box.width / box.height * height
+       }
+       
+       return {
+               width:  width
+       , height: height
+       }
+  }
+  
+  // Delta transform point
+  function deltaTransformPoint(matrix, point) {
+       return {
+               x: point.x * matrix.a + point.y * matrix.c + 0
+       , y: point.x * matrix.b + point.y * matrix.d + 0
+       }
+  }
+  
+  // Map matrix array to object
+  function arrayToMatrix(a) {
+       return { a: a[0], b: a[1], c: a[2], e: a[3], f: a[4], g: a[5] }
   }
   
   // Calculate position according to from and to
   function at(o, pos) {
-    /* number recalculation (don't bother converting to SVG.Number for performance reasons) */
-    return typeof o.from == 'number' ?
-      o.from + (o.to - o.from) * pos :
-    
-    /* instance recalculation */
-    o instanceof SVG.Color || o instanceof SVG.Number ? o.at(pos) :
-    
-    /* for all other values wait until pos has reached 1 to return the final value */
-    pos < 1 ? o.from : o.to
+       /* number recalculation (don't bother converting to SVG.Number for performance reasons) */
+       return typeof o.from == 'number' ?
+               o.from + (o.to - o.from) * pos :
+       
+       /* instance recalculation */
+       o instanceof SVG.Color || o instanceof SVG.Number ? o.at(pos) :
+       
+       /* for all other values wait until pos has reached 1 to return the final value */
+       pos < 1 ? o.from : o.to
   }
   
   // PathArray Helpers
   function arrayToString(a) {
-    for (var i = 0, il = a.length, s = ''; i < il; i++) {
-      s += a[i][0]
-  
-      if (a[i][1] != null) {
-        s += a[i][1]
-  
-        if (a[i][2] != null) {
-          s += ' '
-          s += a[i][2]
-  
-          if (a[i][3] != null) {
-            s += ' '
-            s += a[i][3]
-            s += ' '
-            s += a[i][4]
-  
-            if (a[i][5] != null) {
-              s += ' '
-              s += a[i][5]
-              s += ' '
-              s += a[i][6]
-  
-              if (a[i][7] != null) {
-                s += ' '
-                s += a[i][7]
-              }
-            }
-          }
-        }
-      }
-    }
-    
-    return s + ' '
+       for (var i = 0, il = a.length, s = ''; i < il; i++) {
+               s += a[i][0]
+  
+               if (a[i][1] != null) {
+                       s += a[i][1]
+  
+                       if (a[i][2] != null) {
+                               s += ' '
+                               s += a[i][2]
+  
+                               if (a[i][3] != null) {
+                                       s += ' '
+                                       s += a[i][3]
+                                       s += ' '
+                                       s += a[i][4]
+  
+                                       if (a[i][5] != null) {
+                                               s += ' '
+                                               s += a[i][5]
+                                               s += ' '
+                                               s += a[i][6]
+  
+                                               if (a[i][7] != null) {
+                                                       s += ' '
+                                                       s += a[i][7]
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       return s + ' '
   }
   
   // Deep new id assignment
   function assignNewId(node) {
-    // Do the same for SVG child nodes as well
-    for (var i = node.childNodes.length - 1; i >= 0; i--)
-      if (node.childNodes[i] instanceof SVGElement)
-        assignNewId(node.childNodes[i])
+       // Do the same for SVG child nodes as well
+       for (var i = node.childNodes.length - 1; i >= 0; i--)
+               if (node.childNodes[i] instanceof SVGElement)
+                       assignNewId(node.childNodes[i])
   
-    return SVG.adopt(node).id(SVG.eid(node.nodeName))
+       return SVG.adopt(node).id(SVG.eid(node.nodeName))
   }
   
   // Add more bounding box properties
-  function boxProperties(b) {
-    b.x2 = b.x + b.width
-    b.y2 = b.y + b.height
-    b.cx = b.x + b.width / 2
-    b.cy = b.y + b.height / 2
+  function fullBox(b) {
+       b.x2 = b.x + b.width
+       b.y2 = b.y + b.height
+       b.cx = b.x + b.width / 2
+       b.cy = b.y + b.height / 2
   
-    return b
+       return b
   }
   
   // Parse a matrix string
   function parseMatrix(o) {
-    if (o.matrix) {
-      // Split matrix string
-      var m = o.matrix.replace(/\s/g, '').split(',')
-      
-      // Pasrse values
-      if (m.length == 6) {
-        o.a = parseFloat(m[0])
-        o.b = parseFloat(m[1])
-        o.c = parseFloat(m[2])
-        o.d = parseFloat(m[3])
-        o.e = parseFloat(m[4])
-        o.f = parseFloat(m[5])
-      }
-    }
-    
-    return o
+       if (o.matrix) {
+               // Split matrix string
+               var m = o.matrix.replace(/\s/g, '').split(',')
+               
+               // Pasrse values
+               if (m.length == 6) {
+                       o.a = parseFloat(m[0])
+                       o.b = parseFloat(m[1])
+                       o.c = parseFloat(m[2])
+                       o.d = parseFloat(m[3])
+                       o.e = parseFloat(m[4])
+                       o.f = parseFloat(m[5])
+               }
+       }
+       
+       return o
   }
   
   // Get id from reference string
   function idFromReference(url) {
-    var m = url.toString().match(SVG.regex.reference)
+       var m = url.toString().match(SVG.regex.reference)
   
-    if (m) return m[1]
+       if (m) return m[1]
   }
   
+  // Create matrix array for looping
+  var abcdef = 'abcdef'.split('')
+  
   // Shim layer with setTimeout fallback by Paul Irish
   window.requestAnimFrame = (function(){
-    return  window.requestAnimationFrame       ||
-            window.webkitRequestAnimationFrame ||
-            window.mozRequestAnimationFrame    ||
-            window.msRequestAnimationFrame     ||
-            function (c) { window.setTimeout(c, 1000 / 60) }
+       return  window.requestAnimationFrame       ||
+                                       window.webkitRequestAnimationFrame ||
+                                       window.mozRequestAnimationFrame    ||
+                                       window.msRequestAnimationFrame     ||
+                                       function (c) { window.setTimeout(c, 1000 / 60) }
   })()
 
   if (typeof CustomEvent !== 'function') {
index 992b7c1ca90bb2951fce3411b1096986d185273a..59979e3af7a90a62deb4a24d2699a8092f5cabc1 100755 (executable)
@@ -1,2 +1,3 @@
-(function(){function t(t){return t.toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()})}function e(t){return t.charAt(0).toUpperCase()+t.slice(1)}function i(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}function n(t){var e=t.toString(16);return 1==e.length?"0"+e:e}function r(t,e,i){return(null==e||null==i)&&(null==i?i=t.height/t.width*e:null==e&&(e=t.width/t.height*i)),{width:e,height:i}}function s(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:t instanceof f.Color||t instanceof f.Number?t.at(e):1>e?t.from:t.to}function h(t){for(var e=0,i=t.length,n="";i>e;e++)n+=t[e][0],null!=t[e][1]&&(n+=t[e][1],null!=t[e][2]&&(n+=" ",n+=t[e][2],null!=t[e][3]&&(n+=" ",n+=t[e][3],n+=" ",n+=t[e][4],null!=t[e][5]&&(n+=" ",n+=t[e][5],n+=" ",n+=t[e][6],null!=t[e][7]&&(n+=" ",n+=t[e][7])))));return n+" "}function o(t){for(var e=t.childNodes.length-1;e>=0;e--)t.childNodes[e]instanceof SVGElement&&o(t.childNodes[e]);return f.adopt(t).id(f.eid(t.nodeName))}function a(t){return t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function u(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}function l(t){var e=t.toString().match(f.regex.reference);return e?e[1]:void 0}function c(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var i=document.createEvent("CustomEvent");return i.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),i}var f=this.SVG=function(t){return f.supported?(t=new f.Doc(t),f.parser||f.prepare(t),t):void 0};if(f.ns="http://www.w3.org/2000/svg",f.xmlns="http://www.w3.org/2000/xmlns/",f.xlink="http://www.w3.org/1999/xlink",f.did=1e3,f.eid=function(t){return"Svgjs"+e(t)+f.did++},f.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},f.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];f.Set&&f.Set.inherit&&f.Set.inherit()},f.prepare=function(t){var e=document.getElementsByTagName("body")[0],i=(e?new f.Doc(e):t.nested()).size(2,0),n=f.create("path");i.node.appendChild(n),f.parser={body:e||t.parent(),draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polyline().node,path:n}},f.supported=function(){return!!document.createElementNS&&!!document.createElementNS(f.ns,"svg").createSVGRect}(),!f.supported)return!1;f.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,f.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&f.extend(e,t.extend),t.construct&&f.extend(t.parent||f.Container,t.construct),e},f.adopt=function(t){if(t.instance)return t.instance;var i;return i="svg"==t.nodeName?t.parentNode instanceof SVGElement?new f.Nested:new f.Doc:"lineairGradient"==t.nodeName?new f.Gradient("lineair"):"radialGradient"==t.nodeName?new f.Gradient("radial"):f[e(t.nodeName)]?new(f[e(t.nodeName)]):new f.Element(t),i.type=t.nodeName,i.node=t,t.instance=i,i instanceof f.Doc&&i.namespace().defs(),i},f.regex={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+)\)/,reference:/#([a-z0-9\-_]+)/i,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i,isEvent:/^[\w]+:[\w]+$/},f.utils={map:function(t,e){var i,n=t.length,r=[];for(i=0;n>i;i++)r.push(e(t[i]));return r}},f.defaults={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","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},trans:{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}},f.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?f.regex.isRgb.test(t)?(e=f.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):f.regex.isHex.test(t)&&(e=f.regex.hex.exec(i(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)},f.extend(f.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+n(this.r)+n(this.g)+n(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new f.Color(t),this},at:function(t){return this.destination?(t=0>t?0:t>1?1:t,new f.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),f.Color.test=function(t){return t+="",f.regex.isHex.test(t)||f.regex.isRgb.test(t)},f.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},f.Color.isColor=function(t){return f.Color.isRgb(t)||f.Color.test(t)},f.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},f.extend(f.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<this.destination.length;)this.value.push(e)}return this},settle:function(){for(var t=0,e=this.value.length,i=[];e>t;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 f.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(" ")},reverse:function(){return this.value.reverse(),this}}),f.PointArray=function(t,e){this.constructor.call(this,t,e||[[0,0]])},f.PointArray.prototype=new f.Array,f.extend(f.PointArray,{toString:function(){for(var t=0,e=this.value.length,i=[];e>t;t++)i.push(this.value[t].join(","));return i.join(" ")},toLine:function(){return{x1:this.value[0][0],y1:this.value[0][1],x2:this.value[1][0],y2:this.value[1][1]}},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 f.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.y;return this},bbox:function(){return f.parser.poly.setAttribute("points",this.toString()),f.parser.poly.getBBox()}}),f.PathArray=function(t,e){this.constructor.call(this,t,e||[["M",0,0]])},f.PathArray.prototype=new f.Array,f.extend(f.PathArray,{toString:function(){return h(this.value)},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n,r=this.value.length-1;r>=0;r--)n=this.value[r][0],"M"==n||"L"==n||"T"==n?(this.value[r][1]+=t,this.value[r][2]+=e):"H"==n?this.value[r][1]+=t:"V"==n?this.value[r][1]+=e:"C"==n||"S"==n||"Q"==n?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"==n&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"==n&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var i,n,r=this.bbox();for(i=this.value.length-1;i>=0;i--)n=this.value[i][0],"M"==n||"L"==n||"T"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y):"H"==n?this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x:"V"==n?this.value[i][1]=(this.value[i][1]-r.y)*e/r.height+r.y:"C"==n||"S"==n||"Q"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y,this.value[i][3]=(this.value[i][3]-r.x)*t/r.width+r.x,this.value[i][4]=(this.value[i][4]-r.y)*e/r.height+r.y,"C"==n&&(this.value[i][5]=(this.value[i][5]-r.x)*t/r.width+r.x,this.value[i][6]=(this.value[i][6]-r.y)*e/r.height+r.y)):"A"==n&&(this.value[i][1]=this.value[i][1]*t/r.width,this.value[i][2]=this.value[i][2]*e/r.height,this.value[i][6]=(this.value[i][6]-r.x)*t/r.width+r.x,this.value[i][7]=(this.value[i][7]-r.y)*e/r.height+r.y);return this},parse:function(t){if(t instanceof f.PathArray)return t.valueOf();var e,i,n,r,s,o,a,u,l,c,d,p=0,m=0;for(f.parser.path.setAttribute("d","string"==typeof t?t:h(t)),d=f.parser.path.pathSegList,e=0,i=d.numberOfItems;i>e;++e)c=d.getItem(e),l=c.pathSegTypeAsLetter,"M"==l||"L"==l||"H"==l||"V"==l||"C"==l||"S"==l||"Q"==l||"T"==l||"A"==l?("x"in c&&(p=c.x),"y"in c&&(m=c.y)):("x1"in c&&(s=p+c.x1),"x2"in c&&(a=p+c.x2),"y1"in c&&(o=m+c.y1),"y2"in c&&(u=m+c.y2),"x"in c&&(p+=c.x),"y"in c&&(m+=c.y),"m"==l?d.replaceItem(f.parser.path.createSVGPathSegMovetoAbs(p,m),e):"l"==l?d.replaceItem(f.parser.path.createSVGPathSegLinetoAbs(p,m),e):"h"==l?d.replaceItem(f.parser.path.createSVGPathSegLinetoHorizontalAbs(p),e):"v"==l?d.replaceItem(f.parser.path.createSVGPathSegLinetoVerticalAbs(m),e):"c"==l?d.replaceItem(f.parser.path.createSVGPathSegCurvetoCubicAbs(p,m,s,o,a,u),e):"s"==l?d.replaceItem(f.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(p,m,a,u),e):"q"==l?d.replaceItem(f.parser.path.createSVGPathSegCurvetoQuadraticAbs(p,m,s,o),e):"t"==l?d.replaceItem(f.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(p,m),e):"a"==l?d.replaceItem(f.parser.path.createSVGPathSegArcAbs(p,m,c.r1,c.r2,c.angle,c.largeArcFlag,c.sweepFlag),e):("z"==l||"Z"==l)&&(p=n,m=r)),("M"==l||"m"==l)&&(n=p,r=m);for(t=[],d=f.parser.path.pathSegList,e=0,i=d.numberOfItems;i>e;++e)c=d.getItem(e),l=c.pathSegTypeAsLetter,p=[l],"M"==l||"L"==l||"T"==l?p.push(c.x,c.y):"H"==l?p.push(c.x):"V"==l?p.push(c.y):"C"==l?p.push(c.x1,c.y1,c.x2,c.y2,c.x,c.y):"S"==l?p.push(c.x2,c.y2,c.x,c.y):"Q"==l?p.push(c.x1,c.y1,c.x,c.y):"A"==l&&p.push(c.r1,c.r2,c.angle,0|c.largeArcFlag,0|c.sweepFlag,c.x,c.y),t.push(p);return t},bbox:function(){return f.parser.path.setAttribute("d",this.toString()),f.parser.path.getBBox()}}),f.Number=function(t){if(this.value=0,this.unit="","number"==typeof t)this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;else if("string"==typeof t){var e=t.match(f.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]?this.value/=100:"s"==e[2]&&(this.value*=1e3),this.unit=e[2])}else t instanceof f.Number&&(this.value=t.value,this.unit=t.unit)},f.extend(f.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:"s"==this.unit?this.value/1e3:this.value)+this.unit},valueOf:function(){return this.value},plus:function(t){return this.value=this+new f.Number(t),this},minus:function(t){return this.plus(-new f.Number(t))},times:function(t){return this.value=this*new f.Number(t),this},divide:function(t){return this.value=this/new f.Number(t),this},to:function(t){return"string"==typeof t&&(this.unit=t),this},morph:function(t){return this.destination=new f.Number(t),this},at:function(t){return this.destination?new f.Number(this.destination).minus(this).times(t).plus(this):this}}),f.ViewBox=function(t){var e,i,n,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g),u=t,l=t;for(n=new f.Number(t.width()),r=new f.Number(t.height());"%"==n.unit;)s*=n.value,n=new f.Number(u instanceof f.Doc?u.parent().offsetWidth:u.parent().width()),u=u.parent();for(;"%"==r.unit;)h*=r.value,r=new f.Number(l instanceof f.Doc?l.parent().offsetHeight:l.parent().height()),l=l.parent();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)},f.extend(f.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),f.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}a(this)},f.extend(f.BBox,{merge:function(t){var e=new f.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,a(e)}}),f.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.parent&&(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.x+=window.scrollX,this.y+=window.scrollY,a(this)},f.extend(f.RBox,{merge:function(t){var e=new f.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,a(e)}}),f.Element=f.invent({create:function(t){this._stroke=f.defaults.attrs.stroke,(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute("stroke")||this._stroke)},extend:{x:function(t){return null!=t&&(t=new f.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return null!=t&&(t=new f.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.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=r(this.bbox(),t,e);return this.width(new f.Number(i.width)).height(new f.Number(i.height))},clone:function(){return o(this.node.cloneNode(!0))},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||f.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]=f.regex.isNumber.test(e[i].nodeValue)?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 e=this.node.getAttribute(t),null==e?f.defaults.attrs[t]:f.regex.isNumber.test(e)?parseFloat(e):e;"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),("fill"==t||"stroke"==t)&&(f.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof f.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new f.Number(e):f.Color.isColor(e)?e=new f.Color(e):Array.isArray(e)&&(e=new f.Array(e)),"leading"==t?this.leading&&this.leading(e):"string"==typeof i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!=t&&"x"!=t||this.rebuild(t,e)}return this},transform:function(t){return this},style:function(e,i){if(0==arguments.length)return this.node.style.cssText||"";if(arguments.length<2)if("object"==typeof e)for(i in e)this.style(i,e[i]);else{if(!f.regex.isCss.test(e))return this.node.style[t(e)];e=e.split(";");for(var n=0;n<e.length;n++)i=e[n].split(":"),this.style(i[0].replace(/\s+/g,""),i[1])}else this.node.style[t(e)]=null===i||f.regex.isBlank.test(i)?"":i;return this},id:function(t){return this.attr("id",t)},bbox:function(){return new f.BBox(this)},rbox:function(){return new f.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&t<i.x+i.width&&e<i.y+i.height},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")},classes:function(){var t=this.attr("class");return null==t?[]:t.trim().split(/\s+/)},hasClass:function(t){return-1!=this.classes().indexOf(t)},addClass:function(t){if(!this.hasClass(t)){var e=this.classes();e.push(t),this.attr("class",e.join(" "))}return this},removeClass:function(t){if(this.hasClass(t)){var e=this.classes().filter(function(e){return e!=t});this.attr("class",e.join(" "))}return this},toggleClass:function(t){return this.hasClass(t)?this.removeClass(t):this.addClass(t)},reference:function(t){return f.get(this.attr(t))},parent:function(t){var e=f.adopt(this.node.parentNode);if(t)for(;!(e instanceof t);)e=f.adopt(e.node.parentNode);return e}}}),f.Parent=f.invent({create:function(t){this.constructor.call(this,t)},inherit:f.Element,extend:{children:function(){return f.utils.map(this.node.childNodes,function(t){return f.adopt(t)})},add:function(t,e){return this.has(t)||(e=null==e?this.children().length:e,this.node.insertBefore(t.node,this.node.childNodes[e]||null)),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return this.children().indexOf(t)},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 f.Element&&t.apply(r[i],[i,r]),e&&r[i]instanceof f.Container&&r[i].each(t,e);return this},removeElement:function(t){return this.node.removeChild(t.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,this},defs:function(){return this.doc().defs()}}}),f.Container=f.invent({create:function(t){this.constructor.call(this,t)},inherit:f.Parent,extend:{viewbox:function(t){return 0==arguments.length?new f.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),f.FX=f.invent({create:function(t){this.target=t},extend:{animate:function(t,e,i){var n,r,h,o,a=this.target,u=this;return"object"==typeof t&&(i=t.delay,e=t.ease,t=t.duration),t="="==t?t:null==t?1e3:new f.Number(t).valueOf(),e=e||"<>",u.to=function(t){var i;if(t=0>t?0:t>1?1:t,null==n){n=[];for(o in u.attrs)n.push(o);if(a.morphArray&&(u._plot||n.indexOf("points")>-1)){var l,c=new a.morphArray(u._plot||u.attrs.points||a.array);u._size&&c.size(u._size.width.to,u._size.height.to),l=c.bbox(),u._x?c.move(u._x.to,l.y):u._cx&&c.move(u._cx.to-l.width/2,l.y),l=c.bbox(),u._y?c.move(l.x,u._y.to):u._cy&&c.move(l.x,u._cy.to-l.height/2),delete u._x,delete u._y,delete u._cx,delete u._cy,delete u._size,u._plot=a.array.morph(c)}}if(null==r){r=[];for(o in u.trans)r.push(o)}if(null==h){h=[];for(o in u.styles)h.push(o)}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,u._plot?a.plot(u._plot.at(t)):(u._x?a.x(u._x.at(t)):u._cx&&a.cx(u._cx.at(t)),u._y?a.y(u._y.at(t)):u._cy&&a.cy(u._cy.at(t)),u._size&&a.size(u._size.width.at(t),u._size.height.at(t))),u._viewbox&&a.viewbox(u._viewbox.x.at(t),u._viewbox.y.at(t),u._viewbox.width.at(t),u._viewbox.height.at(t)),u._leading&&a.leading(u._leading.at(t)),i=n.length-1;i>=0;i--)a.attr(n[i],s(u.attrs[n[i]],t));for(i=r.length-1;i>=0;i--)a.transform(r[i],s(u.trans[r[i]],t));for(i=h.length-1;i>=0;i--)a.style(h[i],s(u.styles[h[i]],t));u._during&&u._during.call(a,t,function(e,i){return s({from:e,to:i},t)})},"number"==typeof t&&(this.timeout=setTimeout(function(){var n=(new Date).getTime();u.situation={interval:1e3/60,start:n,play:!0,finish:n+t,duration:t},u.render=function(){if(u.situation.play===!0){var n=(new Date).getTime(),r=n>u.situation.finish?1:(n-u.situation.start)/t;u.to(r),n>u.situation.finish?(u._plot&&a.plot(new f.PointArray(u._plot.destination).settle()),u._loop===!0||"number"==typeof u._loop&&u._loop>1?("number"==typeof u._loop&&--u._loop,u.animate(t,e,i)):u._after?u._after.apply(a,[u]):u.stop()):requestAnimFrame(u.render)}else requestAnimFrame(u.render)},u.render()},new f.Number(i).valueOf())),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{var n=this.target.attr(t);this.attrs[t]=f.Color.isColor(n)?new f.Color(n).morph(e):f.regex.unit.test(n)?new f.Number(n).morph(e):{from:n,to:e}}return this},transform:function(t,e){if(1==arguments.length){t=u(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=new f.Number(this.target.x()).morph(t),this},y:function(t){return this._y=new f.Number(this.target.y()).morph(t),this},cx:function(t){return this._cx=new f.Number(this.target.cx()).morph(t),this},cy:function(t){return this._cy=new f.Number(this.target.cy()).morph(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 f.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:new f.Number(i.width).morph(t),height:new f.Number(i.height).morph(e)}}return this},plot:function(t){return this._plot=t,this},leading:function(t){return this.target._leading&&(this._leading=new f.Number(this.target._leading).morph(t)),this},viewbox:function(t,e,i,n){if(this.target instanceof f.Container){var r=this.target.viewbox();this._viewbox={x:new f.Number(r.x).morph(t),y:new f.Number(r.y).morph(e),width:new f.Number(r.width).morph(i),height:new f.Number(r.height).morph(n)}}return this},update:function(t){return this.target instanceof f.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 f.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(t){return t===!0?(this.animate(0),this._after&&this._after.apply(this.target,[this])):(clearTimeout(this.timeout),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._leading,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}},parent:f.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new f.FX(this))).stop().animate(t,e,i)},stop:function(t){return this.fx&&this.fx.stop(t),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}}),f.extend(f.Element,f.FX,{dx:function(t){return this.x((this.target||this).x()+t)},dy:function(t){return this.y((this.target||this).y()+t)},dmove:function(t,e){return this.dx(t).dy(e)}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){f.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),f.events={},f.listeners={},f.registerEvent=function(t){f.events[t]||(f.events[t]=new c(t))},f.on=function(t,e,i){var n=i.bind(t.instance||t);f.listeners[i]=n,t.addEventListener(e,n,!1)},f.off=function(t,e,i){t.removeEventListener(e,f.listeners[i],!1),delete f.listeners[i]},f.extend(f.Element,{on:function(t,e){return f.on(this.node,t,e),this},off:function(t,e){return f.off(this.node,t,e),this},fire:function(t,e){return f.events[t].detail=e,this.node.dispatchEvent(f.events[t]),delete f.events[t].detail,this}}),f.Defs=f.invent({create:"defs",inherit:f.Container}),f.G=f.invent({create:"g",inherit:f.Container,extend:{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)},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)}},construct:{group:function(){return this.put(new f.G)}}}),f.extend(f.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,e=this.parent();return e.removeElement(this).add(this,t),e instanceof f.Doc&&e.node.appendChild(e.defs().node),this},backward:function(){var t=this.position();return t>0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof f.Doc&&t.node.appendChild(t.defs().node),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}}),f.Mask=f.invent({create:function(){this.constructor.call(this,f.create("mask")),this.targets=[]},inherit:f.Container,extend:{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}},construct:{mask:function(){return this.defs().put(new f.Mask)}}}),f.extend(f.Element,{maskWith:function(t){return this.masker=t instanceof f.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)}}),f.ClipPath=f.invent({create:function(){this.constructor.call(this,f.create("clipPath")),this.targets=[]},inherit:f.Container,extend:{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}},construct:{clip:function(){return this.defs().put(new f.ClipPath)}}}),f.extend(f.Element,{clipWith:function(t){return this.clipper=t instanceof f.ClipPath?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)}}),f.Gradient=f.invent({create:function(t){this.constructor.call(this,f.create(t+"Gradient")),this.type=t},inherit:f.Container,extend:{from:function(t,e){return"radial"==this.type?this.attr({fx:new f.Number(t),fy:new f.Number(e)}):this.attr({x1:new f.Number(t),y1:new f.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new f.Number(t),cy:new f.Number(e)}):this.attr({x2:new f.Number(t),y2:new f.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new f.Number(t)}):this},at:function(t,e,i){return this.put(new f.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),f.extend(f.Defs,{gradient:function(t,e){return this.put(new f.Gradient(t)).update(e)}}),f.Stop=f.invent({create:"stop",inherit:f.Element,extend:{update:function(t){return("number"==typeof t||t instanceof f.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),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 f.Number(t.offset)),this}}}),f.Pattern=f.invent({create:"pattern",inherit:f.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),f.extend(f.Defs,{pattern:function(t,e,i){return this.put(new f.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),f.Doc=f.invent({create:function(t){t&&(t="string"==typeof t?document.getElementById(t):t,"svg"==t.nodeName?this.constructor.call(this,t):(this.constructor.call(this,f.create("svg")),t.appendChild(this.node)),this.namespace().size("100%","100%").defs())},inherit:f.Container,extend:{namespace:function(){return this.attr({xmlns:f.ns,version:"1.1"}).attr("xmlns:xlink",f.xlink,f.xmlns)},defs:function(){if(!this._defs){var t;this._defs=(t=this.node.getElementsByTagName("defs")[0])?f.adopt(t):new f.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return"#document"==this.node.parentNode.nodeName?null:this.node.parentNode}}}),f.extend(f.Doc,{spof:function(){if(this.doSpof){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){var t=this;return this.doSpof=!0,f.on(window,"resize",function(){t.spof()}),this.spof()}}),f.Shape=f.invent({create:function(t){this.constructor.call(this,t)},inherit:f.Element}),f.Symbol=f.invent({create:"symbol",inherit:f.Container,construct:{symbol:function(){return this.defs().put(new f.Symbol)}}}),f.Use=f.invent({create:"use",inherit:f.Shape,extend:{element:function(t){return this.target=t,this.attr("href","#"+t,f.xlink)}},construct:{use:function(t){return this.put(new f.Use).element(t)}}}),f.Rect=f.invent({create:"rect",inherit:f.Shape,construct:{rect:function(t,e){return this.put((new f.Rect).size(t,e))}}}),f.Circle=f.invent({create:"circle",inherit:f.Shape,construct:{circle:function(t){return this.put(new f.Circle).rx(new f.Number(t).divide(2)).move(0,0)}}}),f.extend(f.Circle,f.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),f.Ellipse=f.invent({create:"ellipse",inherit:f.Shape,construct:{ellipse:function(t,e){return this.put(new f.Ellipse).size(t,e).move(0,0)}}}),f.extend(f.Ellipse,f.Rect,f.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),f.extend(f.Circle,f.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new f.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new f.Number(t).divide(this.trans.scaleY))},width:function(t){return null==t?2*this.rx():this.rx(new f.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new f.Number(t).divide(2))},size:function(t,e){var i=r(this.bbox(),t,e);return this.rx(new f.Number(i.width).divide(2)).ry(new f.Number(i.height).divide(2))}}),f.Line=f.invent({create:"line",inherit:f.Shape,extend:{array:function(){return this._array=new f.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return t="number"==typeof t?{x1:t,y1:e,x2:i,y2:n}:(this._array=new f.PointArray(t)).toLine(),this.attr(t)},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=r(this.bbox(),t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return this.put(new f.Line).plot(t,e,i,n)}}}),f.Polyline=f.invent({create:"polyline",inherit:f.Shape,construct:{polyline:function(t){return this.put(new f.Polyline).plot(t)
-}}}),f.Polygon=f.invent({create:"polygon",inherit:f.Shape,construct:{polygon:function(t){return this.put(new f.Polygon).plot(t)}}}),f.extend(f.Polyline,f.Polygon,{array:function(){return this._array||(this._array=new f.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new f.PointArray(t))},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=r(this.bbox(),t,e);return this.attr("points",this.array().size(i.width,i.height))}}),f.extend(f.Line,f.Polyline,f.Polygon,{morphArray:f.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),f.Path=f.invent({create:"path",inherit:f.Shape,extend:{morphArray:f.PathArray,array:function(){return this._array||(this._array=new f.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new f.PathArray(t))},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var i=r(this.bbox(),t,e);return this.attr("d",this.array().size(i.width,i.height))},width:function(t){return null==t?this.bbox().width:this.size(t,this.bbox().height)},height:function(t){return null==t?this.bbox().height:this.size(this.bbox().width,t)}},construct:{path:function(t){return this.put(new f.Path).plot(t)}}}),f.Image=f.invent({create:"image",inherit:f.Shape,extend:{load:function(t){if(!t)return this;var e=this,i=document.createElement("img");return i.onload=function(){var n=e.doc(f.Pattern);0==e.width()&&0==e.height()&&e.size(i.width,i.height),n&&0==n.width()&&0==n.height()&&n.size(e.width(),e.height()),"function"==typeof e._loaded&&e._loaded.call(e,{width:i.width,height:i.height,ratio:i.width/i.height,url:t})},this.attr("href",i.src=this.src=t,f.xlink)},loaded:function(t){return this._loaded=t,this}},construct:{image:function(t,e,i){return this.put(new f.Image).load(t).size(e||0,i||e||0)}}}),f.Text=f.invent({create:function(){this.constructor.call(this,f.create("text")),this._leading=new f.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",f.defaults.attrs["font-family"])},inherit:f.Shape,extend:{x:function(t){return null==t?this.attr("x"):(this.textPath||this.lines.each(function(){this.newLined&&this.x(t)}),this.attr("x",t))},y:function(t){var e=this.attr("y"),i="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-i:e:this.attr("y","number"==typeof t?t+i: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)},text:function(t){if("undefined"==typeof t)return this.content;if(this.clear().build(!0),"function"==typeof t)t.call(this,this);else{t=(this.content=t).split("\n");for(var e=0,i=t.length;i>e;e++)this.tspan(t[e]).newLine()}return this.build(!1).rebuild()},size:function(t){return this.attr("font-size",t).rebuild()},leading:function(t){return null==t?this._leading:(this._leading=new f.Number(t),this.rebuild())},rebuild:function(t){if("boolean"==typeof t&&(this._rebuild=t),this._rebuild){var e=this;this.lines.each(function(){this.newLined&&(this.textPath||this.attr("x",e.attr("x")),this.attr("dy",e._leading*new f.Number(e.attr("font-size"))))}),this.fire("rebuild")}return this},build:function(t){return this._build=!!t,this}},construct:{text:function(t){return this.put(new f.Text).text(t)},plain:function(t){return this.put(new f.Text).plain(t)}}}),f.Tspan=f.invent({create:"tspan",inherit:f.Shape,extend:{text:function(t){return"function"==typeof t?t.call(this,this):this.plain(t),this},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){var t=this.doc(f.Text);return this.newLined=!0,this.dy(t._leading*t.attr("font-size")).attr("x",t.x())}}}),f.extend(f.Text,f.Tspan,{plain:function(t){return this._build===!1&&this.clear(),this.node.appendChild(document.createTextNode(this.content=t)),this},tspan:function(t){var e=(this.textPath||this).node,i=new f.Tspan;return this._build===!1&&this.clear(),e.appendChild(i.node),this instanceof f.Text&&this.lines.add(i),i.text(t)},clear:function(){for(var t=(this.textPath||this).node;t.hasChildNodes();)t.removeChild(t.lastChild);return this instanceof f.Text&&(delete this.lines,this.lines=new f.Set,this.content=""),this},length:function(){return this.node.getComputedTextLength()}}),f.registerEvent("rebuild"),f.TextPath=f.invent({create:"textPath",inherit:f.Element,parent:f.Text,construct:{path:function(t){for(this.textPath=new f.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),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,f.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}}),f.Nested=f.invent({create:function(){this.constructor.call(this,f.create("svg")),this.style("overflow","visible")},inherit:f.Container,construct:{nested:function(){return this.put(new f.Nested)}}}),f.A=f.invent({create:"a",inherit:f.Container,extend:{to:function(t){return this.attr("href",t,f.xlink)},show:function(t){return this.attr("show",t,f.xlink)},target:function(t){return this.attr("target",t)}},construct:{link:function(t){return this.put(new f.A).to(t)}}}),f.extend(f.Element,{linkTo:function(t){var e=new f.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent().put(e).put(this)}}),f.Marker=f.invent({create:"marker",inherit:f.Container,extend:{width:function(t){return this.attr("markerWidth",t)},height:function(t){return this.attr("markerHeight",t)},ref:function(t,e){return this.attr("refX",t).attr("refY",e)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return"url(#"+this.id()+")"}},construct:{marker:function(t,e,i){return this.defs().marker(t,e,i)}}}),f.extend(f.Defs,{marker:function(t,e,i){return this.put(new f.Marker).size(t,e).ref(t/2,e/2).viewbox(0,0,t,e).attr("orient","auto").update(i)}}),f.extend(f.Line,f.Polyline,f.Polygon,f.Path,{marker:function(t,e,i,n){var r=["marker"];return"all"!=t&&r.push(t),r=r.join("-"),t=arguments[1]instanceof f.Marker?arguments[1]:this.doc().marker(e,i,n),this.attr(r,t)}});var d={stroke:["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],fill:["color","opacity","rule"],prefix:function(t,e){return"color"==e?t:t+"-"+e}};["fill","stroke"].forEach(function(t){var e,i={};i[t]=function(i){if("string"==typeof i||f.Color.isRgb(i)||i&&"function"==typeof i.fill)this.attr(t,i);else for(e=d[t].length-1;e>=0;e--)null!=i[d[t][e]]&&this.attr(d.prefix(t,d[t][e]),i[d[t][e]]);return this},f.extend(f.Element,f.FX,i)}),f.extend(f.Element,f.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)}}),f.extend(f.Rect,f.Ellipse,f.Circle,f.FX,{radius:function(t,e){return this.rx(t).ry(null==e?t:e)}}),f.extend(f.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),f.extend(f.Parent,f.Text,f.FX,{font:function(t){for(var e in t)"leading"==e?this.leading(t[e]):"anchor"==e?this.attr("text-anchor",t[e]):"size"==e||"family"==e||"weight"==e||"stretch"==e||"variant"==e||"style"==e?this.attr("font-"+e,t[e]):this.attr(e,t[e]);return this}}),f.Set=f.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{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.index(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.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new f.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new f.Set(t)}}}),f.SetFX=f.invent({create:function(t){this.set=t}}),f.Set.inherit=function(){var t,e=[];for(var t in f.Shape.prototype)"function"==typeof f.Shape.prototype[t]&&"function"!=typeof f.Set.prototype[t]&&e.push(t);e.forEach(function(t){f.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 f.SetFX(this)):this}}),e=[];for(var t in f.FX.prototype)"function"==typeof f.FX.prototype[t]&&"function"!=typeof f.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){f.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}})},f.extend(f.Element,{data:function(t,e,i){if("object"==typeof t)for(e in t)this.data(e,t[e]);else if(arguments.length<2)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}}),f.extend(f.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={})}}),f.get=function(t){var e=document.getElementById(l(t)||t);return e?f.adopt(e):void 0},f.select=function(t,e){return new f.Set(f.utils.map((e||document).querySelectorAll(t),function(t){return f.adopt(t)}))},f.extend(f.Parent,{select:function(t){return f.select(t,this.node)}}),"function"==typeof define&&define.amd?define(function(){return f}):"undefined"!=typeof exports&&(exports.SVG=f),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),"function"!=typeof c&&(c.prototype=window.Event.prototype,window.CustomEvent=c)}).call(this);
\ No newline at end of file
+/* svg.js 1.0.0-rc.10-19-g7cc2d36 - svg inventor adopter regex utilities default color array pointarray patharray number viewbox element boxes matrix attr transform style parent container transporter fx relative event defs group arrange mask clip gradient pattern doc spof shape symbol use rect ellipse line poly pointed path image text textpath nested hyperlink marker sugar set data memory selector loader helpers polyfill - svgjs.com/license */
+(function(){function n(e){return e.toLowerCase().replace(/-(.)/g,function(e,t){return t.toUpperCase()})}function r(e){return e.charAt(0).toUpperCase()+e.slice(1)}function i(e){return e.length==4?["#",e.substring(1,2),e.substring(1,2),e.substring(2,3),e.substring(2,3),e.substring(3,4),e.substring(3,4)].join(""):e}function s(e){var t=e.toString(16);return t.length==1?"0"+t:t}function o(e,t,n){if(t==null||n==null)n==null?n=e.height/e.width*t:t==null&&(t=e.width/e.height*n);return{width:t,height:n}}function u(e,t){return{x:t.x*e.a+t.y*e.c+0,y:t.x*e.b+t.y*e.d+0}}function a(e){return{a:e[0],b:e[1],c:e[2],e:e[3],f:e[4],g:e[5]}}function f(t,n){return typeof t.from=="number"?t.from+(t.to-t.from)*n:t instanceof e.Color||t instanceof e.Number?t.at(n):n<1?t.from:t.to}function l(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t][0],e[t][1]!=null&&(r+=e[t][1],e[t][2]!=null&&(r+=" ",r+=e[t][2],e[t][3]!=null&&(r+=" ",r+=e[t][3],r+=" ",r+=e[t][4],e[t][5]!=null&&(r+=" ",r+=e[t][5],r+=" ",r+=e[t][6],e[t][7]!=null&&(r+=" ",r+=e[t][7])))));return r+" "}function c(t){for(var n=t.childNodes.length-1;n>=0;n--)t.childNodes[n]instanceof SVGElement&&c(t.childNodes[n]);return e.adopt(t).id(e.eid(t.nodeName))}function h(e){return e.x2=e.x+e.width,e.y2=e.y+e.height,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}function p(e){if(e.matrix){var t=e.matrix.replace(/\s/g,"").split(",");t.length==6&&(e.a=parseFloat(t[0]),e.b=parseFloat(t[1]),e.c=parseFloat(t[2]),e.d=parseFloat(t[3]),e.e=parseFloat(t[4]),e.f=parseFloat(t[5]))}return e}function d(t){var n=t.toString().match(e.regex.reference);if(n)return n[1]}var e=this.SVG=function(t){if(e.supported)return t=new e.Doc(t),e.parser||e.prepare(t),t};e.ns="http://www.w3.org/2000/svg",e.xmlns="http://www.w3.org/2000/xmlns/",e.xlink="http://www.w3.org/1999/xlink",e.did=1e3,e.eid=function(t){return"Svgjs"+r(t)+e.did++},e.create=function(e){var t=document.createElementNS(this.ns,e);return t.setAttribute("id",this.eid(e)),t},e.extend=function(){var t,n,r,i;t=[].slice.call(arguments),n=t.pop();for(i=t.length-1;i>=0;i--)if(t[i])for(r in n)t[i].prototype[r]=n[r];e.Set&&e.Set.inherit&&e.Set.inherit()},e.prepare=function(t){var n=document.getElementsByTagName("body")[0],r=(n?new e.Doc(n):t.nested()).size(2,0),i=e.create("path");r.node.appendChild(i),e.parser={body:n||t.parent(),draw:r.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:r.polyline().node,path:i}},e.supported=function(){return!!document.createElementNS&&!!document.createElementNS(e.ns,"svg").createSVGRect}();if(!e.supported)return!1;e.invent=function(t){var n=typeof t.create=="function"?t.create:function(){this.constructor.call(this,e.create(t.create))};return t.inherit&&(n.prototype=new t.inherit),t.extend&&e.extend(n,t.extend),t.construct&&e.extend(t.parent||e.Container,t.construct),n},e.adopt=function(t){if(t.instance)return t.instance;var n;return t.nodeName=="svg"?n=t.parentNode instanceof SVGElement?new e.Nested:new e.Doc:t.nodeName=="lineairGradient"?n=new e.Gradient("lineair"):t.nodeName=="radialGradient"?n=new e.Gradient("radial"):e[r(t.nodeName)]?n=new(e[r(t.nodeName)]):n=new e.Element(t),n.type=t.nodeName,n.node=t,t.instance=n,n instanceof e.Doc&&n.namespace().defs(),n},e.regex={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+)\)/,reference:/#([a-z0-9\-_]+)/i,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i,isEvent:/^[\w]+:[\w]+$/},e.utils={map:function(e,t){var n,r=e.length,i=[];for(n=0;n<r;n++)i.push(t(e[n]));return i},radians:function(e){return e%360*Math.PI/180},degrees:function(e){return e*180/Math.PI%360}},e.defaults={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","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},trans:{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}},e.Color=function(t){var n;this.r=0,this.g=0,this.b=0,typeof t=="string"?e.regex.isRgb.test(t)?(n=e.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(n[1]),this.g=parseInt(n[2]),this.b=parseInt(n[3])):e.regex.isHex.test(t)&&(n=e.regex.hex.exec(i(t)),this.r=parseInt(n[1],16),this.g=parseInt(n[2],16),this.b=parseInt(n[3],16)):typeof t=="object"&&(this.r=t.r,this.g=t.g,this.b=t.b)},e.extend(e.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+s(this.r)+s(this.g)+s(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new e.Color(t),this},at:function(t){return this.destination?(t=t<0?0:t>1?1:t,new e.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),e.Color.test=function(t){return t+="",e.regex.isHex.test(t)||e.regex.isRgb.test(t)},e.Color.isRgb=function(e){return e&&typeof e.r=="number"&&typeof e.g=="number"&&typeof e.b=="number"},e.Color.isColor=function(t){return e.Color.isRgb(t)||e.Color.test(t)},e.Array=function(e,t){e=(e||[]).valueOf(),e.length==0&&t&&(e=t.valueOf()),this.value=this.parse(e)},e.extend(e.Array,{morph:function(e){this.destination=this.parse(e);if(this.value.length!=this.destination.length){var t=this.value[this.value.length-1],n=this.destination[this.destination.length-1];while(this.value.length>this.destination.length)this.destination.push(n);while(this.value.length<this.destination.length)this.value.push(t)}return this},settle:function(){for(var e=0,t=this.value.length,n=[];e<t;e++)n.indexOf(this.value[e])==-1&&n.push(this.value[e]);return this.value=n},at:function(t){if(!this.destination)return this;for(var n=0,r=this.value.length,i=[];n<r;n++)i.push(this.value[n]+(this.destination[n]-this.value[n])*t);return new e.Array(i)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(e){return e=e.valueOf(),Array.isArray(e)?e:this.split(e)},split:function(e){return e.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")},reverse:function(){return this.value.reverse(),this}}),e.PointArray=function(e,t){this.constructor.call(this,e,t||[[0,0]])},e.PointArray.prototype=new e.Array,e.extend(e.PointArray,{toString:function(){for(var e=0,t=this.value.length,n=[];e<t;e++)n.push(this.value[e].join(","));return n.join(" ")},toLine:function(){return{x1:this.value[0][0],y1:this.value[0][1],x2:this.value[1][0],y2:this.value[1][1]}},at:function(t){if(!this.destination)return this;for(var n=0,r=this.value.length,i=[];n<r;n++)i.push([this.value[n][0]+(this.destination[n][0]-this.value[n][0])*t,this.value[n][1]+(this.destination[n][1]-this.value[n][1])*t]);return new e.PointArray(i)},parse:function(e){e=e.valueOf();if(Array.isArray(e))return e;e=this.split(e);for(var t=0,n=e.length,r,i=[];t<n;t++)r=e[t].split(","),i.push([parseFloat(r[0]),parseFloat(r[1])]);return i},move:function(e,t){var n=this.bbox();e-=n.x,t-=n.y;if(!isNaN(e)&&!isNaN(t))for(var r=this.value.length-1;r>=0;r--)this.value[r]=[this.value[r][0]+e,this.value[r][1]+t];return this},size:function(e,t){var n,r=this.bbox();for(n=this.value.length-1;n>=0;n--)this.value[n][0]=(this.value[n][0]-r.x)*e/r.width+r.x,this.value[n][1]=(this.value[n][1]-r.y)*t/r.height+r.y;return this},bbox:function(){return e.parser.poly.setAttribute("points",this.toString()),e.parser.poly.getBBox()}}),e.PathArray=function(e,t){this.constructor.call(this,e,t||[["M",0,0]])},e.PathArray.prototype=new e.Array,e.extend(e.PathArray,{toString:function(){return l(this.value)},move:function(e,t){var n=this.bbox();e-=n.x,t-=n.y;if(!isNaN(e)&&!isNaN(t))for(var r,i=this.value.length-1;i>=0;i--)r=this.value[i][0],r=="M"||r=="L"||r=="T"?(this.value[i][1]+=e,this.value[i][2]+=t):r=="H"?this.value[i][1]+=e:r=="V"?this.value[i][1]+=t:r=="C"||r=="S"||r=="Q"?(this.value[i][1]+=e,this.value[i][2]+=t,this.value[i][3]+=e,this.value[i][4]+=t,r=="C"&&(this.value[i][5]+=e,this.value[i][6]+=t)):r=="A"&&(this.value[i][6]+=e,this.value[i][7]+=t);return this},size:function(e,t){var n,r,i=this.bbox();for(n=this.value.length-1;n>=0;n--)r=this.value[n][0],r=="M"||r=="L"||r=="T"?(this.value[n][1]=(this.value[n][1]-i.x)*e/i.width+i.x,this.value[n][2]=(this.value[n][2]-i.y)*t/i.height+i.y):r=="H"?this.value[n][1]=(this.value[n][1]-i.x)*e/i.width+i.x:r=="V"?this.value[n][1]=(this.value[n][1]-i.y)*t/i.height+i.y:r=="C"||r=="S"||r=="Q"?(this.value[n][1]=(this.value[n][1]-i.x)*e/i.width+i.x,this.value[n][2]=(this.value[n][2]-i.y)*t/i.height+i.y,this.value[n][3]=(this.value[n][3]-i.x)*e/i.width+i.x,this.value[n][4]=(this.value[n][4]-i.y)*t/i.height+i.y,r=="C"&&(this.value[n][5]=(this.value[n][5]-i.x)*e/i.width+i.x,this.value[n][6]=(this.value[n][6]-i.y)*t/i.height+i.y)):r=="A"&&(this.value[n][1]=this.value[n][1]*e/i.width,this.value[n][2]=this.value[n][2]*t/i.height,this.value[n][6]=(this.value[n][6]-i.x)*e/i.width+i.x,this.value[n][7]=(this.value[n][7]-i.y)*t/i.height+i.y);return this},parse:function(t){if(t instanceof e.PathArray)return t.valueOf();var n,r,i,s,o,u,a,f,c,h,p,d=0,v=0;e.parser.path.setAttribute("d",typeof t=="string"?t:l(t)),p=e.parser.path.pathSegList;for(n=0,r=p.numberOfItems;n<r;++n){h=p.getItem(n),c=h.pathSegTypeAsLetter;if(c=="M"||c=="L"||c=="H"||c=="V"||c=="C"||c=="S"||c=="Q"||c=="T"||c=="A")"x"in h&&(d=h.x),"y"in h&&(v=h.y);else{"x1"in h&&(o=d+h.x1),"x2"in h&&(a=d+h.x2),"y1"in h&&(u=v+h.y1),"y2"in h&&(f=v+h.y2),"x"in h&&(d+=h.x),"y"in h&&(v+=h.y);if(c=="m")p.replaceItem(e.parser.path.createSVGPathSegMovetoAbs(d,v),n);else if(c=="l")p.replaceItem(e.parser.path.createSVGPathSegLinetoAbs(d,v),n);else if(c=="h")p.replaceItem(e.parser.path.createSVGPathSegLinetoHorizontalAbs(d),n);else if(c=="v")p.replaceItem(e.parser.path.createSVGPathSegLinetoVerticalAbs(v),n);else if(c=="c")p.replaceItem(e.parser.path.createSVGPathSegCurvetoCubicAbs(d,v,o,u,a,f),n);else if(c=="s")p.replaceItem(e.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(d,v,a,f),n);else if(c=="q")p.replaceItem(e.parser.path.createSVGPathSegCurvetoQuadraticAbs(d,v,o,u),n);else if(c=="t")p.replaceItem(e.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(d,v),n);else if(c=="a")p.replaceItem(e.parser.path.createSVGPathSegArcAbs(d,v,h.r1,h.r2,h.angle,h.largeArcFlag,h.sweepFlag),n);else if(c=="z"||c=="Z")d=i,v=s}if(c=="M"||c=="m")i=d,s=v}t=[],p=e.parser.path.pathSegList;for(n=0,r=p.numberOfItems;n<r;++n)h=p.getItem(n),c=h.pathSegTypeAsLetter,d=[c],c=="M"||c=="L"||c=="T"?d.push(h.x,h.y):c=="H"?d.push(h.x):c=="V"?d.push(h.y):c=="C"?d.push(h.x1,h.y1,h.x2,h.y2,h.x,h.y):c=="S"?d.push(h.x2,h.y2,h.x,h.y):c=="Q"?d.push(h.x1,h.y1,h.x,h.y):c=="A"&&d.push(h.r1,h.r2,h.angle,h.largeArcFlag|0,h.sweepFlag|0,h.x,h.y),t.push(d);return t},bbox:function(){return e.parser.path.setAttribute("d",this.toString()),e.parser.path.getBBox()}}),e.Number=function(t){this.value=0,this.unit="";if(typeof t=="number")this.value=isNaN(t)?0:isFinite(t)?t:t<0?-3.4e38:3.4e38;else if(typeof t=="string"){var n=t.match(e.regex.unit);n&&(this.value=parseFloat(n[1]),n[2]=="%"?this.value/=100:n[2]=="s"&&(this.value*=1e3),this.unit=n[2])}else t instanceof e.Number&&(this.value=t.value,this.unit=t.unit)},e.extend(e.Number,{toString:function(){return(this.unit=="%"?~~(this.value*1e8)/1e6:this.unit=="s"?this.value/1e3:this.value)+this.unit},valueOf:function(){return this.value},plus:function(t){return this.value=this+new e.Number(t),this},minus:function(t){return this.plus(-(new e.Number(t)))},times:function(t){return this.value=this*new e.Number(t),this},divide:function(t){return this.value=this/new e.Number(t),this},to:function(e){return typeof e=="string"&&(this.unit=e),this},morph:function(t){return this.destination=new e.Number(t),this},at:function(t){return this.destination?(new e.Number(this.destination)).minus(this).times(t).plus(this):this}}),e.ViewBox=function(t){var n,r,i,s,o=1,u=1,a=t.bbox(),f=(t.attr("viewBox")||"").match(/-?[\d\.]+/g),l=t,c=t;i=new e.Number(t.width()),s=new e.Number(t.height());while(i.unit=="%")o*=i.value,i=new e.Number(l instanceof e.Doc?l.parent().offsetWidth:l.parent().width()),l=l.parent();while(s.unit=="%")u*=s.value,s=new e.Number(c instanceof e.Doc?c.parent().offsetHeight:c.parent().height()),c=c.parent();this.x=a.x,this.y=a.y,this.width=i*o,this.height=s*u,this.zoom=1,f&&(n=parseFloat(f[0]),r=parseFloat(f[1]),i=parseFloat(f[2]),s=parseFloat(f[3]),this.zoom=this.width/this.height>i/s?this.height/s:this.width/i,this.x=n,this.y=r,this.width=i,this.height=s)},e.extend(e.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),e.Element=e.invent({create:function(t){this._stroke=e.defaults.attrs.stroke;if(this.node=t)this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute("stroke")||this._stroke},extend:{x:function(t){return t!=null&&(t=new e.Number(t),t.value/=this.ctm().extract().scaleX),this.attr("x",t)},y:function(t){return t!=null&&(t=new e.Number(t),t.value/=this.ctm().extract().scaleY),this.attr("y",t)},cx:function(e){return e==null?this.x()+this.width()/2:this.x(e-this.width()/2)},cy:function(e){return e==null?this.y()+this.height()/2:this.y(e-this.height()/2)},move:function(e,t){return this.x(e).y(t)},center:function(e,t){return this.cx(e).cy(t)},width:function(e){return this.attr("width",e)},height:function(e){return this.attr("height",e)},size:function(t,n){var r=o(this.bbox(),t,n);return this.width(new e.Number(r.width)).height(new e.Number(r.height))},clone:function(){return c(this.node.cloneNode(!0))},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(e){return this.after(e).remove(),e},addTo:function(e){return e.put(this)},putIn:function(e){return e.add(this)},id:function(e){return this.attr("id",e)},inside:function(e,t){var n=this.bbox();return e>n.x&&t>n.y&&e<n.x+n.width&&t<n.y+n.height},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return this.style("display")!="none"},toString:function(){return this.attr("id")},classes:function(){var e=this.attr("class");return e==null?[]:e.trim().split(/\s+/)},hasClass:function(e){return this.classes().indexOf(e)!=-1},addClass:function(e){if(!this.hasClass(e)){var t=this.classes();t.push(e),this.attr("class",t.join(" "))}return this},removeClass:function(e){return this.hasClass(e)&&this.attr("class",this.classes().filter(function(t){return t!=e}).join(" ")),this},toggleClass:function(e){return this.hasClass(e)?this.removeClass(e):this.addClass(e)},reference:function(t){return e.get(this.attr(t))},parent:function(t){var n=e.adopt(this.node.parentNode);if(t)while(!(n instanceof t))n=e.adopt(n.node.parentNode);return n},doc:function(t){return this.parent(t||e.Doc)},"native":function(){return this.node}}}),e.BBox=e.invent({create:function(t){var n;this.x=0,this.y=0,this.width=0,this.height=0;if(t){var r=(new e.Matrix(t)).extract();t.node.getBBox?n=t.node.getBBox():n={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight},this.x=n.x+r.x,this.y=n.y+r.y,this.width=n.width*r.scaleX,this.height=n.height*r.scaleY}h(this)},parent:e.Element,construct:{bbox:function(){return new e.BBox(this)}}}),e.RBox=e.invent({create:function(e){var t,n,r={};this.x=0,this.y=0,this.width=0,this.height=0;if(e){t=e.doc().parent(),n=e.doc().viewbox().zoom,r=e.node.getBoundingClientRect(),this.x=r.left,this.y=r.top,this.x-=t.offsetLeft,this.y-=t.offsetTop;while(t=t.offsetParent)this.x-=t.offsetLeft,this.y-=t.offsetTop;t=e;while(t.parent&&(t=t.parent()))t.viewbox&&(n*=t.viewbox().zoom,this.x-=t.x()||0,this.y-=t.y()||0)}this.x/=n,this.y/=n,this.width=r.width/=n,this.height=r.height/=n,this.x+=window.scrollX,this.y+=window.scrollY,h(this)},parent:e.Element,construct:{rbox:function(){return new e.RBox(this)}}}),[e.BBox,e.RBox].forEach(function(t){e.extend(t,{merge:function(e){var n=new t;return n.x=Math.min(this.x,e.x),n.y=Math.min(this.y,e.y),n.width=Math.max(this.x+this.width,e.x+e.width)-n.x,n.height=Math.max(this.y+this.height,e.y+e.height)-n.y,h(n)}})}),e.Matrix=e.invent({create:function(e){var t,n=a([1,0,0,1,0,0]);e=e.node&&e.node.getCTM?e.node.getCTM():typeof e=="string"?a(e.replace(/\s/g,"").split(",")):arguments.length==6?a([].slice.call(arguments)):typeof e=="object"?e:n;for(t=v.length-1;t>=0;t--)this[v[t]]=typeof e[v[t]]=="number"?e[v[t]]:n[v[t]]},extend:{extract:function(){var e=u(this,{x:0,y:1}),t=u(this,{x:1,y:0});return{x:this.e,y:this.f,skewX:180/Math.PI*Math.atan2(e.y,e.x)-90,skewY:180/Math.PI*Math.atan2(t.y,t.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:this.skewX}},multiply:function(t){return new e.Matrix(this.native().multiply(t.native()))},inverse:function(){return new e.Matrix(this.native().inverse())},translate:function(t,n){return new e.Matrix(this.native().translate(t||0,n||0))},scale:function(t,n,r,i){return n==null?new e.Matrix(this.native().scale(t)):new e.Matrix(this.native().scaleNonUniform(t,n))},rotate:function(t,n,r){return n==null?new e.Matrix(this.native().rotate(t)):(t=e.utils.radians(t),new e.Matrix(1,0,0,1,n,r))},flip:function(t){return new e.Matrix(this.native()["flip"+t.toUpperCase()]())},skew:function(t,n){return new e.Matrix(this.native().skewX(t||0).skewY(n||0))},"native":function(){var t,n=e.parser.draw.node.createSVGMatrix();for(t=v.length-1;t>=0;t--)n[v[t]]=this[v[t]];return n},toString:function(){return"matrix("+[this.a,this.b,this.c,this.d,this.e,this.f].join()+")"}},parent:e.Element,construct:{ctm:function(){return new e.Matrix(this)}}}),e.extend(e.Element,{attr:function(t,n,r){if(t==null){t={},n=this.node.attributes;for(r=n.length-1;r>=0;r--)t[n[r].nodeName]=e.regex.isNumber.test(n[r].nodeValue)?parseFloat(n[r].nodeValue):n[r].nodeValue;return t}if(typeof t=="object")for(n in t)this.attr(n,t[n]);else if(n===null)this.node.removeAttribute(t);else{if(n==null)return n=this.node.getAttribute(t),n==null?e.defaults.attrs[t]:e.regex.isNumber.test(n)?parseFloat(n):n;t=="stroke-width"?this.attr("stroke",parseFloat(n)>0?this._stroke:null):t=="stroke"&&(this._stroke=n);if(t=="fill"||t=="stroke")e.regex.isImage.test(n)&&(n=this.doc().defs().image(n,0,0)),n instanceof e.Image&&(n=this.doc().defs().pattern(0,0,function(){this.add(n)}));typeof n=="number"?n=new e.Number(n):e.Color.isColor(n)?n=new e.Color(n):Array.isArray(n)&&(n=new e.Array(n)),t=="leading"?this.leading&&this.leading(n):typeof r=="string"?this.node.setAttributeNS(r,t,n.toString()):this.node.setAttribute(t,n.toString()),this.rebuild&&(t=="font-size"||t=="x")&&this.rebuild(t,n)}return this}}),e.extend(e.Element,{transform:function(t){if(t==null)return this.ctm().extract();var n=new e.Matrix(this);if(t.a!=null)n=n.multiply(new e.Matrix(t));else if(t.rotation)n=n.rotate(t.rotation,t.cx==null?this.bbox().cx:t.cx,t.cy==null?this.bbox().cy:t.cy);else if(t.scale!=null||t.scaleX!=null||t.scaleY!=null)n=n.scale(t.scale!=null?t.scale:t.scaleX!=null?t.scaleX:1,t.scale!=null?t.scale:t.scaleY!=null?t.scaleY:1,t.cx!=null?t.cx:this.bbox().x,t.cy!=null?t.cy:this.bbox().y);else if(t.skewX||t.skewY)n=n.skew(t.skewX,t.skewY);else if(t.x||t.y)n=n.translate(t.x,t.y);return console.log(t,n),this.attr("transform",n)},untransform:function(){return this.attr("transform",null)}}),e.extend(e.Element,{style:function(t,r){if(arguments.length==0)return this.node.style.cssText||"";if(arguments.length<2)if(typeof t=="object")for(r in t)this.style(r,t[r]);else{if(!e.regex.isCss.test(t))return this.node.style[n(t)];t=t.split(";");for(var i=0;i<t.length;i++)r=t[i].split(":"),this.style(r[0].replace(/\s+/g,""),r[1])}else this.node.style[n(t)]=r===null||e.regex.isBlank.test(r)?"":r;return this}}),e.Parent=e.invent({create:function(e){this.constructor.call(this,e)},inherit:e.Element,extend:{children:function(){return e.utils.map(this.node.childNodes,function(t){return e.adopt(t)})},add:function(e,t){return this.has(e)||(t=t==null?this.children().length:t,this.node.insertBefore(e.node,this.node.childNodes[t]||null)),this},put:function(e,t){return this.add(e,t),e},has:function(e){return this.index(e)>=0},index:function(e){return this.children().indexOf(e)},get:function(e){return this.children()[e]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,n){var r,i,s=this.children();for(r=0,i=s.length;r<i;r++)s[r]instanceof e.Element&&t.apply(s[r],[r,s]),n&&s[r]instanceof e.Container&&s[r].each(t,n);return this},removeElement:function(e){return this.node.removeChild(e.node),this},clear:function(){while(this.node.hasChildNodes())this.node.removeChild(this.node.lastChild);return delete this._defs,this},defs:function(){return this.doc().defs()}}}),e.Container=e.invent({create:function(e){this.constructor.call(this,e)},inherit:e.Parent,extend:{viewbox:function(t){return arguments.length==0?new e.ViewBox(this):(t=arguments.length==1?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),e.extend(e.Parent,e.Text,{svg:function(e){var t=document.createElement("div");if(e){e=e.replace(/\n/,"").replace(/<(\w+)([^<]+?)\/>/g,"<$1$2></$1>"),t.innerHTML="<svg>"+e+"</svg>";for(var n=t.firstChild.childNodes.length-1;n>=0;n--)t.firstChild.childNodes[n].nodeType==1&&this.node.appendChild(t.firstChild.childNodes[n]);return this}var r=this.node.cloneNode(!0);return t.appendChild(r),t.innerHTML}}),e.FX=e.invent({create:function(e){this.target=e},extend:{animate:function(t,n,r){var i,s,o,u,a=this.target,l=this;return typeof t=="object"&&(r=t.delay,n=t.ease,t=t.duration),t=t=="="?t:t==null?1e3:(new e.Number(t)).valueOf(),n=n||"<>",l.to=function(e){var t;e=e<0?0:e>1?1:e;if(i==null){i=[];for(u in l.attrs)i.push(u);if(a.morphArray&&(l._plot||i.indexOf("points")>-1)){var r,c=new a.morphArray(l._plot||l.attrs.points||a.array);l._size&&c.size(l._size.width.to,l._size.height.to),r=c.bbox(),l._x?c.move(l._x.to,r.y):l._cx&&c.move(l._cx.to-r.width/2,r.y),r=c.bbox(),l._y?c.move(r.x,l._y.to):l._cy&&c.move(r.x,l._cy.to-r.height/2),delete l._x,delete l._y,delete l._cx,delete l._cy,delete l._size,l._plot=a.array.morph(c)}}if(s==null){s=[];for(u in l.trans)s.push(u)}if(o==null){o=[];for(u in l.styles)o.push(u)}e=n=="<>"?-Math.cos(e*Math.PI)/2+.5:n==">"?Math.sin(e*Math.PI/2):n=="<"?-Math.cos(e*Math.PI/2)+1:n=="-"?e:typeof n=="function"?n(e):e,l._plot?a.plot(l._plot.at(e)):(l._x?a.x(l._x.at(e)):l._cx&&a.cx(l._cx.at(e)),l._y?a.y(l._y.at(e)):l._cy&&a.cy(l._cy.at(e)),l._size&&a.size(l._size.width.at(e),l._size.height.at(e))),l._viewbox&&a.viewbox(l._viewbox.x.at(e),l._viewbox.y.at(e),l._viewbox.width.at(e),l._viewbox.height.at(e)),l._leading&&a.leading(l._leading.at(e));for(t=i.length-1;t>=0;t--)a.attr(i[t],f(l.attrs[i[t]],e));for(t=s.length-1;t>=0;t--)a.transform(s[t],f(l.trans[s[t]],e));for(t=o.length-1;t>=0;t--)a.style(o[t],f(l.styles[o[t]],e));l._during&&l._during.call(a,e,function(t,n){return f({from:t,to:n},e)})},typeof t=="number"&&(this.timeout=setTimeout(function(){var i=(new Date).getTime();l.situation={interval:1e3/60,start:i,play:!0,finish:i+t,duration:t},l.render=function(){if(l.situation.play===!0){var i=(new Date).getTime(),s=i>l.situation.finish?1:(i-l.situation.start)/t;l.to(s),i>l.situation.finish?(l._plot&&a.plot((new e.PointArray(l._plot.destination)).settle()),l._loop===!0||typeof l._loop=="number"&&l._loop>1?(typeof l._loop=="number"&&--l._loop,l.animate(t,n,r)):l._after?l._after.apply(a,[l]):l.stop()):requestAnimFrame(l.render)}else requestAnimFrame(l.render)},l.render()},(new e.Number(r)).valueOf())),this},bbox:function(){return this.target.bbox()},attr:function(t,n){if(typeof t=="object")for(var r in t)this.attr(r,t[r]);else{var i=this.target.attr(t);this.attrs[t]=e.Color.isColor(i)?(new e.Color(i)).morph(n):e.regex.unit.test(i)?(new e.Number(i)).morph(n):{from:i,to:n}}return this},transform:function(e,t){if(arguments.length==1){e=p(e),delete e.matrix;for(t in e)this.trans[t]={from:this.target.trans[t],to:e[t]}}else{var n={};n[e]=t,this.transform(n)}return this},style:function(e,t){if(typeof e=="object")for(var n in e)this.style(n,e[n]);else this.styles[e]={from:this.target.style(e),to:t};return this},x:function(t){return this._x=(new e.Number(this.target.x())).morph(t),this},y:function(t){return this._y=(new e.Number(this.target.y())).morph(t),this},cx:function(t){return this._cx=(new e.Number(this.target.cx())).morph(t),this},cy:function(t){return this._cy=(new e.Number(this.target.cy())).morph(t),this},move:function(e,t){return this.x(e).y(t)},center:function(e,t){return this.cx(e).cy(t)},size:function(t,n){if(this.target instanceof e.Text)this.attr("font-size",t);else{var r=this.target.bbox();this._size={width:(new e.Number(r.width)).morph(t),height:(new e.Number(r.height)).morph(n)}}return this},plot:function(e){return this._plot=e,this},leading:function(t){return this.target._leading&&(this._leading=(new e.Number(this.target._leading)).morph(t)),this},viewbox:function(t,n,r,i){if(this.target instanceof e.Container){var s=this.target.viewbox();this._viewbox={x:(new e.Number(s.x)).morph(t),y:(new e.Number(s.y)).morph(n),width:(new e.Number(s.width)).morph(r),height:(new e.Number(s.height)).morph(i)}}return this},update:function(t){return this.target instanceof e.Stop&&(t.opacity!=null&&this.attr("stop-opacity",t.opacity),t.color!=null&&this.attr("stop-color",t.color),t.offset!=null&&this.attr("offset",new e.Number(t.offset))),this},during:function(e){return this._during=e,this},after:function(e){return this._after=e,this},loop:function(e){return this._loop=e||!0,this},stop:function(e){return e===!0?(this.animate(0),this._after&&this._after.apply(this.target,[this])):(clearTimeout(this.timeout),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._leading,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 e=(new Date).getTime()-this.situation.pause;this.situation.finish+=e,this.situation.start+=e,this.situation.play=!0}return this}},parent:e.Element,construct:{animate:function(t,n,r){return(this.fx||(this.fx=new e.FX(this))).stop().animate(t,n,r)},stop:function(e){return this.fx&&this.fx.stop(e),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}}),e.extend(e.Element,e.FX,{dx:function(e){return this.x((this.target||this).x()+e)},dy:function(e){return this.y((this.target||this).y()+e)},dmove:function(e,t){return this.dx(e).dy(t)}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){e.Element.prototype[t]=function(e){var n=this;return this.node["on"+t]=typeof e=="function"?function(){return e.apply(n,arguments)}:null,this}}),e.events={},e.listeners={},e.registerEvent=function(t){e.events[t]||(e.events[t]=new m(t))},e.on=function(t,n,r){var i=r.bind(t.instance||t);e.listeners[r]=i,t.addEventListener(n,i,!1)},e.off=function(t,n,r){t.removeEventListener(n,e.listeners[r],!1),delete e.listeners[r]},e.extend(e.Element,{on:function(t,n){return e.on(this.node,t,n),this},off:function(t,n){return e.off(this.node,t,n),this},fire:function(t,n){return e.events[t].detail=n,this.node.dispatchEvent(e.events[t]),delete e.events[t].detail,this}}),e.Defs=e.invent({create:"defs",inherit:e.Container}),e.G=e.invent({create:"g",inherit:e.Container,extend:{x:function(e){return e==null?this.ctm().x:this.transform("x",e)},y:function(e){return e==null?this.ctm().y:this.transform("y",e)},cx:function(e){return e==null?this.bbox().cx:this.x(e-this.bbox().width/2)},cy:function(e){return e==null?this.bbox().cy:this.y(e-this.bbox().height/2)}},construct:{group:function(){return this.put(new e.G)}}}),e.extend(e.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,n=this.parent();return n.removeElement(this).add(this,t),n instanceof e.Doc&&n.node.appendChild(n.defs().node),this},backward:function(){var e=this.position();return e>0&&this.parent().removeElement(this).add(this,e-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof e.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(e){e.remove();var t=this.position();return this.parent().add(e,t),this},after:function(e){e.remove();var t=this.position();return this.parent().add(e,t+1),this}}),e.Mask=e.invent({create:function(){this.constructor.call(this,e.create("mask")),this.targets=[]},inherit:e.Container,extend:{remove:function(){for(var e=this.targets.length-1;e>=0;e--)this.targets[e]&&this.targets[e].unmask();return delete this.targets,this.parent().removeElement(this),this}},construct:{mask:function(){return this.defs().put(new e.Mask)}}}),e.extend(e.Element,{maskWith:function(t){return this.masker=t instanceof e.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)}}),e.ClipPath=e.invent({create:function(){this.constructor.call(this,e.create("clipPath")),this.targets=[]},inherit:e.Container,extend:{remove:function(){for(var e=this.targets.length-1;e>=0;e--)this.targets[e]&&this.targets[e].unclip();return delete this.targets,this.parent().removeElement(this),this}},construct:{clip:function(){return this.defs().put(new e.ClipPath)}}}),e.extend(e.Element,{clipWith:function(t){return this.clipper=t instanceof e.ClipPath?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)}}),e.Gradient=e.invent({create:function(t){this.constructor.call(this,e.create(t+"Gradient")),this.type=t},inherit:e.Container,extend:{from:function(t,n){return this.type=="radial"?this.attr({fx:new e.Number(t),fy:new e.Number(n)}):this.attr({x1:new e.Number(t),y1:new e.Number(n)})},to:function(t,n){return this.type=="radial"?this.attr({cx:new e.Number(t),cy:new e.Number(n)}):this.attr({x2:new e.Number(t),y2:new e.Number(n)})},radius:function(t){return this.type=="radial"?this.attr({r:new e.Number(t)}):this},at:function(t,n,r){return this.put(new e.Stop).update(t,n,r)},update:function(e){return this.clear(),typeof e=="function"&&e.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()}},construct:{gradient:function(e,t){return this.defs().gradient(e,t)}}}),e.extend(e.Defs,{gradient:function(t,n){return this.put(new e.Gradient(t)).update(n)}}),e.Stop=e.invent({create:"stop",inherit:e.Element,extend:{update:function(t){if(typeof t=="number"||t instanceof e.Number)t={offset:arguments[0],color:arguments[1],opacity:arguments[2]};return t.opacity!=null&&this.attr("stop-opacity",t.opacity),t.color!=null&&this.attr("stop-color",t.color),t.offset!=null&&this.attr("offset",new e.Number(t.offset)),this}}}),e.Pattern=e.invent({create:"pattern",inherit:e.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(e){return this.clear(),typeof e=="function"&&e.call(this,this),this},toString:function(){return this.fill()}},construct:{pattern:function(e,t,n){return this.defs().pattern(e,t,n)}}}),e.extend(e.Defs,{pattern:function(t,n,r){return this.put(new e.Pattern).update(r).attr({x:0,y:0,width:t,height:n,patternUnits:"userSpaceOnUse"})}}),e.Doc=e.invent({create:function(t){t&&(t=typeof t=="string"?document.getElementById
+(t):t,t.nodeName=="svg"?this.constructor.call(this,t):(this.constructor.call(this,e.create("svg")),t.appendChild(this.node)),this.namespace().size("100%","100%").defs())},inherit:e.Container,extend:{namespace:function(){return this.attr({xmlns:e.ns,version:"1.1"}).attr("xmlns:xlink",e.xlink,e.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=e.adopt(t):this._defs=new e.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return this.node.parentNode.nodeName=="#document"?null:this.node.parentNode}}}),e.extend(e.Doc,{spof:function(){if(this.doSpof){var e=this.node.getScreenCTM();e&&this.style("left",-e.e%1+"px").style("top",-e.f%1+"px")}return this},fixSubPixelOffset:function(){var t=this;return this.doSpof=!0,e.on(window,"resize",function(){t.spof()}),this.spof()}}),e.Shape=e.invent({create:function(e){this.constructor.call(this,e)},inherit:e.Element}),e.Symbol=e.invent({create:"symbol",inherit:e.Container,construct:{symbol:function(){return this.defs().put(new e.Symbol)}}}),e.Use=e.invent({create:"use",inherit:e.Shape,extend:{element:function(t){return this.target=t,this.attr("href","#"+t,e.xlink)}},construct:{use:function(t){return this.put(new e.Use).element(t)}}}),e.Rect=e.invent({create:"rect",inherit:e.Shape,construct:{rect:function(t,n){return this.put((new e.Rect).size(t,n))}}}),e.Circle=e.invent({create:"circle",inherit:e.Shape,construct:{circle:function(t){return this.put(new e.Circle).rx((new e.Number(t)).divide(2)).move(0,0)}}}),e.extend(e.Circle,e.FX,{rx:function(e){return this.attr("r",e)},ry:function(e){return this.rx(e)}}),e.Ellipse=e.invent({create:"ellipse",inherit:e.Shape,construct:{ellipse:function(t,n){return this.put(new e.Ellipse).size(t,n).move(0,0)}}}),e.extend(e.Ellipse,e.Rect,e.FX,{rx:function(e){return this.attr("rx",e)},ry:function(e){return this.attr("ry",e)}}),e.extend(e.Circle,e.Ellipse,{x:function(e){return e==null?this.cx()-this.rx():this.cx(e+this.rx())},y:function(e){return e==null?this.cy()-this.ry():this.cy(e+this.ry())},cx:function(t){return t==null?this.attr("cx"):this.attr("cx",(new e.Number(t)).divide(this.ctm().scaleX))},cy:function(t){return t==null?this.attr("cy"):this.attr("cy",(new e.Number(t)).divide(this.ctm().scaleY))},width:function(t){return t==null?this.rx()*2:this.rx((new e.Number(t)).divide(2))},height:function(t){return t==null?this.ry()*2:this.ry((new e.Number(t)).divide(2))},size:function(t,n){var r=o(this.bbox(),t,n);return this.rx((new e.Number(r.width)).divide(2)).ry((new e.Number(r.height)).divide(2))}}),e.Line=e.invent({create:"line",inherit:e.Shape,extend:{array:function(){return new e.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,n,r,i){return arguments.length==4?t={x1:t,y1:n,x2:r,y2:i}:t=(new e.PointArray(t)).toLine(),this.attr(t)},move:function(e,t){return this.attr(this.array().move(e,t).toLine())},size:function(e,t){var n=o(this.bbox(),e,t);return this.attr(this.array().size(n.width,n.height).toLine())}},construct:{line:function(t,n,r,i){return this.put(new e.Line).plot(t,n,r,i)}}}),e.Polyline=e.invent({create:"polyline",inherit:e.Shape,construct:{polyline:function(t){return this.put(new e.Polyline).plot(t)}}}),e.Polygon=e.invent({create:"polygon",inherit:e.Shape,construct:{polygon:function(t){return this.put(new e.Polygon).plot(t)}}}),e.extend(e.Polyline,e.Polygon,{array:function(){return this._array||(this._array=new e.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new e.PointArray(t))},move:function(e,t){return this.attr("points",this.array().move(e,t))},size:function(e,t){var n=o(this.bbox(),e,t);return this.attr("points",this.array().size(n.width,n.height))}}),e.extend(e.Line,e.Polyline,e.Polygon,{morphArray:e.PointArray,x:function(e){return e==null?this.bbox().x:this.move(e,this.bbox().y)},y:function(e){return e==null?this.bbox().y:this.move(this.bbox().x,e)},width:function(e){var t=this.bbox();return e==null?t.width:this.size(e,t.height)},height:function(e){var t=this.bbox();return e==null?t.height:this.size(t.width,e)}}),e.Path=e.invent({create:"path",inherit:e.Shape,extend:{morphArray:e.PathArray,array:function(){return this._array||(this._array=new e.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new e.PathArray(t))},move:function(e,t){return this.attr("d",this.array().move(e,t))},x:function(e){return e==null?this.bbox().x:this.move(e,this.bbox().y)},y:function(e){return e==null?this.bbox().y:this.move(this.bbox().x,e)},size:function(e,t){var n=o(this.bbox(),e,t);return this.attr("d",this.array().size(n.width,n.height))},width:function(e){return e==null?this.bbox().width:this.size(e,this.bbox().height)},height:function(e){return e==null?this.bbox().height:this.size(this.bbox().width,e)}},construct:{path:function(t){return this.put(new e.Path).plot(t)}}}),e.Image=e.invent({create:"image",inherit:e.Shape,extend:{load:function(t){if(!t)return this;var n=this,r=document.createElement("img");return r.onload=function(){var i=n.doc(e.Pattern);n.width()==0&&n.height()==0&&n.size(r.width,r.height),i&&i.width()==0&&i.height()==0&&i.size(n.width(),n.height()),typeof n._loaded=="function"&&n._loaded.call(n,{width:r.width,height:r.height,ratio:r.width/r.height,url:t})},this.attr("href",r.src=this.src=t,e.xlink)},loaded:function(e){return this._loaded=e,this}},construct:{image:function(t,n,r){return this.put(new e.Image).load(t).size(n||0,r||n||0)}}}),e.Text=e.invent({create:function(){this.constructor.call(this,e.create("text")),this._leading=new e.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",e.defaults.attrs["font-family"])},inherit:e.Shape,extend:{x:function(e){return e==null?this.attr("x"):(this.textPath||this.lines.each(function(){this.newLined&&this.x(e)}),this.attr("x",e))},y:function(e){var t=this.attr("y"),n=typeof t=="number"?t-this.bbox().y:0;return e==null?typeof t=="number"?t-n:t:this.attr("y",typeof e=="number"?e+n:e)},cx:function(e){return e==null?this.bbox().cx:this.x(e-this.bbox().width/2)},cy:function(e){return e==null?this.bbox().cy:this.y(e-this.bbox().height/2)},text:function(e){if(typeof e=="undefined")return this.content;this.clear().build(!0);if(typeof e=="function")e.call(this,this);else{e=(this.content=e).split("\n");for(var t=0,n=e.length;t<n;t++)this.tspan(e[t]).newLine()}return this.build(!1).rebuild()},size:function(e){return this.attr("font-size",e).rebuild()},leading:function(t){return t==null?this._leading:(this._leading=new e.Number(t),this.rebuild())},rebuild:function(t){typeof t=="boolean"&&(this._rebuild=t);if(this._rebuild){var n=this;this.lines.each(function(){this.newLined&&(this.textPath||this.attr("x",n.attr("x")),this.attr("dy",n._leading*new e.Number(n.attr("font-size"))))}),this.fire("rebuild")}return this},build:function(e){return this._build=!!e,this}},construct:{text:function(t){return this.put(new e.Text).text(t)},plain:function(t){return this.put(new e.Text).plain(t)}}}),e.Tspan=e.invent({create:"tspan",inherit:e.Shape,extend:{text:function(e){return typeof e=="function"?e.call(this,this):this.plain(e),this},dx:function(e){return this.attr("dx",e)},dy:function(e){return this.attr("dy",e)},newLine:function(){var t=this.doc(e.Text);return this.newLined=!0,this.dy(t._leading*t.attr("font-size")).attr("x",t.x())}}}),e.extend(e.Text,e.Tspan,{plain:function(e){return this._build===!1&&this.clear(),this.node.appendChild(document.createTextNode(this.content=e)),this},tspan:function(t){var n=(this.textPath||this).node,r=new e.Tspan;return this._build===!1&&this.clear(),n.appendChild(r.node),this instanceof e.Text&&this.lines.add(r),r.text(t)},clear:function(){var t=(this.textPath||this).node;while(t.hasChildNodes())t.removeChild(t.lastChild);return this instanceof e.Text&&(delete this.lines,this.lines=new e.Set,this.content=""),this},length:function(){return this.node.getComputedTextLength()}}),e.registerEvent("rebuild"),e.TextPath=e.invent({create:"textPath",inherit:e.Element,parent:e.Text,construct:{path:function(t){this.textPath=new e.TextPath;while(this.node.hasChildNodes())this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,e.xlink),this},plot:function(e){return this.track&&this.track.plot(e),this}}}),e.Nested=e.invent({create:function(){this.constructor.call(this,e.create("svg")),this.style("overflow","visible")},inherit:e.Container,construct:{nested:function(){return this.put(new e.Nested)}}}),e.A=e.invent({create:"a",inherit:e.Container,extend:{to:function(t){return this.attr("href",t,e.xlink)},show:function(t){return this.attr("show",t,e.xlink)},target:function(e){return this.attr("target",e)}},construct:{link:function(t){return this.put(new e.A).to(t)}}}),e.extend(e.Element,{linkTo:function(t){var n=new e.A;return typeof t=="function"?t.call(n,n):n.to(t),this.parent().put(n).put(this)}}),e.Marker=e.invent({create:"marker",inherit:e.Container,extend:{width:function(e){return this.attr("markerWidth",e)},height:function(e){return this.attr("markerHeight",e)},ref:function(e,t){return this.attr("refX",e).attr("refY",t)},update:function(e){return this.clear(),typeof e=="function"&&e.call(this,this),this},toString:function(){return"url(#"+this.id()+")"}},construct:{marker:function(e,t,n){return this.defs().marker(e,t,n)}}}),e.extend(e.Defs,{marker:function(t,n,r){return this.put(new e.Marker).size(t,n).ref(t/2,n/2).viewbox(0,0,t,n).attr("orient","auto").update(r)}}),e.extend(e.Line,e.Polyline,e.Polygon,e.Path,{marker:function(t,n,r,i){var s=["marker"];return t!="all"&&s.push(t),s=s.join("-"),t=arguments[1]instanceof e.Marker?arguments[1]:this.doc().marker(n,r,i),this.attr(s,t)}});var t={stroke:["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],fill:["color","opacity","rule"],prefix:function(e,t){return t=="color"?e:e+"-"+t}};["fill","stroke"].forEach(function(n){var r,i={};i[n]=function(i){if(typeof i=="string"||e.Color.isRgb(i)||i&&typeof i.fill=="function")this.attr(n,i);else for(r=t[n].length-1;r>=0;r--)i[t[n][r]]!=null&&this.attr(t.prefix(n,t[n][r]),i[t[n][r]]);return this},e.extend(e.Element,e.FX,i)}),e.extend(e.Element,e.FX,{rotate:function(e,t,n){return this.transform({rotation:e,cx:t,cy:n})},skew:function(e,t){return this.transform({skewX:e,skewY:t})},scale:function(e,t,n,r){return this.transform({scaleX:e,scaleY:t,cx:n,cy:r})},translate:function(e,t){return this.transform({x:e,y:t})},matrix:function(t){return this.attr("transform",new e.Matrix(t))},opacity:function(e){return this.attr("opacity",e)}}),e.extend(e.Rect,e.Ellipse,e.Circle,e.FX,{radius:function(e,t){return this.rx(e).ry(t==null?e:t)}}),e.extend(e.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(e){return this.node.getPointAtLength(e)}}),e.extend(e.Parent,e.Text,e.FX,{font:function(e){for(var t in e)t=="leading"?this.leading(e[t]):t=="anchor"?this.attr("text-anchor",e[t]):t=="size"||t=="family"||t=="weight"||t=="stretch"||t=="variant"||t=="style"?this.attr("font-"+t,e[t]):this.attr(t,e[t]);return this}}),e.Set=e.invent({create:function(e){Array.isArray(e)?this.members=e:this.clear()},extend:{add:function(){var e,t,n=[].slice.call(arguments);for(e=0,t=n.length;e<t;e++)this.members.push(n[e]);return this},remove:function(e){var t=this.index(e);return t>-1&&this.members.splice(t,1),this},each:function(e){for(var t=0,n=this.members.length;t<n;t++)e.apply(this.members[t],[t,this.members]);return this},clear:function(){return this.members=[],this},has:function(e){return this.index(e)>=0},index:function(e){return this.members.indexOf(e)},get:function(e){return this.members[e]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new e.BBox;if(this.members.length==0)return t;var n=this.members[0].rbox();return t.x=n.x,t.y=n.y,t.width=n.width,t.height=n.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new e.Set(t)}}}),e.SetFX=e.invent({create:function(e){this.set=e}}),e.Set.inherit=function(){var t,n=[];for(var t in e.Shape.prototype)typeof e.Shape.prototype[t]=="function"&&typeof e.Set.prototype[t]!="function"&&n.push(t);n.forEach(function(t){e.Set.prototype[t]=function(){for(var n=0,r=this.members.length;n<r;n++)this.members[n]&&typeof this.members[n][t]=="function"&&this.members[n][t].apply(this.members[n],arguments);return t=="animate"?this.fx||(this.fx=new e.SetFX(this)):this}}),n=[];for(var t in e.FX.prototype)typeof e.FX.prototype[t]=="function"&&typeof e.SetFX.prototype[t]!="function"&&n.push(t);n.forEach(function(t){e.SetFX.prototype[t]=function(){for(var e=0,n=this.set.members.length;e<n;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},e.extend(e.Element,{data:function(e,t,n){if(typeof e=="object")for(t in e)this.data(t,e[t]);else if(arguments.length<2)try{return JSON.parse(this.attr("data-"+e))}catch(r){return this.attr("data-"+e)}else this.attr("data-"+e,t===null?null:n===!0||typeof t=="string"||typeof t=="number"?t:JSON.stringify(t));return this}}),e.extend(e.Element,{remember:function(e,t){if(typeof arguments[0]=="object")for(var t in e)this.remember(t,e[t]);else{if(arguments.length==1)return this.memory()[e];this.memory()[e]=t}return this},forget:function(){if(arguments.length==0)this._memory={};else for(var e=arguments.length-1;e>=0;e--)delete this.memory()[arguments[e]];return this},memory:function(){return this._memory||(this._memory={})}}),e.get=function(t){var n=document.getElementById(d(t)||t);if(n)return e.adopt(n)},e.select=function(t,n){return new e.Set(e.utils.map((n||document).querySelectorAll(t),function(t){return e.adopt(t)}))},e.extend(e.Parent,{select:function(t){return e.select(t,this.node)}}),typeof define=="function"&&define.amd?define(function(){return e}):typeof exports!="undefined"&&(exports.SVG=e);var v="abcdef".split("");window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1e3/60)}}();if(typeof m!="function"){function m(e,t){t=t||{bubbles:!1,cancelable:!1,detail:undefined};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),n}m.prototype=window.Event.prototype,window.CustomEvent=m}}).call(this);
\ No newline at end of file
index 94f13db80215297fe3176dbb27395e1e099ebb9b..30899fd4b482f94500823545d017e30648be526a 100755 (executable)
@@ -56,6 +56,7 @@
 <script type="text/javascript" src="spec/memory.js"></script>
 <script type="text/javascript" src="spec/arrange.js"></script>
 <script type="text/javascript" src="spec/bbox.js"></script>
+<script type="text/javascript" src="spec/matrix.js"></script>
 <script type="text/javascript" src="spec/rect.js"></script>
 <script type="text/javascript" src="spec/circle.js"></script>
 <script type="text/javascript" src="spec/ellipse.js"></script>
index 63b1a01da63979c661d0f762c2fbef47ab289ac9..523167db2082a9f98b4b16b151d1af06280d25ae 100755 (executable)
@@ -273,6 +273,12 @@ describe('Container', function() {
   })
   
   describe('viewbox()', function() {
+
+    beforeEach(function() {
+      draw.attr('viewBox', null)
+      console.log(draw.node)
+    })
+
     it('should set the viewbox when four arguments are provided', function() {
       draw.viewbox(0,0,100,100)
       expect(draw.node.getAttribute('viewBox')).toBe('0 0 100 100')
index 598ab1e6943d2d41eadb93283f2feca83f3d3b94..06112109d37cb9bcdebef6a6541e9a8b5af5eb23 100755 (executable)
@@ -1,5 +1,9 @@
 describe('Element', function() {
   
+  beforeEach(function() {
+    draw.attr('viewBox', null)
+  })
+
   afterEach(function() {
     draw.clear()
   })
@@ -15,7 +19,11 @@ describe('Element', function() {
     beforeEach(function() {
       rect = draw.rect(100,100)
     })
-    
+
+    afterEach(function() {
+      rect.remove()
+    })
+
     it('sets one attribute when two arguments are given', function() {
       rect.attr('fill', '#ff0066')
       expect(rect.node.getAttribute('fill')).toBe('#ff0066')
@@ -68,19 +76,20 @@ describe('Element', function() {
       rect.style('cursor', '')
       expect(rect.style.cursor).toBe(undefined)
     })
-    it('should act as a global getter when no arguments are given', function() {
+    it('acts as a global getter when no arguments are given', function() {
       rect.fill('#ff0066')
       expect(rect.attr().fill).toBe('#ff0066')
     })
-    it('should correctly parse numeric values as a global getter', function() {
+    it('correctly parses numeric values as a global getter', function() {
       rect.stroke({ width: 20 })
       expect(rect.attr()['stroke-width']).toBe(20)
     })
-    it('should correctly parse negative numeric values as a global getter', function() {
+    it('correctly parses negative numeric values as a global getter', function() {
       rect.x(-30)
+      console.log(rect.native())
       expect(rect.attr().x).toBe(-30)
     })
-    it('should leave unit values alone as a global getter', function() {
+    it('leaves unit values alone as a global getter', function() {
       rect.attr('x', '69%')
       expect(rect.attr().x).toBe('69%')
     })
@@ -138,27 +147,23 @@ describe('Element', function() {
   describe('transform()', function() {
     it('gets the current transformations', function() {
       var rect = draw.rect(100,100)
-      expect(rect.transform()).toEqual(SVG.defaults.trans())
+      expect(rect.transform()).toEqual(new SVG.Matrix(rect).extract())
     })
     it('sets the translation of and element', function() {
-      var rect = draw.rect(100,100).transform({ x: 10, y: 10 })
-      expect(rect.node.getAttribute('transform')).toBe('translate(10 10)')
-    })
-    it('sets the scaleX of and element', function() {
-      var rect = draw.rect(100,100).transform({ scaleX: 0.1 })
-      expect(rect.node.getAttribute('transform')).toBe('scale(0.1 1)')
+      var rect = draw.rect(100,100).transform({ x: 10, y: 11 })
+      expect(rect.node.getAttribute('transform')).toBe('matrix(1,0,0,1,10,11)')
     })
-    it('sets the scaleY of and element', function() {
-      var rect = draw.rect(100,100).transform({ scaleY: 10 })
-      expect(rect.node.getAttribute('transform')).toBe('scale(1 10)')
+    it('sets the scaleX and scaleY of and element', function() {
+      var rect = draw.rect(100,100).transform({ scaleX: 0.5, scaleY: 2 })
+      expect(rect.node.getAttribute('transform')).toBe('matrix(0.5,0,0,2,0,0)')
     })
     it('sets the skewX of and element', function() {
-      var rect = draw.rect(100,100).transform({ skewX: 0.1 })
-      expect(rect.node.getAttribute('transform')).toBe('skewX(0.1)')
+      var rect = draw.rect(100,100).transform({ skewX: 10 })
+      expect(rect.node.getAttribute('transform')).toBe('matrix(1,0,0.17632698070846498,1,0,0)')
     })
     it('sets the skewY of and element', function() {
-      var rect = draw.rect(100,100).transform({ skewY: 10 })
-      expect(rect.node.getAttribute('transform')).toBe('skewY(10)')
+      var rect = draw.rect(100,100).transform({ skewY: -10 })
+      expect(rect.node.getAttribute('transform')).toBe('matrix(1,-0.17632698070846498,0,1,0,0)')
     })
     it('rotates the element around its centre if no rotation point is given', function() {
       var rect = draw.rect(100,100).transform({ rotation: 45 })
diff --git a/spec/spec/matrix.js b/spec/spec/matrix.js
new file mode 100644 (file)
index 0000000..9689d81
--- /dev/null
@@ -0,0 +1,61 @@
+describe('Matrix', function() {
+       var matrix
+
+       describe('initialization', function() {
+               
+               describe('without a source', function() {
+                       
+                       beforeEach(function() {
+                               matrix = new SVG.Matrix
+                       })
+
+                       it('creates a new matrix with default values', function() {
+                               expect(matrix.a).toBe(1)
+                               expect(matrix.b).toBe(0)
+                               expect(matrix.c).toBe(0)
+                               expect(matrix.d).toBe(1)
+                               expect(matrix.e).toBe(0)
+                               expect(matrix.f).toBe(0)
+                       })
+                       it('parses translation values', function() {
+                               expect(matrix.x).toBe(0)
+                               expect(matrix.y).toBe(0)
+                       })
+                       it('parses skew values', function() {
+                               expect(matrix.skewX).toBe(0)
+                               expect(matrix.skewY).toBe(0)
+                       })
+                       it('parses scale values', function() {
+                               expect(matrix.scaleX).toBe(1)
+                               expect(matrix.scaleY).toBe(1)
+                       })
+                       it('parses rotaton value', function() {
+                               expect(matrix.rotation).toBe(0)
+                       })
+               })
+
+               describe('with an element given', function() {
+                       
+                       beforeEach(function() {
+                               matrix = new SVG.Matrix(draw.rect(100, 100).skew(10, 20).translate(50, 50).scale(3, 2))
+                       })
+
+                       it('parses the current transform matrix form an element', function() {
+                               expect(matrix.a).toBe(1)
+                               expect(matrix.b).toBe(0)
+                               expect(matrix.c).toBe(0)
+                               expect(matrix.d).toBe(1)
+                               expect(matrix.e).toBe(0)
+                               expect(matrix.f).toBe(0)
+                       })
+               })
+       })
+
+})
+
+
+
+
+
+
+
index 8c16aa1519c41166d47ec89dfde0e2afa99f9c78..b28213d712d1f28754bd6c00beaca0a0c46c42b4 100644 (file)
@@ -10,7 +10,6 @@ describe('Selector', function() {
       var element = draw.group()
         , got = SVG.get(element.attr('id'))
       
-      expect(got.transform()).toEqual(SVG.defaults.trans())
       expect(got.attr()).toEqual(element.attr())
     })
     it('gets a referenced element by attribute value', function() {
diff --git a/src/attr.js b/src/attr.js
new file mode 100644 (file)
index 0000000..d18cc90
--- /dev/null
@@ -0,0 +1,79 @@
+SVG.extend(SVG.Element, {
+  // Set svg element attribute
+  attr: function(a, v, n) {
+    // Act as full getter
+    if (a == null) {
+      // Get an object of attributes
+      a = {}
+      v = this.node.attributes
+      for (n = v.length - 1; n >= 0; n--)
+        a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue
+      
+      return a
+      
+    } else if (typeof a == 'object') {
+      // Apply every attribute individually if an object is passed
+      for (v in a) this.attr(v, a[v])
+      
+    } else if (v === null) {
+        // Remove value
+        this.node.removeAttribute(a)
+      
+    } else if (v == null) {
+      // Act as a getter if the first and only argument is not an object
+      v = this.node.getAttribute(a)
+      return v == null ? 
+        SVG.defaults.attrs[a] :
+      SVG.regex.isNumber.test(v) ?
+        parseFloat(v) : v
+    
+    } else {
+      // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0
+      if (a == 'stroke-width')
+        this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null)
+      else if (a == 'stroke')
+        this._stroke = v
+
+      // Convert image fill and stroke to patterns
+      if (a == 'fill' || a == 'stroke') {
+        if (SVG.regex.isImage.test(v))
+          v = this.doc().defs().image(v, 0, 0)
+
+        if (v instanceof SVG.Image)
+          v = this.doc().defs().pattern(0, 0, function() {
+            this.add(v)
+          })
+      }
+      
+      // Ensure correct numeric values (also accepts NaN and Infinity)
+      if (typeof v === 'number')
+        v = new SVG.Number(v)
+
+      // Ensure full hex color
+      else if (SVG.Color.isColor(v))
+        v = new SVG.Color(v)
+      
+      // Parse array values
+      else if (Array.isArray(v))
+        v = new SVG.Array(v)
+
+      // If the passed attribute is leading...
+      if (a == 'leading') {
+        // ... call the leading method instead
+        if (this.leading)
+          this.leading(v)
+      } else {
+        // Set given attribute on node
+        typeof n === 'string' ?
+          this.node.setAttributeNS(n, a, v.toString()) :
+          this.node.setAttribute(a, v.toString())
+      }
+      
+      // Rebuild if required
+      if (this.rebuild && (a == 'font-size' || a == 'x'))
+        this.rebuild(a, v)
+    }
+    
+    return this
+  }
+})
\ No newline at end of file
diff --git a/src/bbox.js b/src/bbox.js
deleted file mode 100755 (executable)
index 1632a02..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-SVG.BBox = function(element) {
-  var box
-
-  // Initialize zero box
-  this.x      = 0
-  this.y      = 0
-  this.width  = 0
-  this.height = 0
-  
-  // Get values if element is given
-  if (element) {
-    try {
-      // Actual, native bounding box
-      box = element.node.getBBox()
-    } catch(e) {
-      // Fallback for some browsers
-      box = {
-        x:      element.node.clientLeft
-      , y:      element.node.clientTop
-      , width:  element.node.clientWidth
-      , height: element.node.clientHeight
-      }
-    }
-    
-    // Include translations on x an y
-    this.x = box.x + element.trans.x
-    this.y = box.y + element.trans.y
-    
-    // Plain width and height
-    this.width  = box.width  * element.trans.scaleX
-    this.height = box.height * element.trans.scaleY
-  }
-
-  // Add center, right and bottom
-  boxProperties(this)
-  
-}
-
-//
-SVG.extend(SVG.BBox, {
-  // merge bounding box with another, return a new instance
-  merge: function(box) {
-    var b = new SVG.BBox
-
-    // Merge box
-    b.x      = Math.min(this.x, box.x)
-    b.y      = Math.min(this.y, box.y)
-    b.width  = Math.max(this.x + this.width,  box.x + box.width)  - b.x
-    b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
-
-    return boxProperties(b)
-  }
-
-})
\ No newline at end of file
diff --git a/src/boxes.js b/src/boxes.js
new file mode 100755 (executable)
index 0000000..e3b7618
--- /dev/null
@@ -0,0 +1,144 @@
+SVG.BBox = SVG.invent({
+  // Initialize
+  create: function(element) {
+    var box
+
+    // Initialize zero box
+    this.x      = 0
+    this.y      = 0
+    this.width  = 0
+    this.height = 0
+    
+    // Get values if element is given
+    if (element) {
+      // Get current extracted transformations
+      var t = new SVG.Matrix(element).extract()
+      
+      // Find native bbox
+      if (element.node.getBBox)
+        box = element.node.getBBox()
+      // Mimic bbox
+      else
+        box = {
+          x:      element.node.clientLeft
+        , y:      element.node.clientTop
+        , width:  element.node.clientWidth
+        , height: element.node.clientHeight
+        }
+      
+      // Include translations on x an y
+      this.x = box.x + t.x
+      this.y = box.y + t.y
+      
+      // Plain width and height
+      this.width  = box.width  * t.scaleX
+      this.height = box.height * t.scaleY
+    }
+
+    // Add center, right and bottom
+    fullBox(this)
+  }
+
+  // define Parent
+, parent: SVG.Element
+
+  // Constructor
+, construct: {
+    // Get bounding box
+    bbox: function() {
+      return new SVG.BBox(this)
+    }
+  }
+
+})
+
+SVG.RBox = SVG.invent({
+  // Initialize
+  create: function(element) {
+    var e, zoom
+      , box = {}
+
+    // Initialize zero box
+    this.x      = 0
+    this.y      = 0
+    this.width  = 0
+    this.height = 0
+    
+    if (element) {
+      e = element.doc().parent()
+      zoom = element.doc().viewbox().zoom
+      
+      // Actual, native bounding box
+      box = element.node.getBoundingClientRect()
+      
+      // Get screen offset
+      this.x = box.left
+      this.y = box.top
+      
+      // Subtract parent offset
+      this.x -= e.offsetLeft
+      this.y -= e.offsetTop
+      
+      while (e = e.offsetParent) {
+        this.x -= e.offsetLeft
+        this.y -= e.offsetTop
+      }
+      
+      // Calculate cumulative zoom from svg documents
+      e = element
+      while (e.parent && (e = e.parent())) {
+        if (e.viewbox) {
+          zoom *= e.viewbox().zoom
+          this.x -= e.x() || 0
+          this.y -= e.y() || 0
+        }
+      }
+    }
+    
+    // Recalculate viewbox distortion
+    this.x /= zoom
+    this.y /= zoom
+    this.width  = box.width  /= zoom
+    this.height = box.height /= zoom
+    
+    // Offset by window scroll position, because getBoundingClientRect changes when window is scrolled
+    this.x += window.scrollX
+    this.y += window.scrollY
+
+    // Add center, right and bottom
+    fullBox(this)
+  }
+
+  // define Parent
+, parent: SVG.Element
+
+  // Constructor
+, construct: {
+    // Get rect box
+    rbox: function() {
+      return new SVG.RBox(this)
+    }
+  }
+
+})
+
+// Add universal merge method
+;[SVG.BBox, SVG.RBox].forEach(function(c) {
+
+  SVG.extend(c, {
+    // Merge rect box with another, return a new instance
+    merge: function(box) {
+      var b = new c()
+
+      // Merge box
+      b.x      = Math.min(this.x, box.x)
+      b.y      = Math.min(this.y, box.y)
+      b.width  = Math.max(this.x + this.width,  box.x + box.width)  - b.x
+      b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
+      
+      return fullBox(b)
+    }
+
+  })
+
+})
index 3d7de1238ea49ca12e6939b3c40b1a7c50ecce7f..6310b6b03f692d2f50b71e86ea4ce680487848a3 100755 (executable)
@@ -21,7 +21,7 @@ SVG.Element = SVG.invent({
     x: function(x) {
       if (x != null) {
         x = new SVG.Number(x)
-        x.value /= this.trans.scaleX
+        x.value /= this.ctm().extract().scaleX
       }
       return this.attr('x', x)
     }
@@ -29,7 +29,7 @@ SVG.Element = SVG.invent({
   , y: function(y) {
       if (y != null) {
         y = new SVG.Number(y)
-        y.value /= this.trans.scaleY
+        y.value /= this.ctm().extract().scaleY
       }
       return this.attr('y', y)
     }
@@ -90,139 +90,10 @@ SVG.Element = SVG.invent({
   , putIn: function(parent) {
       return parent.add(this)
     }
-    // Get parent document
-  , doc: function(type) {
-      return this.parent(type || SVG.Doc)
-    }
-    // Set svg element attribute
-  , attr: function(a, v, n) {
-      // Act as full getter
-      if (a == null) {
-        // Get an object of attributes
-        a = {}
-        v = this.node.attributes
-        for (n = v.length - 1; n >= 0; n--)
-          a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue
-        
-        return a
-        
-      } else if (typeof a == 'object') {
-        // Apply every attribute individually if an object is passed
-        for (v in a) this.attr(v, a[v])
-        
-      } else if (v === null) {
-          // Remove value
-          this.node.removeAttribute(a)
-        
-      } else if (v == null) {
-        // Act as a getter if the first and only argument is not an object
-        v = this.node.getAttribute(a)
-        return v == null ? 
-          SVG.defaults.attrs[a] :
-        SVG.regex.isNumber.test(v) ?
-          parseFloat(v) : v
-      
-      } else {
-        // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0
-        if (a == 'stroke-width')
-          this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null)
-        else if (a == 'stroke')
-          this._stroke = v
-
-        // Convert image fill and stroke to patterns
-        if (a == 'fill' || a == 'stroke') {
-          if (SVG.regex.isImage.test(v))
-            v = this.doc().defs().image(v, 0, 0)
-
-          if (v instanceof SVG.Image)
-            v = this.doc().defs().pattern(0, 0, function() {
-              this.add(v)
-            })
-        }
-        
-        // Ensure correct numeric values (also accepts NaN and Infinity)
-        if (typeof v === 'number')
-          v = new SVG.Number(v)
-
-        // Ensure full hex color
-        else if (SVG.Color.isColor(v))
-          v = new SVG.Color(v)
-        
-        // Parse array values
-        else if (Array.isArray(v))
-          v = new SVG.Array(v)
-
-        // If the passed attribute is leading...
-        if (a == 'leading') {
-          // ... call the leading method instead
-          if (this.leading)
-            this.leading(v)
-        } else {
-          // Set given attribute on node
-          typeof n === 'string' ?
-            this.node.setAttributeNS(n, a, v.toString()) :
-            this.node.setAttribute(a, v.toString())
-        }
-        
-        // Rebuild if required
-        if (this.rebuild && (a == 'font-size' || a == 'x'))
-          this.rebuild(a, v)
-      }
-      
-      return this
-    }
-    // Manage transformations
-  , transform: function(t, v) {
-      // Get a transformation at a given position
-      if (typeof t === 'number') {
-        
-      }
-        
-      return this
-    }
-    // Dynamic style generator
-  , style: function(s, v) {
-      if (arguments.length == 0) {
-        /* get full style */
-        return this.node.style.cssText || ''
-      
-      } else if (arguments.length < 2) {
-        /* apply every style individually if an object is passed */
-        if (typeof s == 'object') {
-          for (v in s) this.style(v, s[v])
-        
-        } else if (SVG.regex.isCss.test(s)) {
-          /* parse css string */
-          s = s.split(';')
-
-          /* apply every definition individually */
-          for (var i = 0; i < s.length; i++) {
-            v = s[i].split(':')
-            this.style(v[0].replace(/\s+/g, ''), v[1])
-          }
-        } else {
-          /* act as a getter if the first and only argument is not an object */
-          return this.node.style[camelCase(s)]
-        }
-      
-      } else {
-        this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v
-      }
-      
-      return this
-    }
     // Get / set id
   , id: function(id) {
       return this.attr('id', id)
     }
-    // Get bounding box
-  , bbox: function() {
-      return new SVG.BBox(this)
-    }
-    // Get rect box
-  , rbox: function() {
-      return new SVG.RBox(this)
-    }
     // Checks whether the given point inside the bounding box of the element
   , inside: function(x, y) {
       var box = this.bbox()
@@ -271,10 +142,9 @@ SVG.Element = SVG.invent({
     // Remove class from the node
   , removeClass: function(name) {
       if (this.hasClass(name)) {
-        var array = this.classes().filter(function(c) {
+        this.attr('class', this.classes().filter(function(c) {
           return c != name
-        })
-        this.attr('class', array.join(' '))
+        }).join(' '))
       }
 
       return this
@@ -299,5 +169,13 @@ SVG.Element = SVG.invent({
 
       return parent
     }
+    // Get parent document
+  , doc: function(type) {
+      return this.parent(type || SVG.Doc)
+    }
+    // Returns the svg node to call native svg methods on it
+  , native: function() {
+      return this.node
+    }
   }
 })
index aab80871a0614ee637f8802890af2239178271a1..f4ab6c9bbee4ffed7c45c59a4c15fd709ea38405 100755 (executable)
@@ -64,11 +64,11 @@ SVG.extend(SVG.Circle, SVG.Ellipse, {
     }
     // Move by center over x-axis
   , cx: function(x) {
-      return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX))
+      return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.ctm().scaleX))
     }
     // Move by center over y-axis
   , cy: function(y) {
-      return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY))
+      return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.ctm().scaleY))
     }
     // Set width of element
   , width: function(width) {
index cfb99ac8bf79910eaf20935edb57fd4fe0bda645..a7ec2e0c3690ea1054e9684688a9ab05e628ee0b 100755 (executable)
@@ -9,11 +9,11 @@ SVG.G = SVG.invent({
 , extend: {
     // Move over x-axis
     x: function(x) {
-      return x == null ? this.trans.x : this.transform('x', x)
+      return x == null ? this.ctm().x : this.transform('x', x)
     }
     // Move over y-axis
   , y: function(y) {
-      return y == null ? this.trans.y : this.transform('y', y)
+      return y == null ? this.ctm().y : this.transform('y', y)
     }
     // Move by center over x-axis
   , cx: function(x) {
index 9411e5e1c12443e1da3fd6c0ef4aae2c531d4c2f..11f50173f7337ebc3df1fabf23e850cd17a77d40 100644 (file)
 // Convert dash-separated-string to camelCase
 function camelCase(s) { 
-  return s.toLowerCase().replace(/-(.)/g, function(m, g) {
-    return g.toUpperCase()
-  })
+       return s.toLowerCase().replace(/-(.)/g, function(m, g) {
+               return g.toUpperCase()
+       })
 }
 
 // Capitalize first letter of a string
 function capitalize(s) {
-  return s.charAt(0).toUpperCase() + s.slice(1)
+       return s.charAt(0).toUpperCase() + s.slice(1)
 }
 
 // Ensure to six-based hex 
 function fullHex(hex) {
-  return hex.length == 4 ?
-    [ '#',
-      hex.substring(1, 2), hex.substring(1, 2)
-    , hex.substring(2, 3), hex.substring(2, 3)
-    , hex.substring(3, 4), hex.substring(3, 4)
-    ].join('') : hex
+       return hex.length == 4 ?
+               [ '#',
+                       hex.substring(1, 2), hex.substring(1, 2)
+               , hex.substring(2, 3), hex.substring(2, 3)
+               , hex.substring(3, 4), hex.substring(3, 4)
+               ].join('') : hex
 }
 
 // Component to hex value
 function compToHex(comp) {
-  var hex = comp.toString(16)
-  return hex.length == 1 ? '0' + hex : hex
+       var hex = comp.toString(16)
+       return hex.length == 1 ? '0' + hex : hex
 }
 
 // Calculate proportional width and height values when necessary
 function proportionalSize(box, width, height) {
-  if (width == null || height == null) {
-    if (height == null)
-      height = box.height / box.width * width
-    else if (width == null)
-      width = box.width / box.height * height
-  }
-  
-  return {
-    width:  width
-  , height: height
-  }
+       if (width == null || height == null) {
+               if (height == null)
+                       height = box.height / box.width * width
+               else if (width == null)
+                       width = box.width / box.height * height
+       }
+       
+       return {
+               width:  width
+       , height: height
+       }
+}
+
+// Delta transform point
+function deltaTransformPoint(matrix, point) {
+       return {
+               x: point.x * matrix.a + point.y * matrix.c + 0
+       , y: point.x * matrix.b + point.y * matrix.d + 0
+       }
+}
+
+// Map matrix array to object
+function arrayToMatrix(a) {
+       return { a: a[0], b: a[1], c: a[2], e: a[3], f: a[4], g: a[5] }
 }
 
 // Calculate position according to from and to
 function at(o, pos) {
-  /* number recalculation (don't bother converting to SVG.Number for performance reasons) */
-  return typeof o.from == 'number' ?
-    o.from + (o.to - o.from) * pos :
-  
-  /* instance recalculation */
-  o instanceof SVG.Color || o instanceof SVG.Number ? o.at(pos) :
-  
-  /* for all other values wait until pos has reached 1 to return the final value */
-  pos < 1 ? o.from : o.to
+       /* number recalculation (don't bother converting to SVG.Number for performance reasons) */
+       return typeof o.from == 'number' ?
+               o.from + (o.to - o.from) * pos :
+       
+       /* instance recalculation */
+       o instanceof SVG.Color || o instanceof SVG.Number ? o.at(pos) :
+       
+       /* for all other values wait until pos has reached 1 to return the final value */
+       pos < 1 ? o.from : o.to
 }
 
 // PathArray Helpers
 function arrayToString(a) {
-  for (var i = 0, il = a.length, s = ''; i < il; i++) {
-    s += a[i][0]
-
-    if (a[i][1] != null) {
-      s += a[i][1]
-
-      if (a[i][2] != null) {
-        s += ' '
-        s += a[i][2]
-
-        if (a[i][3] != null) {
-          s += ' '
-          s += a[i][3]
-          s += ' '
-          s += a[i][4]
-
-          if (a[i][5] != null) {
-            s += ' '
-            s += a[i][5]
-            s += ' '
-            s += a[i][6]
-
-            if (a[i][7] != null) {
-              s += ' '
-              s += a[i][7]
-            }
-          }
-        }
-      }
-    }
-  }
-  
-  return s + ' '
+       for (var i = 0, il = a.length, s = ''; i < il; i++) {
+               s += a[i][0]
+
+               if (a[i][1] != null) {
+                       s += a[i][1]
+
+                       if (a[i][2] != null) {
+                               s += ' '
+                               s += a[i][2]
+
+                               if (a[i][3] != null) {
+                                       s += ' '
+                                       s += a[i][3]
+                                       s += ' '
+                                       s += a[i][4]
+
+                                       if (a[i][5] != null) {
+                                               s += ' '
+                                               s += a[i][5]
+                                               s += ' '
+                                               s += a[i][6]
+
+                                               if (a[i][7] != null) {
+                                                       s += ' '
+                                                       s += a[i][7]
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       return s + ' '
 }
 
 // Deep new id assignment
 function assignNewId(node) {
-  // Do the same for SVG child nodes as well
-  for (var i = node.childNodes.length - 1; i >= 0; i--)
-    if (node.childNodes[i] instanceof SVGElement)
-      assignNewId(node.childNodes[i])
+       // Do the same for SVG child nodes as well
+       for (var i = node.childNodes.length - 1; i >= 0; i--)
+               if (node.childNodes[i] instanceof SVGElement)
+                       assignNewId(node.childNodes[i])
 
-  return SVG.adopt(node).id(SVG.eid(node.nodeName))
+       return SVG.adopt(node).id(SVG.eid(node.nodeName))
 }
 
 // Add more bounding box properties
-function boxProperties(b) {
-  b.x2 = b.x + b.width
-  b.y2 = b.y + b.height
-  b.cx = b.x + b.width / 2
-  b.cy = b.y + b.height / 2
+function fullBox(b) {
+       b.x2 = b.x + b.width
+       b.y2 = b.y + b.height
+       b.cx = b.x + b.width / 2
+       b.cy = b.y + b.height / 2
 
-  return b
+       return b
 }
 
 // Parse a matrix string
 function parseMatrix(o) {
-  if (o.matrix) {
-    // Split matrix string
-    var m = o.matrix.replace(/\s/g, '').split(',')
-    
-    // Pasrse values
-    if (m.length == 6) {
-      o.a = parseFloat(m[0])
-      o.b = parseFloat(m[1])
-      o.c = parseFloat(m[2])
-      o.d = parseFloat(m[3])
-      o.e = parseFloat(m[4])
-      o.f = parseFloat(m[5])
-    }
-  }
-  
-  return o
+       if (o.matrix) {
+               // Split matrix string
+               var m = o.matrix.replace(/\s/g, '').split(',')
+               
+               // Pasrse values
+               if (m.length == 6) {
+                       o.a = parseFloat(m[0])
+                       o.b = parseFloat(m[1])
+                       o.c = parseFloat(m[2])
+                       o.d = parseFloat(m[3])
+                       o.e = parseFloat(m[4])
+                       o.f = parseFloat(m[5])
+               }
+       }
+       
+       return o
 }
 
 // Get id from reference string
 function idFromReference(url) {
-  var m = url.toString().match(SVG.regex.reference)
+       var m = url.toString().match(SVG.regex.reference)
 
-  if (m) return m[1]
+       if (m) return m[1]
 }
 
+// Create matrix array for looping
+var abcdef = 'abcdef'.split('')
+
 // Shim layer with setTimeout fallback by Paul Irish
 window.requestAnimFrame = (function(){
-  return  window.requestAnimationFrame       ||
-          window.webkitRequestAnimationFrame ||
-          window.mozRequestAnimationFrame    ||
-          window.msRequestAnimationFrame     ||
-          function (c) { window.setTimeout(c, 1000 / 60) }
+       return  window.requestAnimationFrame       ||
+                                       window.webkitRequestAnimationFrame ||
+                                       window.mozRequestAnimationFrame    ||
+                                       window.msRequestAnimationFrame     ||
+                                       function (c) { window.setTimeout(c, 1000 / 60) }
 })()
\ No newline at end of file
index d76f89132b6171d131085ed096276c3dc6c92725..0fcf63fe3e4edc1912ec9bd4ffe86255273a6e6e 100755 (executable)
@@ -9,17 +9,17 @@ SVG.Line = SVG.invent({
 , extend: {
     // Get array
     array: function() {
-      return (this._array = new SVG.PointArray([
+      return new SVG.PointArray([
         [ this.attr('x1'), this.attr('y1') ]
       , [ this.attr('x2'), this.attr('y2') ]
-      ]))
+      ])
     }
     // Overwrite native plot() method
   , plot: function(x1, y1, x2, y2) {
-      if (typeof x1 === 'number')
+      if (arguments.length == 4)
         x1 = { x1: x1, y1: y1, x2: x2, y2: y2 }
       else 
-        x1 = (this._array = new SVG.PointArray(x1)).toLine()
+        x1 = new SVG.PointArray(x1).toLine()
 
       return this.attr(x1)
     }
index 2202011789111433e2694430c4570ea9c5cbfc36..fb61bf0dc9e4e534c6236c9b1715473a17a5b3e4 100644 (file)
@@ -1,3 +1,113 @@
-SVG.Matrix = function() {
+SVG.Matrix = SVG.invent({
+       // Initialize
+       create: function(source) {
+               var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0])
 
-}
\ No newline at end of file
+               // Ensure source as object
+               source = source.node && source.node.getCTM ?
+                       source.node.getCTM() :
+               typeof source === 'string' ?
+                       arrayToMatrix(source.replace(/\s/g, '').split(',')) :
+               arguments.length == 6 ?
+                       arrayToMatrix([].slice.call(arguments)) :
+               typeof source === 'object' ?
+                       source : base
+
+               // Merge source
+               for (i = abcdef.length - 1; i >= 0; i--)
+                       this[abcdef[i]] = typeof source[abcdef[i]] === 'number' ?
+                               source[abcdef[i]] : base[abcdef[i]]
+               
+       }
+       
+       // Add methods
+, extend: {
+               // Extract individual transformations
+         extract: function() {
+                       // Find transform points
+                       var px = deltaTransformPoint(this, { x: 0, y: 1 })
+                               , py = deltaTransformPoint(this, { x: 1, y: 0 })
+       
+                       return {
+                               // Translation
+                               x:                              this.e
+                       , y:                            this.f
+                               // Skew
+                       , skewX:                180 / Math.PI * Math.atan2(px.y, px.x) - 90
+                       , skewY:                180 / Math.PI * Math.atan2(py.y, py.x)
+                               // Scale
+                       , scaleX:       Math.sqrt(this.a * this.a + this.b * this.b)
+                       , scaleY:       Math.sqrt(this.c * this.c + this.d * this.d)
+                               // Rotation
+                       , rotation: this.skewX
+                       }
+               }
+               // Multiply
+       , multiply: function(matrix) {
+                       return new SVG.Matrix(this.native().multiply(matrix.native()))
+               }
+               // Inverse
+       , inverse: function() {
+                       return new SVG.Matrix(this.native().inverse())
+               }
+               // Translate
+       , translate: function(x, y) {
+                       return new SVG.Matrix(this.native().translate(x || 0, y || 0))  
+               }
+               // Scale
+       , scale: function(x, y, cx, cy) {
+                       if (y == null)
+                               return new SVG.Matrix(this.native().scale(x))
+                       else
+                               return new SVG.Matrix(this.native().scaleNonUniform(x, y))
+               }
+               // Rotate
+       , rotate: function(d, x, y) {
+                       // Fall back to native rotate method 
+                       if (x == null) return new SVG.Matrix(this.native().rotate(d))
+
+                       // Convert degrees to radians
+                       d = SVG.utils.radians(d)
+
+                       return new SVG.Matrix(1, 0, 0, 1, x, y)
+                               //.multiply(new SVG.Matrix(Math.cos(d), Math.sin(d), -Math.sin(d), Math.cos(d), 0, 0))
+                               //.multiply(new SVG.Matrix(1, 0, 0, 1, -x, -y))
+               }
+               // Flip
+       , flip: function(a) {
+                       return new SVG.Matrix(this.native()['flip' + a.toUpperCase()]())
+               }
+               // Skew
+       , skew: function(x, y) {
+                       return new SVG.Matrix(this.native().skewX(x || 0).skewY(y || 0))
+               }
+               // Convert this to SVGMatrix
+       , native: function() {
+                       // Create new matrix
+                       var i, matrix = SVG.parser.draw.node.createSVGMatrix()
+       
+                       // Update with current values
+                       for (i = abcdef.length - 1; i >= 0; i--)
+                               matrix[abcdef[i]] = this[abcdef[i]]
+
+                       return matrix
+               }
+               // Convert array to string
+       , toString: function() {
+                       return 'matrix(' + [this.a, this.b, this.c, this.d, this.e, this.f].join() + ')'
+               }
+       }
+
+       // Define parent
+, parent: SVG.Element
+
+       // Add parent method
+, construct: {
+               // Get current matrix
+               ctm: function() {
+                       return new SVG.Matrix(this)
+               }
+       
+       }
+
+})
\ No newline at end of file
index 746cdc4a96bf57d847d262c38a968600b7e7e1bf..e127576e22ef1b94d624b85a61ccd02b956ebc68 100755 (executable)
@@ -186,7 +186,7 @@ SVG.extend(SVG.PathArray, {
       else if (s == 'Q')
         x.push(seg.x1, seg.y1, seg.x, seg.y)
       else if (s == 'A')
-        x.push(seg.r1, seg.r2, seg.angle, seg.largeArcFlag|0, seg.sweepFlag|0, seg.x, seg.y)
+        x.push(seg.r1, seg.r2, seg.angle, seg.largeArcFlag | 0, seg.sweepFlag | 0, seg.x, seg.y)
 
       /* store segment */
       array.push(x)
diff --git a/src/rbox.js b/src/rbox.js
deleted file mode 100755 (executable)
index 6ede8ae..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-// Get the rectangular box of a given element
-SVG.RBox = function(element) {
-  var e, zoom
-    , box = {}
-
-  /* initialize zero box */
-  this.x      = 0
-  this.y      = 0
-  this.width  = 0
-  this.height = 0
-  
-  if (element) {
-    e = element.doc().parent()
-    zoom = element.doc().viewbox().zoom
-    
-    /* actual, native bounding box */
-    box = element.node.getBoundingClientRect()
-    
-    /* get screen offset */
-    this.x = box.left
-    this.y = box.top
-    
-    /* subtract parent offset */
-    this.x -= e.offsetLeft
-    this.y -= e.offsetTop
-    
-    while (e = e.offsetParent) {
-      this.x -= e.offsetLeft
-      this.y -= e.offsetTop
-    }
-    
-    /* calculate cumulative zoom from svg documents */
-    e = element
-    while (e.parent && (e = e.parent())) {
-      if (e.type == 'svg' && e.viewbox) {
-        zoom *= e.viewbox().zoom
-        this.x -= e.x() || 0
-        this.y -= e.y() || 0
-      }
-    }
-  }
-  
-  /* recalculate viewbox distortion */
-  this.x /= zoom
-  this.y /= zoom
-  this.width  = box.width  /= zoom
-  this.height = box.height /= zoom
-  
-  /* offset by window scroll position, because getBoundingClientRect changes when window is scrolled */
-  this.x += window.scrollX
-  this.y += window.scrollY
-
-  /* add center, right and bottom */
-  boxProperties(this)
-  
-}
-
-//
-SVG.extend(SVG.RBox, {
-  // merge rect box with another, return a new instance
-  merge: function(box) {
-    var b = new SVG.RBox()
-
-    /* merge box */
-    b.x      = Math.min(this.x, box.x)
-    b.y      = Math.min(this.y, box.y)
-    b.width  = Math.max(this.x + this.width,  box.x + box.width)  - b.x
-    b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
-    
-    return boxProperties(b)
-  }
-
-})
index 13f62d817c15014be1ac8bd8d3e11d678bd51c47..8e974a9c9fb328bfbd85bf7d404fea13e014a138 100755 (executable)
@@ -1,39 +1,39 @@
 // Storage for regular expressions
 SVG.regex = {
   /* parse unit value */
-  unit:         /^(-?[\d\.]+)([a-z%]{0,2})$/
+  unit:             /^(-?[\d\.]+)([a-z%]{0,2})$/
   
   /* parse hex value */
-, hex:          /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
+, hex:              /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
   
   /* parse rgb value */
-, rgb:          /rgb\((\d+),(\d+),(\d+)\)/
+, rgb:              /rgb\((\d+),(\d+),(\d+)\)/
   
   /* parse reference id */
-, reference:    /#([a-z0-9\-_]+)/i
+, reference:        /#([a-z0-9\-_]+)/i
 
   /* test hex value */
-, isHex:        /^#[a-f0-9]{3,6}$/i
+, isHex:            /^#[a-f0-9]{3,6}$/i
   
   /* test rgb value */
-, isRgb:        /^rgb\(/
+, isRgb:            /^rgb\(/
   
   /* test css declaration */
-, isCss:        /[^:]+:[^;]+;?/
+, isCss:            /[^:]+:[^;]+;?/
   
   /* test for blank string */
-, isBlank:      /^(\s+)?$/
+, isBlank:          /^(\s+)?$/
   
   /* test for numeric string */
-, isNumber:     /^-?[\d\.]+$/
+, isNumber:         /^-?[\d\.]+$/
 
   /* test for percent value */
-, isPercent:    /^-?[\d\.]+%$/
+, isPercent:        /^-?[\d\.]+%$/
 
   /* test for image url */
-, isImage:      /\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i
+, isImage:          /\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i
   
   /* test for namespaced event */
-, isEvent:      /^[\w]+:[\w]+$/
+, isEvent:          /^[\w]+:[\w]+$/
 
 }
\ No newline at end of file
diff --git a/src/style.js b/src/style.js
new file mode 100644 (file)
index 0000000..a7f737d
--- /dev/null
@@ -0,0 +1,33 @@
+SVG.extend(SVG.Element, {
+  // Dynamic style generator
+  style: function(s, v) {
+    if (arguments.length == 0) {
+      /* get full style */
+      return this.node.style.cssText || ''
+    
+    } else if (arguments.length < 2) {
+      /* apply every style individually if an object is passed */
+      if (typeof s == 'object') {
+        for (v in s) this.style(v, s[v])
+      
+      } else if (SVG.regex.isCss.test(s)) {
+        /* parse css string */
+        s = s.split(';')
+
+        /* apply every definition individually */
+        for (var i = 0; i < s.length; i++) {
+          v = s[i].split(':')
+          this.style(v[0].replace(/\s+/g, ''), v[1])
+        }
+      } else {
+        /* act as a getter if the first and only argument is not an object */
+        return this.node.style[camelCase(s)]
+      }
+    
+    } else {
+      this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v
+    }
+    
+    return this
+  }
+})
\ No newline at end of file
index 493c6c78616a66ee040824be817ac089b9f346a8..e7598125fa4098a8e947a215c0c1c3873c297d4b 100755 (executable)
@@ -30,37 +30,24 @@ var sugar = {
 
 SVG.extend(SVG.Element, SVG.FX, {
   // Rotation
-  rotate: function(deg, x, y) {
-    return this.transform({
-      rotation: deg || 0
-    , cx: x
-    , cy: y
-    })
+  rotate: function(d, cx, cy) {
+    return this.transform({ rotation: d, cx: cx, cy: cy })
   }
   // Skew
 , skew: function(x, y) {
-    return this.transform({
-      skewX: x || 0
-    , skewY: y || 0
-    })
+    return this.transform({ skewX: x, skewY: y })
   }
   // Scale
-, scale: function(x, y) {
-    return this.transform({
-      scaleX: x
-    , scaleY: y == null ? x : y
-    })
+, scale: function(x, y, cx, cy) {
+    return this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy })
   }
   // Translate
 , translate: function(x, y) {
-    return this.transform({
-      x: x
-    , y: y
-    })
+    return this.transform({ x: x, y: y })
   }
   // Matrix
 , matrix: function(m) {
-    return this.transform({ matrix: m })
+    return this.attr('transform', new SVG.Matrix(m))
   }
   // Opacity
 , opacity: function(value) {
diff --git a/src/transform.js b/src/transform.js
new file mode 100644 (file)
index 0000000..c78865d
--- /dev/null
@@ -0,0 +1,47 @@
+SVG.extend(SVG.Element, {
+       // Add transformations
+       transform: function(o) {
+               // Full getter
+               if (o == null)
+                       return this.ctm().extract()
+
+               // Get current matrix
+               var matrix = new SVG.Matrix(this)
+
+               // Act on matrix
+               if (o.a != null)
+                       matrix = matrix.multiply(new SVG.Matrix(o))
+               
+               // Act on rotate
+               else if (o.rotation)
+                       matrix = matrix.rotate(
+                               o.rotation
+                       , o.cx == null ? this.bbox().cx : o.cx
+                       , o.cy == null ? this.bbox().cy : o.cy
+                       )
+
+               // Act on scale
+               else if (o.scale != null || o.scaleX != null || o.scaleY != null)
+                       matrix = matrix.scale(
+                               o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
+                       , o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
+                       , o.cx          != null ? o.cx          : this.bbox().x
+                       , o.cy          != null ? o.cy          : this.bbox().y
+                       )
+
+               // Act on skew
+               else if (o.skewX || o.skewY)
+                       matrix = matrix.skew(o.skewX, o.skewY)
+
+               // Act on translate
+               else if (o.x || o.y)
+                       matrix = matrix.translate(o.x, o.y)
+console.log(o, matrix)
+               return this.attr('transform', matrix)
+       }
+       // Reset all transformations
+, untransform: function() {
+               return this.attr('transform', null)
+       }
+
+})
\ No newline at end of file
diff --git a/src/transporter.js b/src/transporter.js
new file mode 100644 (file)
index 0000000..d2cf609
--- /dev/null
@@ -0,0 +1,33 @@
+SVG.extend(SVG.Parent, SVG.Text, {
+  // Import svg SVG data
+  svg: function(svg) {
+    // create temporary div to receive svg content
+    var element = document.createElement('div')
+
+    if (svg) {
+      // strip away newlines and properly close tags
+      svg = svg
+        .replace(/\n/, '')
+        .replace(/<(\w+)([^<]+?)\/>/g, '<$1$2></$1>')
+
+      // ensure SVG wrapper for correct element type casting
+      element.innerHTML = '<svg>' + svg + '</svg>'
+
+      // transplant content from well to target
+      for (var i = element.firstChild.childNodes.length - 1; i >= 0; i--)
+        if (element.firstChild.childNodes[i].nodeType == 1)
+          this.node.appendChild(element.firstChild.childNodes[i])
+
+      return this
+
+    } else {
+      // clone element and its contents
+      var clone  = this.node.cloneNode(true)
+
+      // add target to clone
+      element.appendChild(clone)
+
+      return element.innerHTML
+    }
+  }
+})
\ No newline at end of file
index 4770f9b8ab9025020ab08585c2c181dac355651f..0c1a0a391f0383eafe1e17b58e8bc272f5f7c63c 100644 (file)
@@ -11,4 +11,13 @@ SVG.utils = {
     return result
   }
 
+  // Degrees to radians
+, radians: function(d) {
+               return d % 360 * Math.PI / 180
+       }
+       // Radians to degrees
+, degrees: function(r) {
+               return r * 180 / Math.PI % 360
+       }
+
 }
\ No newline at end of file