]> source.dussan.org Git - svg.js.git/commitdiff
Added SVG.Number, reworked gradient system
authorwout <wout@impinc.co.uk>
Sun, 23 Jun 2013 13:59:46 +0000 (14:59 +0100)
committerwout <wout@impinc.co.uk>
Sun, 23 Jun 2013 13:59:46 +0000 (14:59 +0100)
16 files changed:
README.md
Rakefile
dist/svg.js
dist/svg.min.js
package.json
spec/index.html
spec/spec/ellipse.js
spec/spec/gradient.js
spec/spec/number.js [new file with mode: 0644]
src/default.js
src/element.js
src/ellipse.js
src/fx.js
src/gradient.js
src/number.js [new file with mode: 0644]
src/regex.js

index 5e326a778bcc5abcdad66c75dec818c4b719eaee..d226f0745d5596d9d155af460bb3919996365f59 100644 (file)
--- a/README.md
+++ b/README.md
@@ -771,7 +771,7 @@ If you want the masked object to be rendered at 100% you need to set the fill co
 ```javascript
 var gradient = draw.gradient('linear', function(stop) {
   stop.at({ offset: 0, color: '#000' })
-  stop.at({ offset: 100, color: '#fff' })
+  stop.at({ offset: 1, color: '#fff' })
 })
 
 var ellipse = draw.ellipse(80, 40).move(10, 10).fill({ color: gradient })
@@ -881,14 +881,14 @@ There are linear and radial gradients. The linear gradient can be created like t
 ```javascript
 var gradient = draw.gradient('linear', function(stop) {
   stop.at({ offset: 0, color: '#333', opacity: 1 })
-  stop.at({ offset: 100, color: '#fff', opacity: 1 })
+  stop.at({ offset: 1, color: '#fff', opacity: 1 })
 })
 ```
 
-The `offset` and `color` parameters are required for stops, `opacity` is optional. Offset is an integer expressed in percentage. To define the direction you can set from `x`, `y` and to `x`, `y`:
+The `offset` and `color` parameters are required for stops, `opacity` is optional. Offset is float between 0 and 1, or a percentage value (e.g. `33%`). To define the direction you can set from `x`, `y` and to `x`, `y`:
 
 ```javascript
-gradient.from(0, 0).to(0, 100)
+gradient.from(0, 0).to(0, 1)
 ```
 
 The from and to values are also expressed in percent.
@@ -903,18 +903,18 @@ Radial gradients have a `radius()` method to define the outermost radius to wher
 ```javascript
 var gradient = draw.gradient('radial', function(stop) {
   stop.at({ offset: 0, color: '#333', opacity: 1 })
-  stop.at({ offset: 100, color: '#fff', opacity: 1 })
+  stop.at({ offset: 1, color: '#fff', opacity: 1 })
 })
 
-gradient.from(50, 50).to(50, 50).radius(50)
+gradient.from(0.5, 0.5).to(0.5, 0.5).radius(0.5)
 ```
 
 A gradient can also be updated afterwards:
 
 ```javascript
 gradient.update(function(stop) {
-  stop.at({ offset: 10, color: '#333', opacity: 0.2 })
-  stop.at({ offset: 90, color: '#f03', opacity: 1 })
+  stop.at({ offset: 0.1, color: '#333', opacity: 0.2 })
+  stop.at({ offset: 0.9, color: '#f03', opacity: 1 })
 })
 ```
 
@@ -925,11 +925,23 @@ var s1, s2, s3
 
 draw.gradient('radial', function(stop) {
   s1 = stop.at({ offset: 0, color: '#000', opacity: 1 })
-  s2 = stop.at({ offset: 50, color: '#f03', opacity: 1 })
-  s3 = stop.at({ offset: 100, color: '#066', opacity: 1 })
+  s2 = stop.at({ offset: 0.5, color: '#f03', opacity: 1 })
+  s3 = stop.at({ offset: 1, color: '#066', opacity: 1 })
 })
 
-s1.update({ offset: 10, color: '#0f0', opacity: 1 })
+s1.update({ offset: 0.1, color: '#0f0', opacity: 1 })
+```
+
+The `get()` method makes it even easier to get a stop from an existing gradient:
+
+```javascript
+var gradient = draw.gradient('radial', function(stop) {
+  stop.at({ offset: 0, color: '#000', opacity: 1 })   // -> first
+  stop.at({ offset: 0.5, color: '#f03', opacity: 1 }) // -> second
+  stop.at({ offset: 1, color: '#066', opacity: 1 })   // -> third
+})
+
+var s1 = gradient.get(0) // -> returns "first" stop
 ```
 
 [W3Schools](http://www.w3schools.com/svg/svg_grad_linear.asp) has a great example page on how
index 03e72d3fb7e8ffb6aa4f5378628f4471614f2d3e..da0d350747f2d11d8678194c5f7c1eef786be6a8 100644 (file)
--- a/Rakefile
+++ b/Rakefile
@@ -1,7 +1,7 @@
-SVGJS_VERSION = '0.19'
+SVGJS_VERSION = '0.20'
 
 # all available modules in the correct loading order
-MODULES = %w[ svg regex default color viewbox bbox rbox element container fx event group arrange defs mask clip pattern gradient doc shape rect ellipse line poly path plotable image text nested sugar ]
+MODULES = %w[ svg regex default color number viewbox bbox rbox element container fx event group arrange defs mask clip pattern gradient doc shape rect ellipse line poly path plotable image text nested sugar ]
 
 # how many bytes in a "kilobyte"
 KILO = 1024
index cac039ee3164925a68da63c288e8bc92184c35c8..a046985b8babc45db960de1f85f454d10763da77 100644 (file)
@@ -1,4 +1,4 @@
-/* svg.js v0.19 - svg regex default color viewbox bbox rbox element container fx event group arrange defs mask clip pattern gradient doc shape rect ellipse line poly path plotable image text nested sugar - svgjs.com/license */
+/* svg.js v0.20 - svg regex default color number viewbox bbox rbox element container fx event group arrange defs mask clip pattern gradient doc shape rect ellipse line poly path plotable image text nested sugar - svgjs.com/license */
 ;(function() {
 
   this.SVG = function(element) {
@@ -79,7 +79,7 @@
     
     /* parse rgb value */
   , rgb:          /rgb\((\d+),(\d+),(\d+)\)/
-    
+  
     /* test hex value */
   , isHex:        /^#[a-f0-9]{3,6}$/i
     
@@ -97,6 +97,9 @@
     
     /* test for numeric string */
   , isNumber:     /^-?[\d\.]+$/
+  
+    /* test for percent value */
+  , isPercent:    /^-?[\d\.]+%$/
     
   }
 
       'fill-opacity':   1
     , 'stroke-opacity': 1
     , 'stroke-width':   0
-    , fill:       '#000'
-    , stroke:     '#000'
-    , opacity:    1
+    , fill:             '#000'
+    , stroke:           '#000'
+    , opacity:          1
       /* position */
-    , x:          0
-    , y:          0
-    , cx:         0
-    , cy:         0
-      /* size */
-    , width:      0
-    , height:     0
-      /* radius */
-    , r:          0
-    , rx:         0
-    , ry:         0
-      /* gradient */
-    , offset:     0
+    , x:                0
+    , y:                0
+    , cx:               0
+    , cy:               0
+      /* size */  
+    , width:            0
+    , height:           0
+      /* radius */  
+    , r:                0
+    , rx:               0
+    , ry:               0
+      /* gradient */  
+    , offset:           0
+    , 'stop-opacity':   1
+    , 'stop-color':     '#000'
     }
     
     // Default transformation values
     return color && typeof color.r == 'number'
   }
 
+  SVG.Number = function(value) {
+  
+    /* initialize defaults */
+    this.value = 0
+    this.unit = ''
+  
+    /* parse value */
+    switch(typeof value) {
+      case 'number':
+        this.value = value
+      break
+      case 'string':
+        var match = value.match(SVG.regex.unit)
+  
+        /* make valu numeric */
+        this.value = parseFloat(match[1])
+    
+        /* normalize percent value */
+        if (match[2] == '%')
+          this.value /= 100
+    
+        /* store unit */
+        this.unit = match[2]
+  
+      break
+      default:
+        if (value instanceof SVG.Number) {
+          this.value = value.value
+          this.unit  = value.unit
+        }
+      break
+    }
+  }
+  
+  SVG.extend(SVG.Number, {
+    // Stringalize
+    toString: function() {
+      return (this.unit == '%' ? ~~(this.value * 100) : this.value) + this.unit
+    }
+  , // Convert to primitive
+    valueOf: function() {
+      return this.value
+    }
+    // Convert to different unit
+  , to: function(unit) {
+      if (typeof unit === 'string')
+        this.unit = unit
+  
+      return this
+    }
+    // Add number
+  , plus: function(number) {
+      this.value = this + new SVG.Number(number)
+  
+      return this
+    }
+    // Subtract number
+  , minus: function(number) {
+      return this.plus(-new SVG.Number(number))
+    }
+    // Multiply number
+  , times: function(number) {
+      this.value = this * new SVG.Number(number)
+  
+      return this
+    }
+    // Divide number
+  , divide: function(number) {
+      this.value = this / new SVG.Number(number)
+  
+      return this
+    }
+  
+  })
+
   SVG.ViewBox = function(element) {
     var x, y, width, height
       , box  = element.bbox()
   SVG.extend(SVG.Element, {
     // Move over x-axis
     x: function(x) {
-      if (x) x /= this.trans.scaleX
+      if (x) {
+        x = new SVG.Number(x)
+        x.value /= this.trans.scaleX
+      }
       return this.attr('x', x)
     }
     // Move over y-axis
   , y: function(y) {
-      if (y) y /= this.trans.scaleY
+      if (y) {
+        y = new SVG.Number(y)
+        y.value /= this.trans.scaleY
+      }
       return this.attr('y', y)
     }
     // Move by center over x-axis
     // Set element size to given width and height
   , size: function(width, height) { 
       return this.attr({
-        width:  width
-      , height: height
+        width:  new SVG.Number(width)
+      , height: new SVG.Number(height)
       })
     }
     // Clone element
       
       return this
     }
+    // Add animateable gradient update
+  , update: function(o) {
+      if (this.target instanceof SVG.Stop) {
+        if (o.opacity != null) this.attr('stop-opacity', o.opacity)
+        if (o.color   != null) this.attr('stop-color', o.color)
+        if (o.offset  != null) this.attr('offset', new SVG.Number(o.offset))
+      }
+  
+      return this
+    }
     // Add callback for each keyframe
   , during: function(during) {
       this._during = during
       
       /* unit recalculation */
       SVG.regex.unit.test(o.to) ?
-        this._unit(o, pos) :
+        new SVG.Number(o.to)
+          .minus(new SVG.Number(o.from))
+          .times(pos)
+          .plus(new SVG.Number(o.from)) :
       
       /* color recalculation */
       o.to && (o.to.r || SVG.Color.test(o.to)) ?
       /* for all other values wait until pos has reached 1 to return the final value */
       pos < 1 ? o.from : o.to
     }
-    // Private: tween unit
-  , _unit: function(o, pos) {
-      var match, from
-      
-      /* convert FROM unit */
-      match = SVG.regex.unit.exec(o.from.toString())
-      from = parseFloat(match ? match[1] : 0)
-      
-      /* convert TO unit */
-      match = SVG.regex.unit.exec(o.to)
-      
-      return (from + (parseFloat(match[1]) - from) * pos) + match[2]
-    }
     // Private: tween color
   , _color: function(o, pos) {
       var from, to
     // From position
     from: function(x, y) {
       return this.type == 'radial' ?
-        this.attr({ fx: x + '%', fy: y + '%' }) :
-        this.attr({ x1: x + '%', y1: y + '%' })
+        this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) :
+        this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) })
     }
     // To position
   , to: function(x, y) {
       return this.type == 'radial' ?
-        this.attr({ cx: x + '%', cy: y + '%' }) :
-        this.attr({ x2: x + '%', y2: y + '%' })
+        this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) :
+        this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) })
     }
     // Radius for radial gradient
-  , radius: function(radius) {
+  , radius: function(r) {
       return this.type == 'radial' ?
-        this.attr({ r: radius + '%' }) :
+        this.attr({ r: new SVG.Number(r) }) :
         this
     }
     // Add a color stop
     // Update gradient
   , update: function(block) {
       /* remove all stops */
-      while (this.node.hasChildNodes())
-        this.node.removeChild(this.node.lastChild)
+      this.clear()
       
       /* invoke passed block */
       block(this)
   , fill: function() {
       return 'url(#' + this.attr('id') + ')'
     }
+    // Get a stop at the given index
+  , get: function(i) {
+      return this.children()[i]
+    }
     
   })
   
   }
   
   // Inherit from SVG.Element
-  SVG.Stop.prototype = new SVG.Element()
+  SVG.Stop.prototype = new SVG.Element
   
   //
   SVG.extend(SVG.Stop, {
     // add color stops
     update: function(o) {
-      var index
-        , attr = ['opacity', 'color']
-      
-      /* build style attribute */
-      for (index = attr.length - 1; index >= 0; index--)
-        if (o[attr[index]] != null)
-          this.style('stop-' + attr[index], o[attr[index]])
-      
       /* set attributes */
-      return this.attr('offset', (o.offset != null ? o.offset : this.attr('offset')) + '%')
+      if (o.opacity != null) this.attr('stop-opacity', o.opacity)
+      if (o.color   != null) this.attr('stop-color', o.color)
+      if (o.offset  != null) this.attr('offset', new SVG.Number(o.offset))
+  
+      return this
     }
     
   })
     }
     // Move by center over x-axis
   , cx: function(x) {
-      return x == null ? this.attr('cx') : this.attr('cx', x / this.trans.scaleX)
+      return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX))
     }
     // Move by center over y-axis
   , cy: function(y) {
-      return y == null ? this.attr('cy') : this.attr('cy', y / this.trans.scaleY)
+      return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY))
     }
     // Custom size function
   , size: function(width, height) {
       return this.attr({
-        rx: width / 2,
-        ry: height / 2
+        rx: new SVG.Number(width).divide(2)
+      , ry: new SVG.Number(height).divide(2)
       })
     }
     
     }
     // Create an ellipse
   , ellipse: function(width, height) {
-      return this.put(new SVG.Ellipse().size(width, height).move(0, 0))
+      return this.put(new SVG.Ellipse).size(width, height).move(0, 0)
     }
     
   })
index d4ad94a0b3c625e8b725300aebb0fe7891344d83..f41495bbe3d44b529fd46687467f9e19c0f1b4fa 100644 (file)
@@ -1 +1 @@
-(function(){if(this.SVG=function(t){return SVG.supported?new SVG.Doc(t):void 0},this.svg=function(t){return console.warn("WARNING: svg() is deprecated, please use SVG() instead."),SVG(t)},SVG.ns="http://www.w3.org/2000/svg",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i]},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^([\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/},SVG.defaults={matrix:"1,0,0,1,0,0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,fill:"#000",stroke:"#000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r},SVG.ViewBox=function(t){var e,i,n,s,r=t.bbox(),o=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);this.x=r.x,this.y=r.y,this.width=t.node.offsetWidth||t.attr("width"),this.height=t.node.offsetHeight||t.attr("height"),o&&(e=parseFloat(o[0]),i=parseFloat(o[1]),n=parseFloat(o[2]),s=parseFloat(o[3]),this.zoom=this.width/this.height>n/s?this.height/s:this.width/n,this.x=e,this.y=i,this.width=n,this.height=s),this.zoom=this.zoom||1},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.Element=function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},SVG.extend(SVG.Element,{x:function(t){return t&&(t/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){return this.attr({width:t,height:e})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return this._isStyle(t)?"text"==t?this.content:"leading"==t&&this.leading?this.leading():this.style(t):(e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e);if("style"==t)return this.style(e);if("function"==typeof e.fill&&(e=e.fill()),"x"==t&&Array.isArray(this.lines))for(i=this.lines.length-1;i>=0;i--)this.lines[i].attr(t,e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),(SVG.Color.test(e)||SVG.Color.isRgb(e))&&(e=new SVG.Color(e).toHex()),null!=i?this.node.setAttributeNS(i,t,e):this.node.setAttribute(t,e),this._isStyle(t)&&("text"==t?this.text(e):"leading"==t&&this.leading?this.leading(e):this.style(t,e),this.rebuild&&this.rebuild(t,e))}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(2>arguments.length)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+","+this.trans.b+","+this.trans.c+","+this.trans.d+","+this.trans.e+","+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+","+(t.cx||this.bbox().cx)+","+(t.cy||this.bbox().cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+","+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+t.x/t.scaleX+","+t.y/t.scaleY+")"),this._offset&&i.push("translate("+-this._offset.x+","+-this._offset.y+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(2>arguments.length)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;t.length>i;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},data:function(t,e,i){if(2>arguments.length)try{return JSON.parse(this.attr("data-"+t))}catch(n){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:i===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&i.x+i.width>t&&i.y+i.height>e},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_isStyle:function(t){return"string"==typeof t?SVG.regex.test(t,"isStyle"):!1},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t}}),SVG.Container=function(t){this.constructor.call(this,t)},SVG.Container.prototype=new SVG.Element,SVG.extend(SVG.Container,{children:function(){return this._children||(this._children=[])},add:function(t,e){if(!this.has(t)){if(e=null==e?this.children().length:e,t.parent){var i=t.parent.children().indexOf(t);t.parent.children().splice(i,1)}this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this}return this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.children().indexOf(t)>=0},each:function(t,e){var i,n,s=this.children();for(i=0,n=s.length;n>i;i++)s[i]instanceof SVG.Shape&&t.apply(s[i],[i,s]),e&&s[i]instanceof SVG.Container&&s[i].each(t,e);return this},removeElement:function(t){var e=this.children().indexOf(t);return this.children().splice(e,1),this.node.removeChild(t.node),t.parent=null,this},defs:function(){return this._defs||(this._defs=this.put(new SVG.Defs,0))},level:function(){return this.removeElement(this.defs()).put(this.defs(),0)},first:function(){return this.children()[0]instanceof SVG.Defs?this.children()[1]:this.children()[0]},last:function(){return this.children()[this.children().length-1]},viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t.join(" ")))},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&(this._defs.remove(),delete this._defs),this}}),SVG.FX=function(t){this.target=t},SVG.extend(SVG.FX,{animate:function(t,e,i){var n,s,r,o,h=this.target,a=this;return"object"==typeof t&&(i=t.delay,e=t.ease,t=t.duration),t=null==t?1e3:t,e=e||"<>",a.to=function(t){var i;if(t=0>t?0:t>1?1:t,null==n){n=[];for(o in a.attrs)n.push(o)}if(null==s){s=[];for(o in a.trans)s.push(o)}if(null==r){r=[];for(o in a.styles)r.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,a._x?h.x(a._at(a._x,t)):a._cx&&h.cx(a._at(a._cx,t)),a._y?h.y(a._at(a._y,t)):a._cy&&h.cy(a._at(a._cy,t)),a._size&&h.size(a._at(a._size.width,t),a._at(a._size.height,t)),a._viewbox&&h.viewbox(a._at(a._viewbox.x,t),a._at(a._viewbox.y,t),a._at(a._viewbox.width,t),a._at(a._viewbox.height,t)),i=n.length-1;i>=0;i--)h.attr(n[i],a._at(a.attrs[n[i]],t));for(i=s.length-1;i>=0;i--)h.transform(s[i],a._at(a.trans[s[i]],t));for(i=r.length-1;i>=0;i--)h.style(r[i],a._at(a.styles[r[i]],t));a._during&&a._during.call(h,t,function(e,i){return a._at({from:e,to:i},t)})},"number"==typeof t&&(this.timeout=setTimeout(function(){var e=1e3/60,i=(new Date).getTime(),n=i+t;a.interval=setInterval(function(){var e=(new Date).getTime(),s=e>n?1:(e-i)/t;a.to(s),e>n&&(clearInterval(a.interval),a._after?a._after.apply(h,[a]):a.stop())},t>e?e:t)},i||0)),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else this.attrs[t]={from:this.target.attr(t),to:e};return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var s=this.target.viewbox();this._viewbox={x:{from:s.x,to:t},y:{from:s.y,to:e},width:{from:s.width,to:i},height:{from:s.height,to:n}}}return this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),this.attrs={},this.trans={},this.styles={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._after,delete this._during,delete this._viewbox,this},_at:function(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:SVG.regex.unit.test(t.to)?this._unit(t,e):t.to&&(t.to.r||SVG.Color.test(t.to))?this._color(t,e):1>e?t.from:t.to},_unit:function(t,e){var i,n;return i=SVG.regex.unit.exec(t.from.toString()),n=parseFloat(i?i[1]:0),i=SVG.regex.unit.exec(t.to),n+(parseFloat(i[1])-n)*e+i[2]},_color:function(t,e){var i,n;return e=0>e?0:e>1?1:e,i=new SVG.Color(t.from),n=new SVG.Color(t.to),new SVG.Color({r:~~(i.r+(n.r-i.r)*e),g:~~(i.g+(n.g-i.g)*e),b:~~(i.b+(n.b-i.b)*e)}).toHex()}}),SVG.extend(SVG.Element,{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchend","touchmove","touchcancel"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.G=function(){this.constructor.call(this,SVG.create("g"))},SVG.G.prototype=new SVG.Container,SVG.extend(SVG.G,{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)},defs:function(){return this.doc().defs()}}),SVG.extend(SVG.Container,{group:function(){return this.put(new SVG.G)}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){return this.siblings().indexOf(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){return this.parent.removeElement(this).put(this,this.position()+1)},backward:function(){this.parent.level();var t=this.position();return t>1&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.parent.level(),this.position()>1&&this.parent.removeElement(this).add(this,0),this}}),SVG.Defs=function(){this.constructor.call(this,SVG.create("defs"))},SVG.Defs.prototype=new SVG.Container,SVG.Mask=function(){this.constructor.call(this,SVG.create("mask"))},SVG.Mask.prototype=new SVG.Container,SVG.extend(SVG.Element,{maskWith:function(t){return this.mask=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.attr("mask","url(#"+this.mask.attr("id")+")")}}),SVG.extend(SVG.Container,{mask:function(){return this.defs().put(new SVG.Mask)}}),SVG.Clip=function(){this.constructor.call(this,SVG.create("clipPath"))},SVG.Clip.prototype=new SVG.Container,SVG.extend(SVG.Element,{clipWith:function(t){return this.clip=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.attr("clip-path","url(#"+this.clip.attr("id")+")")}}),SVG.extend(SVG.Container,{clip:function(){return this.defs().put(new SVG.Clip)}}),SVG.Pattern=function(){this.constructor.call(this,SVG.create("pattern"))},SVG.Pattern.prototype=new SVG.Container,SVG.extend(SVG.Pattern,{fill:function(){return"url(#"+this.attr("id")+")"}}),SVG.extend(SVG.Defs,{pattern:function(t,e,i){var n=this.put(new SVG.Pattern);return i(n),n.attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),SVG.extend(SVG.Container,{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}),SVG.Gradient=function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},SVG.Gradient.prototype=new SVG.Container,SVG.extend(SVG.Gradient,{from:function(t,e){return"radial"==this.type?this.attr({fx:t+"%",fy:e+"%"}):this.attr({x1:t+"%",y1:e+"%"})},to:function(t,e){return"radial"==this.type?this.attr({cx:t+"%",cy:e+"%"}):this.attr({x2:t+"%",y2:e+"%"})},radius:function(t){return"radial"==this.type?this.attr({r:t+"%"}):this},at:function(t){return this.put(new SVG.Stop(t))},update:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return t(this),this},fill:function(){return"url(#"+this.attr("id")+")"}}),SVG.extend(SVG.Defs,{gradient:function(t,e){var i=this.put(new SVG.Gradient(t));return e(i),i}}),SVG.extend(SVG.Container,{gradient:function(t,e){return this.defs().gradient(t,e)}}),SVG.Stop=function(t){this.constructor.call(this,SVG.create("stop")),this.update(t)},SVG.Stop.prototype=new SVG.Element,SVG.extend(SVG.Stop,{update:function(t){var e,i=["opacity","color"];for(e=i.length-1;e>=0;e--)null!=t[i[e]]&&this.style("stop-"+i[e],t[i[e]]);return this.attr("offset",(null!=t.offset?t.offset:this.attr("offset"))+"%")}}),SVG.Doc=function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xlink",SVG.xlink,SVG.ns).defs(),"svg"!=this.parent.nodeName&&this.stage()},SVG.Doc.prototype=new SVG.Container,SVG.extend(SVG.Doc,{stage:function(){var t,e=this,i=document.createElement("div");return i.style.cssText="position:relative;height:100%;",e.parent.appendChild(i),i.appendChild(e.node),t=function(){"complete"===document.readyState?(e.style("position:absolute;"),setTimeout(function(){e.style("position:relative;"),e.parent.removeChild(e.node.parentNode),e.node.parentNode.removeChild(e.node),e.parent.appendChild(e.node),e.fixSubPixelOffset(),SVG.on(window,"resize",function(){e.fixSubPixelOffset()})},5)):setTimeout(t,10)},t(),this},fixSubPixelOffset:function(){var t=this.node.getScreenCTM();this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}}),SVG.Shape=function(t){this.constructor.call(this,t)},SVG.Shape.prototype=new SVG.Element,SVG.Rect=function(){this.constructor.call(this,SVG.create("rect"))},SVG.Rect.prototype=new SVG.Shape,SVG.extend(SVG.Container,{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}),SVG.Ellipse=function(){this.constructor.call(this,SVG.create("ellipse"))},SVG.Ellipse.prototype=new SVG.Shape,SVG.extend(SVG.Ellipse,{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t/this.trans.scaleX)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t/this.trans.scaleY)},size:function(t,e){return this.attr({rx:t/2,ry:e/2})}}),SVG.extend(SVG.Container,{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put((new SVG.Ellipse).size(t,e).move(0,0))}}),SVG.Line=function(){this.constructor.call(this,SVG.create("line"))},SVG.Line.prototype=new SVG.Shape,SVG.extend(SVG.Line,{x:function(t){var e=this.bbox();return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},size:function(t,e){var i=this.bbox();return this.attr(this.attr("x1")<this.attr("x2")?"x2":"x1",i.x+t).attr(this.attr("y1")<this.attr("y2")?"y2":"y1",i.y+e)},plot:function(t,e,i,n){return this.attr({x1:t,y1:e,x2:i,y2:n})}}),SVG.extend(SVG.Container,{line:function(t,e,i,n){return this.put((new SVG.Line).plot(t,e,i,n))}}),SVG.Polyline=function(t){this.constructor.call(this,SVG.create("polyline")),this.unbiased=t},SVG.Polyline.prototype=new SVG.Shape,SVG.Polygon=function(t){this.constructor.call(this,SVG.create("polygon")),this.unbiased=t},SVG.Polygon.prototype=new SVG.Shape,SVG.extend(SVG.Polyline,SVG.Polygon,{_plot:function(t){if(Array.isArray(t)){var e,i,n=[];for(e=0,i=t.length;i>e;e++)n.push(t[e].join(","));t=n.length>0?n.join(" "):"0,0"}return this.attr("points",t||"0,0")}}),SVG.extend(SVG.Container,{polyline:function(t,e){return this.put(new SVG.Polyline(e)).plot(t)},polygon:function(t,e){return this.put(new SVG.Polygon(e)).plot(t)}}),SVG.Path=function(t){this.constructor.call(this,SVG.create("path")),this.unbiased=t},SVG.Path.prototype=new SVG.Shape,SVG.extend(SVG.Path,{_plot:function(t){return this.attr("d",t||"M0,0")}}),SVG.extend(SVG.Container,{path:function(t,e){return this.put(new SVG.Path(e)).plot(t)}}),SVG.extend(SVG.Polyline,SVG.Polygon,SVG.Path,{x:function(t){return null==t?this.bbox().x:this.transform("x",t)},y:function(t){return null==t?this.bbox().y:this.transform("y",t)},size:function(t,e){var i=t/this._offset.width;return this.transform({scaleX:i,scaleY:null!=e?e/this._offset.height:i})},plot:function(t){var e=this.trans.scaleX,i=this.trans.scaleY;return this._plot(t),this._offset=this.transform({scaleX:1,scaleY:1}).bbox(),this.unbiased?this._offset.x=this._offset.y=0:(this._offset.x-=this.trans.x,this._offset.y-=this.trans.y),this.transform({scaleX:e,scaleY:i})}}),SVG.Image=function(){this.constructor.call(this,SVG.create("image"))},SVG.Image.prototype=new SVG.Shape,SVG.extend(SVG.Image,{load:function(t){return t?this.attr("xlink:href",this.src=t,SVG.xlink):this}}),SVG.extend(SVG.Container,{image:function(t,e,i){return e=null!=e?e:100,this.put((new SVG.Image).load(t).size(e,null!=i?i:e))}});var t="size family weight stretch variant style".split(" ");SVG.Text=function(){this.constructor.call(this,SVG.create("text")),this.styles={"font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},this._leading=1.2,this._base=.276666666},SVG.Text.prototype=new SVG.Shape,SVG.extend(SVG.Text,{x:function(t,e){return null==t?e?this.attr("x"):this.bbox().x:(e||(e=this.style("text-anchor"),t="start"==e?t:"end"==e?t+this.bbox().width:t+this.bbox().width/2),this.attr("x",t))},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t,e){return null==t?this.bbox().cy:this.y(e?t:t-this.bbox().height/2)},move:function(t,e,i){return this.x(t,i).y(e)},center:function(t,e,i){return this.cx(t,i).cy(e,i)},text:function(t){if(null==t)return this.content;this.clear(),this.content=SVG.regex.isBlank.test(t)?"text":t;var e,i,n=t.split("\n");for(e=0,i=n.length;i>e;e++)this.tspan(n[e]);return this.attr("textLength",1).attr("textLength",null)},tspan:function(t){var e=(new SVG.TSpan).text(t);return this.node.appendChild(e.node),this.lines.push(e),e.attr("style",this.style())},size:function(t){return this.attr("font-size",t)},leading:function(t){return null==t?this._leading:(this._leading=t,this.rebuild("leading",t))},rebuild:function(){var t,e,i=this.styles["font-size"];for(t=0,e=this.lines.length;e>t;t++)this.lines[t].attr({dy:i*this._leading-(0==t?i*this._base:0),x:this.attr("x")||0,style:this.style()});return this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.lines=[],this}}),SVG.extend(SVG.Container,{text:function(t){return this.put((new SVG.Text).text(t))}}),SVG.TSpan=function(){this.constructor.call(this,SVG.create("tspan"))},SVG.TSpan.prototype=new SVG.Shape,SVG.extend(SVG.TSpan,{text:function(t){return this.node.appendChild(document.createTextNode(t)),this}}),SVG.Nested=function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},SVG.Nested.prototype=new SVG.Container,SVG.extend(SVG.Container,{nested:function(){return this.put(new SVG.Nested)}}),SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var e=function(t,e){return"color"==e?t:t+"-"+e};["fill","stroke"].forEach(function(t){var i={};i[t]=function(i){if("string"==typeof i||SVG.Color.isRgb(i))this.attr(t,i);else for(index=SVG["_"+t].length-1;index>=0;index--)null!=i[SVG["_"+t][index]]&&this.attr(e(t,SVG["_"+t][index]),i[SVG["_"+t][index]]);return this},SVG.extend(SVG.Shape,SVG.FX,i)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.Text&&SVG.extend(SVG.Text,SVG.FX,{font:function(e){for(var i in e)"anchor"==i?this.attr("text-anchor",e[i]):t.indexOf(i)>-1?this.attr("font-"+i,e[i]):this.attr(i,e[i]);return this}})}).call(this);
\ No newline at end of file
+!function(){if(this.SVG=function(t){return SVG.supported?new SVG.Doc(t):void 0},this.svg=function(t){return console.warn("WARNING: svg() is deprecated, please use SVG() instead."),SVG(t)},SVG.ns="http://www.w3.org/2000/svg",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i]},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^([\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/},SVG.defaults={matrix:"1,0,0,1,0,0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,fill:"#000",stroke:"#000",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":"#000"},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r},SVG.Number=function(t){switch(this.value=0,this.unit="",typeof t){case"number":this.value=t;break;case"string":var e=t.match(SVG.regex.unit);this.value=parseFloat(e[1]),"%"==e[2]&&(this.value/=100),this.unit=e[2];break;default:t instanceof SVG.Number&&(this.value=t.value,this.unit=t.unit)}},SVG.extend(SVG.Number,{toString:function(){return("%"==this.unit?~~(100*this.value):this.value)+this.unit},valueOf:function(){return this.value},to:function(t){return"string"==typeof t&&(this.unit=t),this},plus:function(t){return this.value=this+new SVG.Number(t),this},minus:function(t){return this.plus(-new SVG.Number(t))},times:function(t){return this.value=this*new SVG.Number(t),this},divide:function(t){return this.value=this/new SVG.Number(t),this}}),SVG.ViewBox=function(t){var e,i,n,s,r=t.bbox(),o=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);this.x=r.x,this.y=r.y,this.width=t.node.offsetWidth||t.attr("width"),this.height=t.node.offsetHeight||t.attr("height"),o&&(e=parseFloat(o[0]),i=parseFloat(o[1]),n=parseFloat(o[2]),s=parseFloat(o[3]),this.zoom=this.width/this.height>n/s?this.height/s:this.width/n,this.x=e,this.y=i,this.width=n,this.height=s),this.zoom=this.zoom||1},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.Element=function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},SVG.extend(SVG.Element,{x:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){return this.attr({width:new SVG.Number(t),height:new SVG.Number(e)})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return this._isStyle(t)?"text"==t?this.content:"leading"==t&&this.leading?this.leading():this.style(t):(e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e);if("style"==t)return this.style(e);if("function"==typeof e.fill&&(e=e.fill()),"x"==t&&Array.isArray(this.lines))for(i=this.lines.length-1;i>=0;i--)this.lines[i].attr(t,e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),(SVG.Color.test(e)||SVG.Color.isRgb(e))&&(e=new SVG.Color(e).toHex()),null!=i?this.node.setAttributeNS(i,t,e):this.node.setAttribute(t,e),this._isStyle(t)&&("text"==t?this.text(e):"leading"==t&&this.leading?this.leading(e):this.style(t,e),this.rebuild&&this.rebuild(t,e))}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(arguments.length<2)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+","+this.trans.b+","+this.trans.c+","+this.trans.d+","+this.trans.e+","+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+","+(t.cx||this.bbox().cx)+","+(t.cy||this.bbox().cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+","+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+t.x/t.scaleX+","+t.y/t.scaleY+")"),this._offset&&i.push("translate("+-this._offset.x+","+-this._offset.y+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(arguments.length<2)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;i<t.length;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},data:function(t,e,i){if(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},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&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")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_isStyle:function(t){return"string"==typeof t?SVG.regex.test(t,"isStyle"):!1},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t}}),SVG.Container=function(t){this.constructor.call(this,t)},SVG.Container.prototype=new SVG.Element,SVG.extend(SVG.Container,{children:function(){return this._children||(this._children=[])},add:function(t,e){if(!this.has(t)){if(e=null==e?this.children().length:e,t.parent){var i=t.parent.children().indexOf(t);t.parent.children().splice(i,1)}this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this}return this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.children().indexOf(t)>=0},each:function(t,e){var i,n,s=this.children();for(i=0,n=s.length;n>i;i++)s[i]instanceof SVG.Shape&&t.apply(s[i],[i,s]),e&&s[i]instanceof SVG.Container&&s[i].each(t,e);return this},removeElement:function(t){var e=this.children().indexOf(t);return this.children().splice(e,1),this.node.removeChild(t.node),t.parent=null,this},defs:function(){return this._defs||(this._defs=this.put(new SVG.Defs,0))},level:function(){return this.removeElement(this.defs()).put(this.defs(),0)},first:function(){return this.children()[0]instanceof SVG.Defs?this.children()[1]:this.children()[0]},last:function(){return this.children()[this.children().length-1]},viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t.join(" ")))},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&(this._defs.remove(),delete this._defs),this}}),SVG.FX=function(t){this.target=t},SVG.extend(SVG.FX,{animate:function(t,e,i){var n,s,r,o,h=this.target,a=this;return"object"==typeof t&&(i=t.delay,e=t.ease,t=t.duration),t=null==t?1e3:t,e=e||"<>",a.to=function(t){var i;if(t=0>t?0:t>1?1:t,null==n){n=[];for(o in a.attrs)n.push(o)}if(null==s){s=[];for(o in a.trans)s.push(o)}if(null==r){r=[];for(o in a.styles)r.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,a._x?h.x(a._at(a._x,t)):a._cx&&h.cx(a._at(a._cx,t)),a._y?h.y(a._at(a._y,t)):a._cy&&h.cy(a._at(a._cy,t)),a._size&&h.size(a._at(a._size.width,t),a._at(a._size.height,t)),a._viewbox&&h.viewbox(a._at(a._viewbox.x,t),a._at(a._viewbox.y,t),a._at(a._viewbox.width,t),a._at(a._viewbox.height,t)),i=n.length-1;i>=0;i--)h.attr(n[i],a._at(a.attrs[n[i]],t));for(i=s.length-1;i>=0;i--)h.transform(s[i],a._at(a.trans[s[i]],t));for(i=r.length-1;i>=0;i--)h.style(r[i],a._at(a.styles[r[i]],t));a._during&&a._during.call(h,t,function(e,i){return a._at({from:e,to:i},t)})},"number"==typeof t&&(this.timeout=setTimeout(function(){var e=1e3/60,i=(new Date).getTime(),n=i+t;a.interval=setInterval(function(){var e=(new Date).getTime(),s=e>n?1:(e-i)/t;a.to(s),e>n&&(clearInterval(a.interval),a._after?a._after.apply(h,[a]):a.stop())},t>e?e:t)},i||0)),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else this.attrs[t]={from:this.target.attr(t),to:e};return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var s=this.target.viewbox();this._viewbox={x:{from:s.x,to:t},y:{from:s.y,to:e},width:{from:s.width,to:i},height:{from:s.height,to:n}}}return this},update:function(t){return this.target instanceof SVG.Stop&&(null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),this.attrs={},this.trans={},this.styles={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._after,delete this._during,delete this._viewbox,this},_at:function(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:SVG.regex.unit.test(t.to)?new SVG.Number(t.to).minus(new SVG.Number(t.from)).times(e).plus(new SVG.Number(t.from)):t.to&&(t.to.r||SVG.Color.test(t.to))?this._color(t,e):1>e?t.from:t.to},_color:function(t,e){var i,n;return e=0>e?0:e>1?1:e,i=new SVG.Color(t.from),n=new SVG.Color(t.to),new SVG.Color({r:~~(i.r+(n.r-i.r)*e),g:~~(i.g+(n.g-i.g)*e),b:~~(i.b+(n.b-i.b)*e)}).toHex()}}),SVG.extend(SVG.Element,{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchend","touchmove","touchcancel"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.G=function(){this.constructor.call(this,SVG.create("g"))},SVG.G.prototype=new SVG.Container,SVG.extend(SVG.G,{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)},defs:function(){return this.doc().defs()}}),SVG.extend(SVG.Container,{group:function(){return this.put(new SVG.G)}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){return this.siblings().indexOf(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){return this.parent.removeElement(this).put(this,this.position()+1)},backward:function(){this.parent.level();var t=this.position();return t>1&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.parent.level(),this.position()>1&&this.parent.removeElement(this).add(this,0),this}}),SVG.Defs=function(){this.constructor.call(this,SVG.create("defs"))},SVG.Defs.prototype=new SVG.Container,SVG.Mask=function(){this.constructor.call(this,SVG.create("mask"))},SVG.Mask.prototype=new SVG.Container,SVG.extend(SVG.Element,{maskWith:function(t){return this.mask=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.attr("mask","url(#"+this.mask.attr("id")+")")}}),SVG.extend(SVG.Container,{mask:function(){return this.defs().put(new SVG.Mask)}}),SVG.Clip=function(){this.constructor.call(this,SVG.create("clipPath"))},SVG.Clip.prototype=new SVG.Container,SVG.extend(SVG.Element,{clipWith:function(t){return this.clip=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.attr("clip-path","url(#"+this.clip.attr("id")+")")}}),SVG.extend(SVG.Container,{clip:function(){return this.defs().put(new SVG.Clip)}}),SVG.Pattern=function(){this.constructor.call(this,SVG.create("pattern"))},SVG.Pattern.prototype=new SVG.Container,SVG.extend(SVG.Pattern,{fill:function(){return"url(#"+this.attr("id")+")"}}),SVG.extend(SVG.Defs,{pattern:function(t,e,i){var n=this.put(new SVG.Pattern);return i(n),n.attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),SVG.extend(SVG.Container,{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}),SVG.Gradient=function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},SVG.Gradient.prototype=new SVG.Container,SVG.extend(SVG.Gradient,{from:function(t,e){return"radial"==this.type?this.attr({fx:new SVG.Number(t),fy:new SVG.Number(e)}):this.attr({x1:new SVG.Number(t),y1:new SVG.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new SVG.Number(t),cy:new SVG.Number(e)}):this.attr({x2:new SVG.Number(t),y2:new SVG.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new SVG.Number(t)}):this},at:function(t){return this.put(new SVG.Stop(t))},update:function(t){return this.clear(),t(this),this},fill:function(){return"url(#"+this.attr("id")+")"},get:function(t){return this.children()[t]}}),SVG.extend(SVG.Defs,{gradient:function(t,e){var i=this.put(new SVG.Gradient(t));return e(i),i}}),SVG.extend(SVG.Container,{gradient:function(t,e){return this.defs().gradient(t,e)}}),SVG.Stop=function(t){this.constructor.call(this,SVG.create("stop")),this.update(t)},SVG.Stop.prototype=new SVG.Element,SVG.extend(SVG.Stop,{update:function(t){return null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset)),this}}),SVG.Doc=function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xlink",SVG.xlink,SVG.ns).defs(),"svg"!=this.parent.nodeName&&this.stage()},SVG.Doc.prototype=new SVG.Container,SVG.extend(SVG.Doc,{stage:function(){var t,e=this,i=document.createElement("div");return i.style.cssText="position:relative;height:100%;",e.parent.appendChild(i),i.appendChild(e.node),t=function(){"complete"===document.readyState?(e.style("position:absolute;"),setTimeout(function(){e.style("position:relative;"),e.parent.removeChild(e.node.parentNode),e.node.parentNode.removeChild(e.node),e.parent.appendChild(e.node),e.fixSubPixelOffset(),SVG.on(window,"resize",function(){e.fixSubPixelOffset()})},5)):setTimeout(t,10)},t(),this},fixSubPixelOffset:function(){var t=this.node.getScreenCTM();this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}}),SVG.Shape=function(t){this.constructor.call(this,t)},SVG.Shape.prototype=new SVG.Element,SVG.Rect=function(){this.constructor.call(this,SVG.create("rect"))},SVG.Rect.prototype=new SVG.Shape,SVG.extend(SVG.Container,{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}),SVG.Ellipse=function(){this.constructor.call(this,SVG.create("ellipse"))},SVG.Ellipse.prototype=new SVG.Shape,SVG.extend(SVG.Ellipse,{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new SVG.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new SVG.Number(t).divide(this.trans.scaleY))},size:function(t,e){return this.attr({rx:new SVG.Number(t).divide(2),ry:new SVG.Number(e).divide(2)})}}),SVG.extend(SVG.Container,{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put(new SVG.Ellipse).size(t,e).move(0,0)}}),SVG.Line=function(){this.constructor.call(this,SVG.create("line"))},SVG.Line.prototype=new SVG.Shape,SVG.extend(SVG.Line,{x:function(t){var e=this.bbox();return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},size:function(t,e){var i=this.bbox();return this.attr(this.attr("x1")<this.attr("x2")?"x2":"x1",i.x+t).attr(this.attr("y1")<this.attr("y2")?"y2":"y1",i.y+e)},plot:function(t,e,i,n){return this.attr({x1:t,y1:e,x2:i,y2:n})}}),SVG.extend(SVG.Container,{line:function(t,e,i,n){return this.put((new SVG.Line).plot(t,e,i,n))}}),SVG.Polyline=function(t){this.constructor.call(this,SVG.create("polyline")),this.unbiased=t},SVG.Polyline.prototype=new SVG.Shape,SVG.Polygon=function(t){this.constructor.call(this,SVG.create("polygon")),this.unbiased=t},SVG.Polygon.prototype=new SVG.Shape,SVG.extend(SVG.Polyline,SVG.Polygon,{_plot:function(t){if(Array.isArray(t)){var e,i,n=[];for(e=0,i=t.length;i>e;e++)n.push(t[e].join(","));t=n.length>0?n.join(" "):"0,0"}return this.attr("points",t||"0,0")}}),SVG.extend(SVG.Container,{polyline:function(t,e){return this.put(new SVG.Polyline(e)).plot(t)},polygon:function(t,e){return this.put(new SVG.Polygon(e)).plot(t)}}),SVG.Path=function(t){this.constructor.call(this,SVG.create("path")),this.unbiased=t},SVG.Path.prototype=new SVG.Shape,SVG.extend(SVG.Path,{_plot:function(t){return this.attr("d",t||"M0,0")}}),SVG.extend(SVG.Container,{path:function(t,e){return this.put(new SVG.Path(e)).plot(t)}}),SVG.extend(SVG.Polyline,SVG.Polygon,SVG.Path,{x:function(t){return null==t?this.bbox().x:this.transform("x",t)},y:function(t){return null==t?this.bbox().y:this.transform("y",t)},size:function(t,e){var i=t/this._offset.width;return this.transform({scaleX:i,scaleY:null!=e?e/this._offset.height:i})},plot:function(t){var e=this.trans.scaleX,i=this.trans.scaleY;return this._plot(t),this._offset=this.transform({scaleX:1,scaleY:1}).bbox(),this.unbiased?this._offset.x=this._offset.y=0:(this._offset.x-=this.trans.x,this._offset.y-=this.trans.y),this.transform({scaleX:e,scaleY:i})}}),SVG.Image=function(){this.constructor.call(this,SVG.create("image"))},SVG.Image.prototype=new SVG.Shape,SVG.extend(SVG.Image,{load:function(t){return t?this.attr("xlink:href",this.src=t,SVG.xlink):this}}),SVG.extend(SVG.Container,{image:function(t,e,i){return e=null!=e?e:100,this.put((new SVG.Image).load(t).size(e,null!=i?i:e))}});var t="size family weight stretch variant style".split(" ");SVG.Text=function(){this.constructor.call(this,SVG.create("text")),this.styles={"font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},this._leading=1.2,this._base=.276666666},SVG.Text.prototype=new SVG.Shape,SVG.extend(SVG.Text,{x:function(t,e){return null==t?e?this.attr("x"):this.bbox().x:(e||(e=this.style("text-anchor"),t="start"==e?t:"end"==e?t+this.bbox().width:t+this.bbox().width/2),this.attr("x",t))},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t,e){return null==t?this.bbox().cy:this.y(e?t:t-this.bbox().height/2)},move:function(t,e,i){return this.x(t,i).y(e)},center:function(t,e,i){return this.cx(t,i).cy(e,i)},text:function(t){if(null==t)return this.content;this.clear(),this.content=SVG.regex.isBlank.test(t)?"text":t;var e,i,n=t.split("\n");for(e=0,i=n.length;i>e;e++)this.tspan(n[e]);return this.attr("textLength",1).attr("textLength",null)},tspan:function(t){var e=(new SVG.TSpan).text(t);return this.node.appendChild(e.node),this.lines.push(e),e.attr("style",this.style())},size:function(t){return this.attr("font-size",t)},leading:function(t){return null==t?this._leading:(this._leading=t,this.rebuild("leading",t))},rebuild:function(){var t,e,i=this.styles["font-size"];for(t=0,e=this.lines.length;e>t;t++)this.lines[t].attr({dy:i*this._leading-(0==t?i*this._base:0),x:this.attr("x")||0,style:this.style()});return this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.lines=[],this}}),SVG.extend(SVG.Container,{text:function(t){return this.put((new SVG.Text).text(t))}}),SVG.TSpan=function(){this.constructor.call(this,SVG.create("tspan"))},SVG.TSpan.prototype=new SVG.Shape,SVG.extend(SVG.TSpan,{text:function(t){return this.node.appendChild(document.createTextNode(t)),this}}),SVG.Nested=function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},SVG.Nested.prototype=new SVG.Container,SVG.extend(SVG.Container,{nested:function(){return this.put(new SVG.Nested)}}),SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var e=function(t,e){return"color"==e?t:t+"-"+e};["fill","stroke"].forEach(function(t){var i={};i[t]=function(i){if("string"==typeof i||SVG.Color.isRgb(i))this.attr(t,i);else for(index=SVG["_"+t].length-1;index>=0;index--)null!=i[SVG["_"+t][index]]&&this.attr(e(t,SVG["_"+t][index]),i[SVG["_"+t][index]]);return this},SVG.extend(SVG.Shape,SVG.FX,i)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.Text&&SVG.extend(SVG.Text,SVG.FX,{font:function(e){for(var i in e)"anchor"==i?this.attr("text-anchor",e[i]):t.indexOf(i)>-1?this.attr("font-"+i,e[i]):this.attr(i,e[i]);return this}})}.call(this);
\ No newline at end of file
index 601fd14ee15c469d6e78871adac85a677d937651..bfb80d9bf3b73c586a3ced368e6b874cd140c678 100644 (file)
@@ -6,7 +6,7 @@
 , "keywords":     ["svg", "vector", "graphics", "animation"]
 , "author":       "Wout Fierens <wout@impinc.co.uk>"
 , "main":         "dist/svg.js"
-, "version":      "v0.19"
+, "version":      "v0.20"
 , "jam": {
     "include": [
       "dist/svg.js"
index 38150a92854d03d525ad8ae0bd286b4a3d551f4e..d4cf605658939c672997dde5f2f5b020832bdfd3 100644 (file)
@@ -43,6 +43,7 @@
 <script type="text/javascript" src="spec/doc.js"></script>
 <script type="text/javascript" src="spec/gradient.js"></script>
 <script type="text/javascript" src="spec/color.js"></script>
+<script type="text/javascript" src="spec/number.js"></script>
 
 <script type="text/javascript">
   (function() {
index b19aa57716c932f6c9f0298de5b518cee01eb6fa..cd3e0e501084f7a18ae1bb0595a06ff4faa9f0d4 100644 (file)
@@ -63,7 +63,7 @@ describe('Ellipse', function() {
   })
   
   describe('center()', function() {
-    it('should set the cx and cy position', function() {
+    it('sets the cx and cy position', function() {
       ellipse.center(321,567)
       var box = ellipse.bbox()
       expect(box.cx).toBe(321)
@@ -72,7 +72,7 @@ describe('Ellipse', function() {
   })
   
   describe('size()', function() {
-    it('should define the rx and ry of the element', function() {
+    it('defines the rx and ry of the element', function() {
       ellipse.size(987,654)
       expect(ellipse.node.getAttribute('rx')).toBe((987 / 2).toString())
       expect(ellipse.node.getAttribute('ry')).toBe((654 / 2).toString())
index f07a7e813267feb4bba357b7a210803633dbdf29..8aef33e39a92c92fba9888832b41c98efd1bfb46 100644 (file)
@@ -2,21 +2,81 @@ describe('Gradient', function() {
   var rect = draw.rect(100,100)
     , gradient = draw.gradient('linear', function(stop) {
       stop.at({ offset: 0, color: '#333', opacity: 1 })
-      stop.at({ offset: 100, color: '#fff', opacity: 1 })
+      stop.at({ offset: 1, color: '#fff', opacity: 1 })
     })
   
-  it('should be an instance of SVG.Gradient', function() {
+  it('is an instance of SVG.Gradient', function() {
     expect(gradient instanceof SVG.Gradient).toBe(true)
   })
   
   describe('fill()', function() {
-    it('should return the id of the gradient wrapped in url()', function() {
+    it('returns the id of the gradient wrapped in url()', function() {
       expect(gradient.fill()).toBe('url(#' + gradient.attr('id') + ')')
     })
-    it('should be called when instance is passed as an attribute value', function() {
+    it('is called when instance is passed as an attribute value', function() {
       rect.attr('fill', gradient)
       expect(rect.attr('fill')).toBe('url(#' + gradient.attr('id') + ')')
     })
   })
+
+  describe('input values', function() {
+    var s1, s2
+
+    it('accepts floats', function() {
+      gradient = draw.gradient('linear', function(stop) {
+        s1 = stop.at({ offset: 0.12, color: '#333', opacity: 1 })
+        s2 = stop.at({ offset: 0.93, color: '#fff', opacity: 1 })
+      })
+      expect(s1.attr('offset')).toBe(0.12)
+      expect(s2.attr('offset')).toBe(0.93)
+    })
+    it('accepts string floats', function() {
+      gradient = draw.gradient('linear', function(stop) {
+        s1 = stop.at({ offset: '0.13', color: '#333', opacity: 1 })
+        s2 = stop.at({ offset: '0.92', color: '#fff', opacity: 1 })
+      })
+      expect(s1.attr('offset')).toBe(0.13)
+      expect(s2.attr('offset')).toBe(0.92)
+    })
+    it('accept percentages', function() {
+      gradient = draw.gradient('linear', function(stop) {
+        s1 = stop.at({ offset: '14%', color: '#333', opacity: 1 })
+        s2 = stop.at({ offset: '91%', color: '#fff', opacity: 1 })
+      })
+      expect(s1.attr('offset')).toBe('14%')
+      expect(s2.attr('offset')).toBe('91%')
+    })
+  })
+
+  describe('update()', function() {
+
+    it('removes all existing children first', function() {
+      gradient = draw.gradient('linear', function(stop) {
+        s1 = stop.at({ offset: 0.12, color: '#333', opacity: 1 })
+        s2 = stop.at({ offset: 0.93, color: '#fff', opacity: 1 })
+      })
+      expect(gradient.children().length).toBe(2)
+      gradient.update(function(stop) {
+        s1 = stop.at({ offset: 0.33, color: '#666', opacity: 1 })
+        s2 = stop.at({ offset: 1, color: '#000', opacity: 1 })
+      })
+      expect(gradient.children().length).toBe(2)
+    })
+
+  })
+
+  describe('get()', function() {
+
+    it('returns the stop at a given index', function() {
+      gradient = draw.gradient('linear', function(stop) {
+        s1 = stop.at({ offset: 0.12, color: '#333', opacity: 1 })
+        s2 = stop.at({ offset: 0.93, color: '#fff', opacity: 1 })
+      })
+      expect(gradient.get(0)).toBe(s1)
+      expect(gradient.get(1)).toBe(s2)
+      expect(gradient.get(2)).toBe(undefined)
+    })
+
+  })
   
 })
\ No newline at end of file
diff --git a/spec/spec/number.js b/spec/spec/number.js
new file mode 100644 (file)
index 0000000..62e51a2
--- /dev/null
@@ -0,0 +1,177 @@
+describe('Number', function() {
+  var number
+
+  beforeEach(function() {
+    number = new SVG.Number
+  })
+
+  describe('new', function() {
+    
+    it('is zero', function() {
+      expect(number.value).toBe(0)
+    })
+    it('has a blank unit', function() {
+      expect(number.unit).toBe('')
+    })
+    it('parses a pixel value', function() {
+      number = new SVG.Number('20px')
+      expect(number.value).toBe(20)
+      expect(number.unit).toBe('px')
+    })
+    it('parses a percent value', function() {
+      number = new SVG.Number('99%')
+      expect(number.value).toBe(0.99)
+      expect(number.unit).toBe('%')
+    })
+
+  })
+
+  describe('toString()', function() {
+
+    it('converts the number to a string', function() {
+      expect(number.toString()).toBe('0')
+    })
+    it('appends the unit', function() {
+      number.value = 1.21
+      number.unit = 'px' 
+      expect(number.toString()).toBe('1.21px')
+    })
+    it('converts percent values properly', function() {
+      number.value = 1.36
+      number.unit = '%' 
+      expect(number.toString()).toBe('136%')
+    })
+
+  })
+
+  describe('valueOf()', function() {
+
+    it('returns a numeric value for default units', function() {
+      expect(typeof number.valueOf()).toBe('number')
+      number = new SVG.Number('12')
+      expect(typeof number.valueOf()).toBe('number')
+      number = new SVG.Number(13)
+      expect(typeof number.valueOf()).toBe('number')
+    })
+    it('returns a numeric value for pixel units', function() {
+      number = new SVG.Number('10px')
+      expect(typeof number.valueOf()).toBe('number')
+    })
+    it('returns a numeric value for percent units', function() {
+      number = new SVG.Number('20%')
+      expect(typeof number.valueOf()).toBe('number')
+    })
+    it('converts to a primitive when multiplying', function() {
+      number.value = 80
+      expect(number * 4).toBe(320)
+    })
+
+  })
+
+  describe('to()', function() {
+
+    beforeEach(function() {
+      number.plus(4)
+    })
+
+    it('changes the unit value', function() {
+      number.to('%')
+      expect(number.unit).toBe('%')
+    })
+    it('changes the output value', function() {
+      var oldNumber = number.valueOf()
+      number.to('%')
+      expect(number.toString()).toBe('400%')
+    })
+
+  })
+
+  describe('plus()', function() {
+
+    it('adds a given number', function() {
+      number.plus(3.5)
+      expect(number.valueOf()).toBe(3.5)
+    })
+    it('adds a given percentage value', function() {
+      number.plus('225%')
+      expect(number.valueOf()).toBe(2.25)
+    })
+    it('adds a given pixel value', function() {
+      number.plus('83px')
+      expect(number.valueOf()).toBe(83)
+    })
+
+  })
+
+  describe('minus()', function() {
+
+    it('subtracts a given number', function() {
+      number.minus(3.7)
+      expect(number.valueOf()).toBe(-3.7)
+    })
+    it('subtracts a given percentage value', function() {
+      number.minus('223%')
+      expect(number.valueOf()).toBe(-2.23)
+    })
+    it('subtracts a given pixel value', function() {
+      number.minus('85px')
+      expect(number.valueOf()).toBe(-85)
+    })
+
+  })
+
+  describe('times()', function() {
+
+    beforeEach(function() {
+      number.plus(4)
+    })
+
+    it('multiplies with a given number', function() {
+      number.times(3)
+      expect(number.valueOf()).toBe(12)
+    })
+    it('multiplies with a given percentage value', function() {
+      number.times('110%')
+      expect(number.valueOf()).toBe(4.4)
+    })
+    it('multiplies with a given pixel value', function() {
+      number.times('85px')
+      expect(number.valueOf()).toBe(340)
+    })
+
+  })
+
+  describe('divide()', function() {
+
+    beforeEach(function() {
+      number.plus(90)
+    })
+
+    it('divides by a given number', function() {
+      number.divide(3)
+      expect(number.valueOf()).toBe(30)
+    })
+    it('divides by a given percentage value', function() {
+      number.divide('3000%')
+      expect(number.valueOf()).toBe(3)
+    })
+    it('divides by a given pixel value', function() {
+      number.divide('45px')
+      expect(number.valueOf()).toBe(2)
+    })
+
+  })
+
+})
+
+
+
+
+
+
+
+
+
+
+
+
index 314961d550f27ec91aeaf0b03b432f848aef6d09..fd8522884a42e27eb85dce91ab1f14a80a10d365 100644 (file)
@@ -9,23 +9,25 @@ SVG.defaults = {
     'fill-opacity':   1
   , 'stroke-opacity': 1
   , 'stroke-width':   0
-  , fill:       '#000'
-  , stroke:     '#000'
-  , opacity:    1
+  , fill:             '#000'
+  , stroke:           '#000'
+  , opacity:          1
     /* position */
-  , x:          0
-  , y:          0
-  , cx:         0
-  , cy:         0
-    /* size */
-  , width:      0
-  , height:     0
-    /* radius */
-  , r:          0
-  , rx:         0
-  , ry:         0
-    /* gradient */
-  , offset:     0
+  , x:                0
+  , y:                0
+  , cx:               0
+  , cy:               0
+    /* size */  
+  , width:            0
+  , height:           0
+    /* radius */  
+  , r:                0
+  , rx:               0
+  , ry:               0
+    /* gradient */  
+  , offset:           0
+  , 'stop-opacity':   1
+  , 'stop-color':     '#000'
   }
   
   // Default transformation values
index 3fa456fa1aceae103b4aca8f132bb5a017051245..d40108de229dee3db811eedcf12903da0aeadc57 100644 (file)
@@ -23,12 +23,18 @@ SVG.Element = function(node) {
 SVG.extend(SVG.Element, {
   // Move over x-axis
   x: function(x) {
-    if (x) x /= this.trans.scaleX
+    if (x) {
+      x = new SVG.Number(x)
+      x.value /= this.trans.scaleX
+    }
     return this.attr('x', x)
   }
   // Move over y-axis
 , y: function(y) {
-    if (y) y /= this.trans.scaleY
+    if (y) {
+      y = new SVG.Number(y)
+      y.value /= this.trans.scaleY
+    }
     return this.attr('y', y)
   }
   // Move by center over x-axis
@@ -50,8 +56,8 @@ SVG.extend(SVG.Element, {
   // Set element size to given width and height
 , size: function(width, height) { 
     return this.attr({
-      width:  width
-    , height: height
+      width:  new SVG.Number(width)
+    , height: new SVG.Number(height)
     })
   }
   // Clone element
index 2ff0c868830b4bca0f8badc5af09fbccaa59c2c2..ea019e0c9bc941c039f8026e23aee78a05294b16 100644 (file)
@@ -18,17 +18,17 @@ SVG.extend(SVG.Ellipse, {
   }
   // Move by center over x-axis
 , cx: function(x) {
-    return x == null ? this.attr('cx') : this.attr('cx', x / this.trans.scaleX)
+    return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX))
   }
   // Move by center over y-axis
 , cy: function(y) {
-    return y == null ? this.attr('cy') : this.attr('cy', y / this.trans.scaleY)
+    return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY))
   }
   // Custom size function
 , size: function(width, height) {
     return this.attr({
-      rx: width / 2,
-      ry: height / 2
+      rx: new SVG.Number(width).divide(2)
+    , ry: new SVG.Number(height).divide(2)
     })
   }
   
@@ -42,7 +42,7 @@ SVG.extend(SVG.Container, {
   }
   // Create an ellipse
 , ellipse: function(width, height) {
-    return this.put(new SVG.Ellipse().size(width, height).move(0, 0))
+    return this.put(new SVG.Ellipse).size(width, height).move(0, 0)
   }
   
 })
index e1063e3e8b082e72c0235019a8da267d54a96ca9..eb563fa22c051d86bd63a208f402050c0e0551bc 100644 (file)
--- a/src/fx.js
+++ b/src/fx.js
@@ -250,6 +250,16 @@ SVG.extend(SVG.FX, {
     
     return this
   }
+  // Add animateable gradient update
+, update: function(o) {
+    if (this.target instanceof SVG.Stop) {
+      if (o.opacity != null) this.attr('stop-opacity', o.opacity)
+      if (o.color   != null) this.attr('stop-color', o.color)
+      if (o.offset  != null) this.attr('offset', new SVG.Number(o.offset))
+    }
+
+    return this
+  }
   // Add callback for each keyframe
 , during: function(during) {
     this._during = during
@@ -291,7 +301,10 @@ SVG.extend(SVG.FX, {
     
     /* unit recalculation */
     SVG.regex.unit.test(o.to) ?
-      this._unit(o, pos) :
+      new SVG.Number(o.to)
+        .minus(new SVG.Number(o.from))
+        .times(pos)
+        .plus(new SVG.Number(o.from)) :
     
     /* color recalculation */
     o.to && (o.to.r || SVG.Color.test(o.to)) ?
@@ -300,19 +313,6 @@ SVG.extend(SVG.FX, {
     /* for all other values wait until pos has reached 1 to return the final value */
     pos < 1 ? o.from : o.to
   }
-  // Private: tween unit
-, _unit: function(o, pos) {
-    var match, from
-    
-    /* convert FROM unit */
-    match = SVG.regex.unit.exec(o.from.toString())
-    from = parseFloat(match ? match[1] : 0)
-    
-    /* convert TO unit */
-    match = SVG.regex.unit.exec(o.to)
-    
-    return (from + (parseFloat(match[1]) - from) * pos) + match[2]
-  }
   // Private: tween color
 , _color: function(o, pos) {
     var from, to
index f98a0b0719863eba17633d6cda6e55d186e6027d..71da7d1766639e9b46a50d4f8b64225209f8260c 100644 (file)
@@ -13,19 +13,19 @@ SVG.extend(SVG.Gradient, {
   // From position
   from: function(x, y) {
     return this.type == 'radial' ?
-      this.attr({ fx: x + '%', fy: y + '%' }) :
-      this.attr({ x1: x + '%', y1: y + '%' })
+      this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) :
+      this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) })
   }
   // To position
 , to: function(x, y) {
     return this.type == 'radial' ?
-      this.attr({ cx: x + '%', cy: y + '%' }) :
-      this.attr({ x2: x + '%', y2: y + '%' })
+      this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) :
+      this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) })
   }
   // Radius for radial gradient
-, radius: function(radius) {
+, radius: function(r) {
     return this.type == 'radial' ?
-      this.attr({ r: radius + '%' }) :
+      this.attr({ r: new SVG.Number(r) }) :
       this
   }
   // Add a color stop
@@ -35,8 +35,7 @@ SVG.extend(SVG.Gradient, {
   // Update gradient
 , update: function(block) {
     /* remove all stops */
-    while (this.node.hasChildNodes())
-      this.node.removeChild(this.node.lastChild)
+    this.clear()
     
     /* invoke passed block */
     block(this)
@@ -47,6 +46,10 @@ SVG.extend(SVG.Gradient, {
 , fill: function() {
     return 'url(#' + this.attr('id') + ')'
   }
+  // Get a stop at the given index
+, get: function(i) {
+    return this.children()[i]
+  }
   
 })
 
@@ -82,22 +85,18 @@ SVG.Stop = function(stop) {
 }
 
 // Inherit from SVG.Element
-SVG.Stop.prototype = new SVG.Element()
+SVG.Stop.prototype = new SVG.Element
 
 //
 SVG.extend(SVG.Stop, {
   // add color stops
   update: function(o) {
-    var index
-      , attr = ['opacity', 'color']
-    
-    /* build style attribute */
-    for (index = attr.length - 1; index >= 0; index--)
-      if (o[attr[index]] != null)
-        this.style('stop-' + attr[index], o[attr[index]])
-    
     /* set attributes */
-    return this.attr('offset', (o.offset != null ? o.offset : this.attr('offset')) + '%')
+    if (o.opacity != null) this.attr('stop-opacity', o.opacity)
+    if (o.color   != null) this.attr('stop-color', o.color)
+    if (o.offset  != null) this.attr('offset', new SVG.Number(o.offset))
+
+    return this
   }
   
 })
diff --git a/src/number.js b/src/number.js
new file mode 100644 (file)
index 0000000..28e260e
--- /dev/null
@@ -0,0 +1,75 @@
+// Module for unit convertions
+SVG.Number = function(value) {
+
+  /* initialize defaults */
+  this.value = 0
+  this.unit = ''
+
+  /* parse value */
+  switch(typeof value) {
+    case 'number':
+      this.value = value
+    break
+    case 'string':
+      var match = value.match(SVG.regex.unit)
+
+      /* make valu numeric */
+      this.value = parseFloat(match[1])
+  
+      /* normalize percent value */
+      if (match[2] == '%')
+        this.value /= 100
+  
+      /* store unit */
+      this.unit = match[2]
+
+    break
+    default:
+      if (value instanceof SVG.Number) {
+        this.value = value.value
+        this.unit  = value.unit
+      }
+    break
+  }
+}
+
+SVG.extend(SVG.Number, {
+  // Stringalize
+  toString: function() {
+    return (this.unit == '%' ? ~~(this.value * 100) : this.value) + this.unit
+  }
+, // Convert to primitive
+  valueOf: function() {
+    return this.value
+  }
+  // Convert to different unit
+, to: function(unit) {
+    if (typeof unit === 'string')
+      this.unit = unit
+
+    return this
+  }
+  // Add number
+, plus: function(number) {
+    this.value = this + new SVG.Number(number)
+
+    return this
+  }
+  // Subtract number
+, minus: function(number) {
+    return this.plus(-new SVG.Number(number))
+  }
+  // Multiply number
+, times: function(number) {
+    this.value = this * new SVG.Number(number)
+
+    return this
+  }
+  // Divide number
+, divide: function(number) {
+    this.value = this / new SVG.Number(number)
+
+    return this
+  }
+
+})
\ No newline at end of file
index 4cd74ae5856a9a3dce5a0b1da6ffdf488ff5ea30..4286d8a1d2bec98c2590d5762d2ab67ea0d3e0d8 100644 (file)
@@ -13,7 +13,7 @@ SVG.regex = {
   
   /* parse rgb value */
 , rgb:          /rgb\((\d+),(\d+),(\d+)\)/
-  
+
   /* test hex value */
 , isHex:        /^#[a-f0-9]{3,6}$/i
   
@@ -31,5 +31,8 @@ SVG.regex = {
   
   /* test for numeric string */
 , isNumber:     /^-?[\d\.]+$/
+
+  /* test for percent value */
+, isPercent:    /^-?[\d\.]+%$/
   
 }
\ No newline at end of file