diff options
author | wout <wout@impinc.co.uk> | 2013-03-09 22:28:47 +0100 |
---|---|---|
committer | wout <wout@impinc.co.uk> | 2013-03-09 22:28:47 +0100 |
commit | 6c6c82ed59533f44f8754b69d47e3b11cd6dd129 (patch) | |
tree | adaef53d8970acb228fe14d490c532f1baabbb13 | |
parent | 16c4a146ee97fa43e0c456fe801490351e551e96 (diff) | |
download | svg.js-6c6c82ed59533f44f8754b69d47e3b11cd6dd129.tar.gz svg.js-6c6c82ed59533f44f8754b69d47e3b11cd6dd129.zip |
Bumped to v0.9, added better style management, text support and extended animation functionality0.10
-rw-r--r-- | README.md | 79 | ||||
-rw-r--r-- | Rakefile | 7 | ||||
-rw-r--r-- | dist/svg.js | 635 | ||||
-rw-r--r-- | dist/svg.min.js | 4 | ||||
-rw-r--r-- | package.json | 37 | ||||
-rw-r--r-- | src/arrange.js | 4 | ||||
-rw-r--r-- | src/bbox.js | 2 | ||||
-rw-r--r-- | src/color.js | 1 | ||||
-rw-r--r-- | src/doc.js | 10 | ||||
-rw-r--r-- | src/element.js | 108 | ||||
-rw-r--r-- | src/ellipse.js | 10 | ||||
-rw-r--r-- | src/event.js | 37 | ||||
-rw-r--r-- | src/fx.js | 72 | ||||
-rw-r--r-- | src/gradient.js | 32 | ||||
-rw-r--r-- | src/line.js | 29 | ||||
-rw-r--r-- | src/nested.js | 2 | ||||
-rw-r--r-- | src/path.js | 11 | ||||
-rw-r--r-- | src/pattern.js | 10 | ||||
-rw-r--r-- | src/poly.js | 4 | ||||
-rw-r--r-- | src/rect.js | 2 | ||||
-rw-r--r-- | src/regex.js | 37 | ||||
-rw-r--r-- | src/shape.js | 2 | ||||
-rw-r--r-- | src/sugar.js | 20 | ||||
-rw-r--r-- | src/svg.js | 86 | ||||
-rw-r--r-- | src/text.js | 128 | ||||
-rw-r--r-- | src/wrap.js | 32 |
26 files changed, 864 insertions, 537 deletions
@@ -2,7 +2,7 @@ A lightweight library for manipulating and animating SVG. -Svg.js has no dependencies and aims to be as small as possible. The base library is 3k gzipped, with all bells and whistles about 5k. +Svg.js has no dependencies and aims to be as small as possible. The base library is 3k gzipped, with all bells and whistles about 6.5k. Svg.js is licensed under the terms of the MIT License. @@ -12,27 +12,27 @@ See [svgjs.com](http://svgjs.com) for an introduction, [documentation](http://sv ### Create a SVG document -Use the `svg()` function to create a SVG document within a given html element: +Use the `SVG()` function to create a SVG document within a given html element: ```javascript -var draw = svg('paper').size(300, 300) +var draw = SVG('canvas').size(300, 300) var rect = draw.rect(100, 100).attr({ fill: '#f06' }) ``` The first argument can either be an id of the element or the selected element itself. This will generate the following output: ```html -<div id="paper"> +<div id="canvas"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xlink="http://www.w3.org/1999/xlink" width="300" height="300"> <rect width="100" height="100" fill="#f06"></rect> </svg> </div> ``` -By default the svg canvas follows the dimensions of its parent, in this case `#paper`: +By default the svg canvas follows the dimensions of its parent, in this case `#canvas`: ```javascript -var draw = svg('paper').size('100%', '100%') +var draw = SVG('canvas').size('100%', '100%') ``` ### Checking for SVG support @@ -41,7 +41,7 @@ By default this library assumes the client's browser supports SVG. You can test ```javascript if (SVG.supported) { - var draw = svg('paper') + var draw = SVG('canvas') var rect = draw.rect(100, 100) } else { alert('SVG not supported') @@ -71,6 +71,19 @@ var zoom = box.zoom If the size of the viewbox equals the size of the svg canvas, the zoom value will be 1. +### Nested svg +With this feature you can nest svg documents within each other. Nested svg documents have exactly the same features as the main, top-level svg document: + +```javascript +var nested = draw.nested() + +var rect = nested.rect(200, 200) +``` + + +_This functionality requires the nested.js module which is included in the default distribution._ + + ## Elements ### Rect @@ -240,11 +253,45 @@ rect.transform({ Important: matrix transformations are not yet supported. +### Style +With the `style()` method the `style` attribute can be managed like attributes with `attr`: + +```javascript +rect.style('cursor', 'pointer') +``` + +Multiple styles can be set at once using an object: + +```javascript +rect.style({ cursor: 'pointer', fill: '#f03' }) +``` + +Or a css string: + +```javascript +rect.style('cursor:pointer;fill:#f03;') +``` + +Similarly to `attr()` the `style()` method can also act as a getter: + +```javascript +rect.style('cursor') +// => pointer +``` + +Or even a full getter: + +```javascript +rect.style() +// => 'cursor:pointer;fill:#f03;' +``` + + ### Move Move the element to a given `x` and `y` position by its upper left corner: ```javascript -rect.move(200, 350); +rect.move(200, 350) ``` Note that you can also use the following code to move elements around: @@ -647,20 +694,6 @@ group.add(rect) _This functionality requires the group.js module which is included in the default distribution._ - -## Nested svg -With this feature you can nest svg documents within each other. Nested svg documents have exactly the same features as the main, top-level svg document: - -```javascript -var nested = draw.nested() - -var rect = nested.rect(200, 200) -``` - - -_This functionality requires the nested.js module which is included in the default distribution._ - - ## Gradients There are linear and radial gradients. The linear gradient can be created like this: @@ -932,8 +965,6 @@ _The Rakefile has been borrowed from [madrobby's](https://github.com/madrobby) [ ## To-do - Instance module - Text on path module (write text along paths) -- Dynamic css styles -- Animatable unit values (e.g. '100%' or '30mm') ## Compatibility @@ -1,4 +1,4 @@ -SVGJS_VERSION = '0.8' +SVGJS_VERSION = '0.9' # all available modules in the correct loading order MODULES = %w[ svg regex color viewbox bbox element container fx event group arrange defs mask pattern gradient doc shape wrap rect ellipse line poly path image text nested sugar ] @@ -53,12 +53,13 @@ BuildTask.define_task 'dist/svg.js' => MODULES.map {|m| "src/#{ m }.js" } do |ta File.open(task.name, 'w') do |file| file.puts "/* svg.js %s - %s - svgjs.com/license */" % [version_string, task.modules.join(' ')] - - file.puts '(function() {' + + file.puts ';(function() {' file.puts "\n" file.puts svgjs file.puts '}).call(this);' end + end file 'dist/svg.min.js' => 'dist/svg.js' do |task| diff --git a/dist/svg.js b/dist/svg.js index 7cec3bd..f3a0b84 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -1,50 +1,54 @@ -/* svg.js v0.8-5-g5e3ff07 - svg regex color viewbox bbox element container fx event group arrange defs mask pattern gradient doc shape wrap rect ellipse line poly path image text nested sugar - svgjs.com/license */ -(function() { +/* svg.js v0.9 - svg regex color viewbox bbox element container fx event group arrange defs mask pattern gradient doc shape wrap rect ellipse line poly path image text nested sugar - svgjs.com/license */ +;(function() { - this.svg = function(element) { + this.SVG = function(element) { if (SVG.supported) return new SVG.Doc(element) } - // The main wrapping element - this.SVG = { - /* default namespaces */ - ns: 'http://www.w3.org/2000/svg' - , xlink: 'http://www.w3.org/1999/xlink' + // DEPRECATED!!! Use SVG() instead + this.svg = function(element) { + console.warn('WARNING: svg() is deprecated, please use SVG() instead.') + return SVG(element) + } + + // Default namespaces + SVG.ns = 'http://www.w3.org/2000/svg' + SVG.xlink = 'http://www.w3.org/1999/xlink' + + // Element id sequence + SVG.did = 1000 + + // Get next named element id + SVG.eid = function(name) { + return 'Svgjs' + name.charAt(0).toUpperCase() + name.slice(1) + 'Element' + (SVG.did++) + } + + // Method for element creation + SVG.create = function(name) { + /* create element */ + var element = document.createElementNS(this.ns, name) + + /* apply unique id */ + element.setAttribute('id', this.eid(name)) - /* element id sequence */ - , did: 1000 + return element + } - // Get next named element id - , eid: function(name) { - return 'Svgjs' + name.charAt(0).toUpperCase() + name.slice(1) + 'Element' + (SVG.did++) - } - // Method for element creation - , create: function(name) { - /* create element */ - var element = document.createElementNS(this.ns, name) - - /* apply unique id */ - element.setAttribute('id', this.eid(name)) - - return element - } // Method for extending objects - , extend: function() { - var modules, methods, key, i - - /* get list of modules */ - modules = Array.prototype.slice.call(arguments) - - /* get object with extensions */ - methods = modules.pop() - - for (i = modules.length - 1; i >= 0; i--) - if (modules[i]) - for (key in methods) - modules[i].prototype[key] = methods[key] - } + SVG.extend = function() { + var modules, methods, key, i + + /* get list of modules */ + modules = Array.prototype.slice.call(arguments) + /* get object with extensions */ + methods = modules.pop() + + for (i = modules.length - 1; i >= 0; i--) + if (modules[i]) + for (key in methods) + modules[i].prototype[key] = methods[key] } // svg support test @@ -53,24 +57,39 @@ !! document.createElementNS(SVG.ns,'svg').createSVGRect })() - if (!SVG.supported) return false; + if (!SVG.supported) return false 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 - - , rgb: /rgb\((\d+),(\d+),(\d+),([\d\.]+)\)/ - - , hsb: /hsb\((\d+),(\d+),(\d+),([\d\.]+)\)/ - - , isHex: /^#/i - - , isRgb: /^rgb\(/ + /* parse rgb value */ + , rgb: /rgb\((\d+),(\d+),(\d+),([\d\.]+)\)/ + + /* parse hsb value */ + , hsb: /hsb\((\d+),(\d+),(\d+),([\d\.]+)\)/ + + /* test hex value */ + , isHex: /^#[a-f0-9]{3,6}$/i + + /* test rgb value */ + , isRgb: /^rgb\(/ - , isHsb: /^hsb\(/ + /* test hsb value */ + , isHsb: /^hsb\(/ + /* test css declaration */ + , isCss: /[^:]+:[^;]+;?/ + + /* test css property */ + , isStyle: /^font|text|leading|cursor/ + + /* test for blank string */ + , isBlank: /^(\s+)?$/ + } SVG.Color = function(color) { @@ -116,7 +135,6 @@ this.r = color.r this.g = color.g this.b = color.b - } } @@ -290,7 +308,7 @@ this.y = box.y + element.trans.y /* add the center */ - this.cx = box.x + element.trans.x + box.width / 2 + this.cx = box.x + element.trans.x + box.width / 2 this.cy = box.y + element.trans.y + box.height / 2 /* plain width and height */ @@ -320,7 +338,7 @@ } /* initialize style store */ - this.style = {} + this.styles = {} /* initialize transformation store with defaults */ this.trans = { @@ -409,6 +427,7 @@ , remove: function() { if (this.parent) this.parent.remove(this) + return this } // Get parent document @@ -424,24 +443,29 @@ if (arguments.length < 2) { /* apply every attribute individually if an object is passed */ if (typeof a == 'object') - for (v in a) this.attr(v, a[v]) + for (v in a) + this.attr(v, a[v]) /* act as a getter for style attributes */ else if (this._isStyle(a)) return a == 'text' ? this.content : a == 'leading' ? - this[a] : - this.style[a] + this.leading() : + this.style(a) /* act as a getter if the first and only argument is not an object */ else - return this.attrs[a] + return this.attrs[a] || this.node.getAttribute(a) } else if (v === null) { /* remove value */ this.node.removeAttribute(a) + } else if (a == 'style') { + /* redirect to the style method */ + return this.style(v) + } else { /* store value */ this.attrs[a] = v @@ -472,28 +496,38 @@ a == 'text' ? this.text(v) : a == 'leading' ? - this[a] = v : - this.style[a] = v - - this.text(this.content) + this.leading(v) : + this.style(a, v) + + /* rebuild if required */ + if (this.rebuild) + this.rebuild() } } return this } // Manage transformations - , transform: function(o) { - /* act as a getter if the first argument is a string */ - if (typeof o === 'string') - return this.trans[o] + , transform: function(o, v) { + if (typeof o === 'string') { + /* act as a getter if only one string argument is given */ + if (arguments.length < 2) + return this.trans[o] + /* apply transformations as object if key value arguments are given*/ + var transform = {} + transform[o] = v + + return this.transform(transform) + } + /* ... otherwise continue as a setter */ - var key, transform = [] + var transform = [] /* merge values */ - for (key in o) - if (o[key] != null) - this.trans[key] = o[key] + for (v in o) + if (o[v] != null) + this.trans[v] = o[v] /* alias current transformations */ o = this.trans @@ -524,6 +558,52 @@ /* add only te required transformations */ return this.attr('transform', transform.join(' ')) } + // Dynamic style generator + , style: function(s, v) { + if (arguments.length == 0) { + /* get full style */ + return this.attr('style') + + } 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(':') + + if (v.length == 2) + this.style(v[0].replace(/\s+/g, ''), v[1].replace(/^\s+/,'').replace(/\s+$/,'')) + } + } else { + /* act as a getter if the first and only argument is not an object */ + return this.styles[s] + } + + } else if (v === null) { + /* remove value */ + delete this.styles[s] + + } else { + /* store value */ + this.styles[s] = v + } + + /* rebuild style string */ + s = '' + for (v in this.styles) + s += v + ':' + this.styles[v] + ';' + + /* apply style */ + this.node.setAttribute('style', s) + + return this + } // Store data values on svg nodes , data: function(a, v, r) { if (arguments.length < 2) { @@ -554,19 +634,15 @@ } // Show element , show: function() { - this.node.style.display = '' - - return this + return this.style('display', '') } // Hide element , hide: function() { - this.node.style.display = 'none' - - return this + return this.style('display', 'none') } // Is element visible? , visible: function() { - return this.node.style.display != 'none' + return this.style('display') != 'none' } // Private: find svg parent by instance , _parent: function(parent) { @@ -579,7 +655,7 @@ } // Private: tester method for style detection , _isStyle: function(attr) { - return typeof attr == 'string' && this._isText() ? (/^font|text|leading/).test(attr) : false + return typeof attr == 'string' ? SVG.regex.isStyle.test(attr) : false } // Private: element type tester , _isText: function() { @@ -755,7 +831,7 @@ duration = duration == null ? 1000 : duration ease = ease || '<>' - var akeys, tkeys, tvalues + var akeys, tkeys, skeys , element = this.target , fx = this , start = new Date().getTime() @@ -764,24 +840,29 @@ /* start animation */ this.interval = setInterval(function(){ // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby. - var index + var i, key , time = new Date().getTime() , pos = time > finish ? 1 : (time - start) / duration /* collect attribute keys */ if (akeys == null) { akeys = [] - for (var key in fx.attrs) + for (key in fx.attrs) akeys.push(key) } /* collect transformation keys */ if (tkeys == null) { tkeys = [] - for (var key in fx.trans) + for (key in fx.trans) tkeys.push(key) - - tvalues = {} + } + + /* collect style keys */ + if (skeys == null) { + skeys = [] + for (key in fx.styles) + skeys.push(key) } /* apply easing */ @@ -801,23 +882,23 @@ if (fx._move) element.move(fx._at(fx._move.x, pos), fx._at(fx._move.y, pos)) else if (fx._center) - element.move(fx._at(fx._center.x, pos), fx._at(fx._center.y, pos)) + element.center(fx._at(fx._center.x, pos), fx._at(fx._center.y, pos)) /* run all size properties */ if (fx._size) element.size(fx._at(fx._size.width, pos), fx._at(fx._size.height, pos)) /* animate attributes */ - for (index = akeys.length - 1; index >= 0; index--) - element.attr(akeys[index], fx._at(fx.attrs[akeys[index]], pos)) + for (i = akeys.length - 1; i >= 0; i--) + element.attr(akeys[i], fx._at(fx.attrs[akeys[i]], pos)) /* animate transformations */ - if (tkeys.length > 0) { - for (index = tkeys.length - 1; index >= 0; index--) - tvalues[tkeys[index]] = fx._at(fx.trans[tkeys[index]], pos) - - element.transform(tvalues) - } + for (i = tkeys.length - 1; i >= 0; i--) + element.transform(tkeys[i], fx._at(fx.trans[tkeys[i]], pos)) + + /* animate styles */ + for (i = skeys.length - 1; i >= 0; i--) + element.style(skeys[i], fx._at(fx.styles[skeys[i]], pos)) /* callback for each keyframe */ if (fx._during) @@ -844,12 +925,23 @@ else this.attrs[a] = { from: this.target.attr(a), to: v } - return this; + return this } // Add animatable transformations - , transform: function(o) { - for (var key in o) - this.trans[key] = { from: this.target.trans[key], to: o[key] } + , transform: function(t, v) { + for (var key in t) + this.trans[key] = { from: this.target.trans[key], to: t[key] } + + return this + } + // Add animatable styles + , style: function(s, v) { + if (typeof s == 'object') + for (var key in s) + this.style(key, s[key]) + + else + this.styles[s] = { from: this.target.style(s), to: v } return this } @@ -866,11 +958,18 @@ } // Add animatable size , size: function(width, height) { - var box = this.target.bbox() - - this._size = { - width: { from: box.width, to: width } - , height: { from: box.height, to: height } + if (this.target instanceof SVG.Text) { + /* animate font size for Text elements */ + this.attr('font-size', width) + + } else { + /* animate bbox based size for all other elements */ + var box = this.target.bbox() + + this._size = { + width: { from: box.width, to: width } + , height: { from: box.height, to: height } + } } return this @@ -906,6 +1005,7 @@ /* reset storage for properties that need animation */ this.attrs = {} this.trans = {} + this.styles = {} this._move = null this._size = null this._after = null @@ -986,28 +1086,27 @@ // }) - ;[ 'click', - 'dblclick', - 'mousedown', - 'mouseup', - 'mouseover', - 'mouseout', - 'mousemove', - 'mouseenter', - 'mouseleave', - 'touchstart', - 'touchend', - 'touchmove', - 'touchcancel' ].forEach(function(event) { + ;[ 'click' + , 'dblclick' + , 'mousedown' + , 'mouseup' + , 'mouseover' + , 'mouseout' + , 'mousemove' + , 'mouseenter' + , 'mouseleave' + , 'touchstart' + , 'touchend' + , 'touchmove' + , 'touchcancel' ].forEach(function(event) { /* add event to SVG.Element */ SVG.Element.prototype[event] = function(f) { var self = this /* bind event to element rather than element node */ - this.node['on' + event] = typeof f == 'function' - ? function() { return f.apply(self, arguments); } - : null + this.node['on' + event] = typeof f == 'function' ? + function() { return f.apply(self, arguments) } : null return this } @@ -1037,14 +1136,14 @@ SVG.on(this.node, event, listener) return this - }, + } // Unbind event from listener - off: function(event, listener) { + , off: function(event, listener) { SVG.off(this.node, event, listener) return this } - }); + }) SVG.G = function() { this.constructor.call(this, SVG.create('g')) @@ -1091,11 +1190,9 @@ } // Send given element one step backward , backward: function() { - var i - this.parent.level() - i = this.position() + var i = this.position() if (i > 1) this.parent.remove(this).add(this, i - 1) @@ -1171,11 +1268,11 @@ block(element) return element.attr({ - x: 0, - y: 0, - width: width, - height: height, - patternUnits: 'userSpaceOnUse' + x: 0 + , y: 0 + , width: width + , height: height + , patternUnits: 'userSpaceOnUse' }) } @@ -1198,25 +1295,25 @@ return this.type == 'radial' ? this.attr({ fx: x + '%', fy: y + '%' }) : this.attr({ x1: x + '%', y1: y + '%' }) - }, + } // To position - to: function(x, y) { + , to: function(x, y) { return this.type == 'radial' ? this.attr({ cx: x + '%', cy: y + '%' }) : this.attr({ x2: x + '%', y2: y + '%' }) - }, + } // Radius for radial gradient - radius: function(radius) { + , radius: function(radius) { return this.type == 'radial' ? this.attr({ r: radius + '%' }) : this - }, + } // Add a color stop - at: function(stop) { + , at: function(stop) { return this.put(new SVG.Stop(stop)) - }, + } // Update gradient - update: function(block) { + , update: function(block) { /* remove all stops */ while (this.node.hasChildNodes()) this.node.removeChild(this.node.lastChild) @@ -1225,9 +1322,9 @@ block(this) return this - }, + } // Return the fill id - fill: function() { + , fill: function() { return 'url(#' + this.attr('id') + ')' } @@ -1264,20 +1361,16 @@ /* add color stops */ update: function(o) { - var index, - style = '', - attr = ['opacity', 'color'] + var index + , attr = ['opacity', 'color'] /* build style attribute */ for (index = attr.length - 1; index >= 0; index--) if (o[attr[index]] != null) - style += 'stop-' + attr[index] + ':' + o[attr[index]] + ';' + this.style('stop-' + attr[index], o[attr[index]]) /* set attributes */ - return this.attr({ - offset: (o.offset != null ? o.offset : this.attrs.offset || 0) + '%', - style: style - }) + return this.attr('offset', (o.offset != null ? o.offset : this.attrs.offset || 0) + '%') } }) @@ -1309,9 +1402,9 @@ // Basically it sets the position of the svg node to absolute // when the dom is loaded, and resets it to relative a few milliseconds later. SVG.Doc.prototype.stage = function() { - var check, - element = this, - wrapper = document.createElement('div') + var check + , element = this + , wrapper = document.createElement('div') /* set temp wrapper to position relative */ wrapper.style.cssText = 'position:relative;height:100%;' @@ -1323,10 +1416,10 @@ /* check for dom:ready */ check = function() { if (document.readyState === 'complete') { - element.attr('style', 'position:absolute;') + element.style('position:absolute;') setTimeout(function() { /* set position back to relative */ - element.attr('style', 'position:relative;') + element.style('position:relative;') /* remove temp wrapper */ element.parent.removeChild(element.node.parentNode) @@ -1349,7 +1442,7 @@ } // Inherit from SVG.Element - SVG.Shape.prototype = new SVG.Element() + SVG.Shape.prototype = new SVG.Element SVG.Wrap = function(element) { this.constructor.call(this, SVG.create('g')) @@ -1367,30 +1460,30 @@ // Move wrapper around move: function(x, y) { return this.transform({ - x: x, - y: y + x: x + , y: y }) - }, + } // Set the actual size in pixels - size: function(width, height) { + , size: function(width, height) { var scale = width / this._b.width this.child.transform({ - scaleX: scale, - scaleY: height != null ? height / this._b.height : scale + scaleX: scale + , scaleY: height != null ? height / this._b.height : scale }) return this - }, + } // Move by center - center: function(x, y) { + , center: function(x, y) { return this.move( - x + (this._b.width * this.child.trans.scaleX) / -2, - y + (this._b.height * this.child.trans.scaleY) / -2 + x + (this._b.width * this.child.trans.scaleX) / -2 + , y + (this._b.height * this.child.trans.scaleY) / -2 ) - }, + } // Create distributed attr - attr: function(a, v, n) { + , attr: function(a, v, n) { /* call individual attributes if an object is given */ if (typeof a == 'object') { for (v in a) this.attr(v, a[v]) @@ -1413,9 +1506,9 @@ } return this - }, + } // Distribute plot method to child - plot: function(data) { + , plot: function(data) { /* plot new shape */ this.child.plot(data) @@ -1424,8 +1517,8 @@ /* reposition element withing wrapper */ this.child.transform({ - x: -this._b.x, - y: -this._b.y + x: -this._b.x + , y: -this._b.y }) return this @@ -1438,7 +1531,7 @@ } // Inherit from SVG.Shape - SVG.Rect.prototype = new SVG.Shape() + SVG.Rect.prototype = new SVG.Shape SVG.Ellipse = function() { this.constructor.call(this, SVG.create('ellipse')) @@ -1458,15 +1551,15 @@ }, // Custom size function size: function(width, height) { - return this. - attr({ rx: width / 2, ry: (height != null ? height : width) / 2 }). - center() + return this + .attr({ rx: width / 2, ry: (height != null ? height : width) / 2 }) + .center() }, // Custom center function center: function(x, y) { return this.attr({ - cx: x || (this.attrs.x || 0) + (this.attrs.rx || 0), - cy: y || (this.attrs.y || 0) + (this.attrs.ry || 0) + cx: x != null ? x : (this.attrs.x || 0) + (this.attrs.rx || 0) + , cy: y != null ? y : (this.attrs.y || 0) + (this.attrs.ry || 0) }) } @@ -1490,24 +1583,25 @@ var bbox = this.bbox() return this.attr({ - x1: this.attr('x1') - bbox.x + x, - y1: this.attr('y1') - bbox.y + y, - x2: this.attr('x2') - bbox.x + x, - y2: this.attr('y2') - bbox.y + y + x1: this.attr('x1') - bbox.x + x + , y1: this.attr('y1') - bbox.y + y + , x2: this.attr('x2') - bbox.x + x + , y2: this.attr('y2') - bbox.y + y }) - }, + } // Move element by its center - center: function(x, y) { + , center: function(x, y) { var bbox = this.bbox() return this.move(x - bbox.width / 2, y - bbox.height / 2) - }, + } // Set line size by width and height - size: function(width, height) { + , size: function(width, height) { var bbox = this.bbox() - this.attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', bbox.x + width) - return this.attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', bbox.y + height) + return this + .attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', bbox.x + width) + .attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', bbox.y + height) } }) @@ -1515,10 +1609,10 @@ SVG.extend(SVG.Container, { line: function(x1, y1, x2, y2) { return this.put(new SVG.Line().attr({ - x1: x1, - y1: y1, - x2: x2, - y2: y2 + x1: x1 + , y1: y1 + , x2: x2 + , y2: y2 })) } }) @@ -1537,7 +1631,7 @@ } // Inherit from SVG.Shape - SVG.Polyline.prototype = new SVG.Shape() + SVG.Polyline.prototype = new SVG.Shape // Add polygon-specific functions SVG.extend(SVG.Polyline, SVG.Poly) @@ -1547,7 +1641,7 @@ } // Inherit from SVG.Shape - SVG.Polygon.prototype = new SVG.Shape() + SVG.Polygon.prototype = new SVG.Shape // Add polygon-specific functions SVG.extend(SVG.Polygon, SVG.Poly) @@ -1560,21 +1654,20 @@ SVG.Path.prototype = new SVG.Shape() SVG.extend(SVG.Path, { - - /* move using transform */ + // Move using transform move: function(x, y) { this.transform({ x: x, y: y }) - }, + } - /* set path data */ - plot: function(data) { + // Set path data + , plot: function(data) { return this.attr('d', data || 'M0,0') } - }); + }) SVG.Image = function() { this.constructor.call(this, SVG.create('image')) @@ -1593,78 +1686,118 @@ }) - var _styleAttr = ['size', 'family', 'weight', 'stretch', 'variant', 'style'] + var _styleAttr = ('size family weight stretch variant style').split(' ') SVG.Text = function() { this.constructor.call(this, SVG.create('text')) /* define default style */ - this.style = { 'font-size': 16, 'font-family': 'Helvetica', 'text-anchor': 'start' } - this.leading = 1.2 + this.styles = { + 'font-size': 16 + , 'font-family': 'Helvetica' + , 'text-anchor': 'start' + } + + this._leading = 1.2 } // Inherit from SVG.Element - SVG.Text.prototype = new SVG.Shape() + SVG.Text.prototype = new SVG.Shape SVG.extend(SVG.Text, { // Set the text content text: function(text) { - /* update the content */ - this.content = text = text || 'text' - this.lines = [] + /* act as getter */ + if (text == null) + return this.content - var index, length, tspan, - style = this._style(), - parent = this.doc(), - lines = text.split("\n"), - size = this.style['font-size'] + /* remove existing lines */ + this.clear() - /* remove existing child nodes */ - while (this.node.hasChildNodes()) - this.node.removeChild(this.node.lastChild) + /* update the content */ + this.content = SVG.regex.isBlank.test(text) ? 'text' : text + + var i, il + , lines = text.split('\n') /* build new lines */ - for (index = 0, length = lines.length; index < length; index++) { - /* create new tspan and set attributes */ - tspan = new SVG.TSpan(). - text(lines[index]). - attr({ - dy: size * this.leading - (index == 0 ? size * 0.3 : 0), - x: (this.attrs.x || 0), - style: style - }) + for (i = 0, il = lines.length; i < il; i++) + this.tspan(lines[i]) - /* add new tspan */ - this.node.appendChild(tspan.node) - this.lines.push(tspan) - } - /* set style */ - return this.attr('style', style) - }, - - // Build style based on _styleAttr - _style: function() { - var index, style = '' + return this.attr('style', this.style()) + } + // Create a tspan + , tspan: function(text) { + var tspan = new SVG.TSpan().text(text) - for (index = _styleAttr.length - 1; index >= 0; index--) - if (this.style['font-' + _styleAttr[index]] != null) - style += 'font-' + _styleAttr[index] + ':' + this.style['font-' + _styleAttr[index]] + ';' + /* add new tspan */ + this.node.appendChild(tspan.node) + this.lines.push(tspan) - style += 'text-anchor:' + this.style['text-anchor'] + ';' - - return style + return tspan.attr('style', this.style()) + } + // Move element by its center + , center: function(x, y) { + var anchor = this.style('text-anchor') + , box = this.bbox() + , x = anchor == 'start' ? + x - box.width / 2 : + anchor == 'end' ? + x + box.width / 2 : x + + return this.move(x, y - box.height / 2) + } + // Set font size + , size: function(size) { + return this.attr('font-size', size) + } + // Set / get leading + , leading: function(value) { + /* act as getter */ + if (value == null) + return this._leading + + /* act as setter */ + this._leading = value + + return this.rebuild() + } + // rebuild appearance type + , rebuild: function() { + var i, il + , size = this.styles['font-size'] + + /* define position of all lines */ + for (i = 0, il = this.lines.length; i < il; i++) + this.lines[i].attr({ + dy: size * this._leading - (i == 0 ? size * 0.3 : 0) + , x: (this.attrs.x || 0) + , style: this.style() + }) + + return this + } + // Clear all lines + , clear: function() { + /* remove existing child nodes */ + while (this.node.hasChildNodes()) + this.node.removeChild(this.node.lastChild) + + this.lines = [] + + return this } }) - + // tspan class SVG.TSpan = function() { this.constructor.call(this, SVG.create('tspan')) } // Inherit from SVG.Shape - SVG.TSpan.prototype = new SVG.Shape() + SVG.TSpan.prototype = new SVG.Shape // Include the container object SVG.extend(SVG.TSpan, { @@ -1675,12 +1808,12 @@ return this } - }); + }) SVG.Nested = function() { this.constructor.call(this, SVG.create('svg')) - this.attr('style', 'overflow:visible') + this.style('overflow', 'visible') } // Inherit from SVG.Container @@ -1750,23 +1883,19 @@ if (SVG.Text) { - SVG.extend(SVG.Text, { + SVG.extend(SVG.Text, SVG.FX, { // Set font font: function(o) { - var key, attr = {} - - for (key in o) - key == 'leading' ? - attr[key] = o[key] : + for (var key in o) key == 'anchor' ? - attr['text-anchor'] = o[key] : + this.attr('text-anchor', o[key]) : _styleAttr.indexOf(key) > -1 ? - attr['font-'+ key] = o[key] : - void 0 - - return this.attr(attr).text(this.content) + this.attr('font-'+ key, o[key]) : + this.attr(key, o[key]) + + return this } - + }) } diff --git a/dist/svg.min.js b/dist/svg.min.js index 37d363f..b406ded 100644 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,2 +1,2 @@ -/* svg.js v0.8-5-g5e3ff07 - svg regex color viewbox bbox element container fx event group arrange defs mask pattern gradient doc shape wrap rect ellipse line poly path image text nested sugar - svgjs.com/license */ -(function(){this.svg=function(e){if(SVG.supported)return new SVG.Doc(e)},this.SVG={ns:"http://www.w3.org/2000/svg",xlink:"http://www.w3.org/1999/xlink",did:1e3,eid:function(e){return"Svgjs"+e.charAt(0).toUpperCase()+e.slice(1)+"Element"+SVG.did++},create:function(e){var t=document.createElementNS(this.ns,e);return t.setAttribute("id",this.eid(e)),t},extend:function(){var e,t,n,r;e=Array.prototype.slice.call(arguments),t=e.pop();for(r=e.length-1;r>=0;r--)if(e[r])for(n in t)e[r].prototype[n]=t[n]}},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}();if(!SVG.supported)return!1;SVG.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+),([\d\.]+)\)/,hsb:/hsb\((\d+),(\d+),(\d+),([\d\.]+)\)/,isHex:/^#/i,isRgb:/^rgb\(/,isHsb:/^hsb\(/},SVG.Color=function(e){var t;this.r=0,this.g=0,this.b=0,typeof e=="string"?SVG.regex.isRgb.test(e)?(t=SVG.regex.rgb.exec(e.replace(/\s/g,"")),this.r=parseInt(m[1]),this.g=parseInt(m[2]),this.b=parseInt(m[3])):SVG.regex.isHex.test(e)?(t=SVG.regex.hex.exec(this._fullHex(e)),this.r=parseInt(t[1],16),this.g=parseInt(t[2],16),this.b=parseInt(t[3],16)):SVG.regex.isHsb.test(e)&&(t=SVG.regex.hsb.exec(e.replace(/\s/g,"")),e=this._hsbToRgb(t[1],t[2],t[3])):typeof e=="object"&&(SVG.Color.isHsb(e)&&(e=this._hsbToRgb(e.h,e.s,e.b)),this.r=e.r,this.g=e.g,this.b=e.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 this.r/255*.3+this.g/255*.59+this.b/255*.11},_hsbToRgb:function(e,t,n){var i,s;e=parseInt(e)%360,e<0&&(e+=360),t=parseInt(t),t=t>100?100:t,n=parseInt(n),n=(n<0?0:n>100?100:n)*255/100,i=n*t/100,s=i*(e*256/60%256)/256;switch(Math.floor(e/60)){case 0:r=n,g=n-i+s,b=n-i;break;case 1:r=n-s,g=n,b=n-i;break;case 2:r=n-i,g=n,b=n-i+s;break;case 3:r=n-i,g=n-s,b=n;break;case 4:r=n-i+s,g=n-i,b=n;break;case 5:r=n,g=n-i,b=n-s}return{r:Math.floor(r+.5),g:Math.floor(g+.5),b:Math.floor(b+.5)}},_fullHex:function(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},_compToHex:function(e){var t=e.toString(16);return t.length==1?"0"+t:t}}),SVG.Color.test=function(e){return e+="",SVG.regex.isHex.test(e)||SVG.regex.isRgb.test(e)||SVG.regex.isHsb.test(e)},SVG.Color.isRgb=function(e){return typeof e.r=="number"},SVG.Color.isHsb=function(e){return typeof e.h=="number"},SVG.ViewBox=function(e){var t,n,r,i,s=e.bbox(),o=(e.attr("viewBox")||"").match(/[\d\.]+/g);this.x=s.x,this.y=s.y,this.width=e.node.offsetWidth||e.attr("width"),this.height=e.node.offsetHeight||e.attr("height"),o&&(t=parseFloat(o[0]),n=parseFloat(o[1]),r=parseFloat(o[2])-t,i=parseFloat(o[3])-n,this.zoom=this.width/this.height>r/i?this.height/i:this.width/r,this.x=t,this.y=n,this.width=r,this.height=i),this.zoom=this.zoom||1},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(e){var t=e.node.getBBox();this.x=t.x+e.trans.x,this.y=t.y+e.trans.y,this.cx=t.x+e.trans.x+t.width/2,this.cy=t.y+e.trans.y+t.height/2,this.width=t.width,this.height=t.height},SVG.Element=function(e){this.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},this.style={},this.trans={x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0};if(this.node=e)this.type=e.nodeName,this.attrs.id=e.getAttribute("id")},SVG.extend(SVG.Element,{move:function(e,t){return this.attr({x:e,y:t})},center:function(e,t){var n=this.bbox();return this.move(e-n.width/2,t-n.height/2)},size:function(e,t){return this.attr({width:e,height:t})},clone:function(){var e;if(this instanceof SVG.Wrap)e=this.parent[this.child.node.nodeName](),e.attrs=this.attrs,e.child.trans=this.child.trans,e.child.attr(this.child.attrs).transform({}),e.plot&&e.plot(this.child.attrs[this.child instanceof SVG.Path?"d":"points"]);else{var t=this.node.nodeName;e=t=="rect"?this.parent[t](this.attrs.width,this.attrs.height):t=="ellipse"?this.parent[t](this.attrs.rx*2,this.attrs.ry*2):t=="image"?this.parent[t](this.src):t=="text"?this.parent[t](this.content):t=="g"?this.parent.group():this.parent[t](),e.attr(this.attrs)}return e.trans=this.trans,e.transform({})},remove:function(){return this.parent&&this.parent.remove(this),this},doc:function(){return this._parent(SVG.Doc)},nested:function(){return this._parent(SVG.Nested)},attr:function(e,t,n){if(arguments.length<2){if(typeof e!="object")return this._isStyle(e)?e=="text"?this.content:e=="leading"?this[e]:this.style[e]:this.attrs[e];for(t in e)this.attr(t,e[t])}else if(t===null)this.node.removeAttribute(e);else{this.attrs[e]=t;if(e=="x"&&this._isText())for(var r=this.lines.length-1;r>=0;r--)this.lines[r].attr(e,t);else{e=="stroke-width"&&this.attr("stroke",parseFloat(t)>0?this.attrs.stroke:null);if(SVG.Color.test(t)||SVG.Color.isRgb(t)||SVG.Color.isHsb(t))t=(new SVG.Color(t)).toHex();n!=null?this.node.setAttributeNS(n,e,t):this.node.setAttribute(e,t)}this._isStyle(e)&&(e=="text"?this.text(t):e=="leading"?this[e]=t:this.style[e]=t,this.text(this.content))}return this},transform:function(e){if(typeof e=="string")return this.trans[e];var t,n=[];for(t in e)e[t]!=null&&(this.trans[t]=e[t]);return e=this.trans,e.rotation!=0&&n.push("rotate("+e.rotation+","+(e.cx!=null?e.cx:this.bbox().cx)+","+(e.cy!=null?e.cy:this.bbox().cy)+")"),n.push("scale("+e.scaleX+","+e.scaleY+")"),e.skewX!=0&&n.push("skewX("+e.skewX+")"),e.skewY!=0&&n.push("skewY("+e.skewY+")"),n.push("translate("+e.x+","+e.y+")"),this.attr("transform",n.join(" "))},data:function(e,t,n){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?t:JSON.stringify(t));return this},bbox:function(){return new SVG.BBox(this)},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.node.style.display="",this},hide:function(){return this.node.style.display="none",this},visible:function(){return this.node.style.display!="none"},_parent:function(e){var t=this;while(t!=null&&!(t instanceof e))t=t.parent;return t},_isStyle:function(e){return typeof e=="string"&&this._isText()?/^font|text|leading/.test(e):!1},_isText:function(){return this instanceof SVG.Text}}),SVG.Container=function(e){this.constructor.call(this,e)},SVG.Container.prototype=new SVG.Element,SVG.extend(SVG.Container,{add:function(e,t){if(!this.has(e)){t=t==null?this.children().length:t;if(e.parent){var n=e.parent.children().indexOf(e);e.parent.children().splice(n,1)}this.children().splice(t,0,e),this.node.insertBefore(e.node,this.node.childNodes[t]||null),e.parent=this}return this},put:function(e,t){return this.add(e,t),e},has:function(e){return this.children().indexOf(e)>=0},children:function(){return this._children||(this._children=[])},each:function(e){var t,n=this.children();for(t=0,length=n.length;t<length;t++)n[t]instanceof SVG.Shape&&e.apply(n[t],[t,n]);return this},remove:function(e){var t=this.children().indexOf(e);return this.children().splice(t,1),this.node.removeChild(e.node),e.parent=null,this},defs:function(){return this._defs||(this._defs=this.put(new SVG.Defs,0))},level:function(){return this.remove(this.defs()).put(this.defs(),0)},group:function(){return this.put(new SVG.G)},rect:function(e,t){return this.put((new SVG.Rect).size(e,t))},circle:function(e){return this.ellipse(e)},ellipse:function(e,t){return this.put((new SVG.Ellipse).size(e,t))},polyline:function(e){return this.put(new SVG.Wrap(new SVG.Polyline)).plot(e)},polygon:function(e){return this.put(new SVG.Wrap(new SVG.Polygon)).plot(e)},path:function(e){return this.put(new SVG.Wrap(new SVG.Path)).plot(e)},image:function(e,t,n){return t=t!=null?t:100,this.put((new SVG.Image).load(e).size(t,n!=null?n:t))},text:function(e){return this.put((new SVG.Text).text(e))},nested:function(){return this.put(new SVG.Nested)},gradient:function(e,t){return this.defs().gradient(e,t)},pattern:function(e,t,n){return this.defs().pattern(e,t,n)},mask:function(){return this.defs().put(new SVG.Mask)},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(){return arguments.length==0?new SVG.ViewBox(this):this.attr("viewBox",Array.prototype.slice.call(arguments).join(" "))},clear:function(){this._children=[];while(this.node.hasChildNodes())this.node.removeChild(this.node.lastChild);return this}}),SVG.FX=function(e){this.target=e},SVG.extend(SVG.FX,{animate:function(e,t){e=e==null?1e3:e,t=t||"<>";var n,r,i,s=this.target,o=this,u=(new Date).getTime(),a=u+e;return this.interval=setInterval(function(){var f,l=(new Date).getTime(),c=l>a?1:(l-u)/e;if(n==null){n=[];for(var h in o.attrs)n.push(h)}if(r==null){r=[];for(var h in o.trans)r.push(h);i={}}c=t=="<>"?-Math.cos(c*Math.PI)/2+.5:t==">"?Math.sin(c*Math.PI/2):t=="<"?-Math.cos(c*Math.PI/2)+1:t=="-"?c:typeof t=="function"?t(c):c,o._move?s.move(o._at(o._move.x,c),o._at(o._move.y,c)):o._center&&s.move(o._at(o._center.x,c),o._at(o._center.y,c)),o._size&&s.size(o._at(o._size.width,c),o._at(o._size.height,c));for(f=n.length-1;f>=0;f--)s.attr(n[f],o._at(o.attrs[n[f]],c));if(r.length>0){for(f=r.length-1;f>=0;f--)i[r[f]]=o._at(o.trans[r[f]],c);s.transform(i)}o._during&&o._during.call(s,c,function(e,t){return o._at({from:e,to:t},c)}),l>a&&(clearInterval(o.interval),o._after?o._after.apply(s,[o]):o.stop())},e>10?10:e),this},attr:function(e,t,n){if(typeof e=="object")for(var r in e)this.attr(r,e[r]);else this.attrs[e]={from:this.target.attr(e),to:t};return this},transform:function(e){for(var t in e)this.trans[t]={from:this.target.trans[t],to:e[t]};return this},move:function(e,t){var n=this.target.bbox();return this._move={x:{from:n.x,to:e},y:{from:n.y,to:t}},this},size:function(e,t){var n=this.target.bbox();return this._size={width:{from:n.width,to:e},height:{from:n.height,to:t}},this},center:function(e,t){var n=this.target.bbox();return this._move={x:{from:n.cx,to:e-n.width/2},y:{from:n.cy,to:t-n.height/2}},this},during:function(e){return this._during=e,this},after:function(e){return this._after=e,this},stop:function(){return clearInterval(this.interval),this.attrs={},this.trans={},this._move=null,this._size=null,this._after=null,this._during=null,this},_at:function(e,t){return typeof e.from=="number"?e.from+(e.to-e.from)*t:SVG.regex.unit.test(e.to)?this._unit(e,t):e.to&&(e.to.r||SVG.Color.test(e.to))?this._color(e,t):t<1?e.from:e.to},_unit:function(e,t){var n,r;return n=SVG.regex.unit.exec(e.from.toString()),r=parseFloat(n[1]),n=SVG.regex.unit.exec(e.to),r+(parseFloat(n[1])-r)*t+n[2]},_color:function(e,t){var n,r;return t=t<0?0:t>1?1:t,n=new SVG.Color(e.from),r=new SVG.Color(e.to),(new SVG.Color({r:~~(n.r+(r.r-n.r)*t),g:~~(n.g+(r.g-n.g)*t),b:~~(n.b+(r.b-n.b)*t)})).toHex()}}),SVG.extend(SVG.Element,{animate:function(e,t){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(e,t)},stop:function(){return this.fx.stop(),this}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchend","touchmove","touchcancel"].forEach(function(e){SVG.Element.prototype[e]=function(t){var n=this;return this.node["on"+e]=typeof t=="function"?function(){return t.apply(n,arguments)}:null,this}}),SVG.on=function(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent("on"+t,n)},SVG.off=function(e,t,n){e.removeEventListener?e.removeEventListener(t,n,!1):e.detachEvent("on"+t,n)},SVG.extend(SVG.Element,{on:function(e,t){return SVG.on(this.node,e,t),this},off:function(e,t){return SVG.off(this.node,e,t),this}}),SVG.G=function(){this.constructor.call(this,SVG.create("g"))},SVG.G.prototype=new SVG.Container,SVG.extend(SVG.G,{move:function(e,t){return this.transform({x:e,y:t})},defs:function(){return this.doc().defs()}}),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.remove(this).put(this,this.position()+1)},backward:function(){var e;return this.parent.level(),e=this.position(),e>1&&this.parent.remove(this).add(this,e-1),this},front:function(){return this.parent.remove(this).put(this)},back:function(){return this.parent.level(),this.position()>1&&this.parent.remove(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(e){return this.mask=e instanceof SVG.Mask?e:this.parent.mask().add(e),this.attr("mask","url(#"+this.mask.attr("id")+")")}}),SVG.Pattern=function(e){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(e,t,n){var r=this.put(new SVG.Pattern);return n(r),r.attr({x:0,y:0,width:e,height:t,patternUnits:"userSpaceOnUse"})}}),SVG.Gradient=function(e){this.constructor.call(this,SVG.create(e+"Gradient")),this.type=e},SVG.Gradient.prototype=new SVG.Container,SVG.extend(SVG.Gradient,{from:function(e,t){return this.type=="radial"?this.attr({fx:e+"%",fy:t+"%"}):this.attr({x1:e+"%",y1:t+"%"})},to:function(e,t){return this.type=="radial"?this.attr({cx:e+"%",cy:t+"%"}):this.attr({x2:e+"%",y2:t+"%"})},radius:function(e){return this.type=="radial"?this.attr({r:e+"%"}):this},at:function(e){return this.put(new SVG.Stop(e))},update:function(e){while(this.node.hasChildNodes())this.node.removeChild(this.node.lastChild);return e(this),this},fill:function(){return"url(#"+this.attr("id")+")"}}),SVG.extend(SVG.Defs,{gradient:function(e,t){var n=this.put(new SVG.Gradient(e));return t(n),n}}),SVG.Stop=function(e){this.constructor.call(this,SVG.create("stop")),this.update(e)},SVG.Stop.prototype=new SVG.Element,SVG.extend(SVG.Stop,{update:function(e){var t,n="",r=["opacity","color"];for(t=r.length-1;t>=0;t--)e[r[t]]!=null&&(n+="stop-"+r[t]+":"+e[r[t]]+";");return this.attr({offset:(e.offset!=null?e.offset:this.attrs.offset||0)+"%",style:n})}}),SVG.Doc=function(e){this.constructor.call(this,SVG.create("svg")),this.parent=typeof e=="string"?document.getElementById(e):e,this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xlink",SVG.xlink,SVG.ns).defs(),this.stage()},SVG.Doc.prototype=new SVG.Container,SVG.Doc.prototype.stage=function(){var e,t=this,n=document.createElement("div");return n.style.cssText="position:relative;height:100%;",t.parent.appendChild(n),n.appendChild(t.node),e=function(){document.readyState==="complete"?(t.attr("style","position:absolute;"),setTimeout(function(){t.attr("style","position:relative;"),t.parent.removeChild(t.node.parentNode),t.node.parentNode.removeChild(t.node),t.parent.appendChild(t.node)},5)):setTimeout(e,10)},e(),this},SVG.Shape=function(e){this.constructor.call(this,e)},SVG.Shape.prototype=new SVG.Element,SVG.Wrap=function(e){this.constructor.call(this,SVG.create("g")),this.node.insertBefore(e.node,null),this.child=e,this.type=e.node.nodeName},SVG.Wrap.prototype=new SVG.Shape,SVG.extend(SVG.Wrap,{move:function(e,t){return this.transform({x:e,y:t})},size:function(e,t){var n=e/this._b.width;return this.child.transform({scaleX:n,scaleY:t!=null?t/this._b.height:n}),this},center:function(e,t){return this.move(e+this._b.width*this.child.trans.scaleX/-2,t+this._b.height*this.child.trans.scaleY/-2)},attr:function(e,t,n){if(typeof e=="object")for(t in e)this.attr(t,e[t]);else{if(arguments.length<2)return e=="transform"?this.attrs[e]:this.child.attrs[e];e=="transform"?(this.attrs[e]=t,n!=null?this.node.setAttributeNS(n,e,t):this.node.setAttribute(e,t)):this.child.attr(e,t,n)}return this},plot:function(e){return this.child.plot(e),this._b=this.child.bbox(),this.child.transform({x:-this._b.x,y:-this._b.y}),this}}),SVG.Rect=function(){this.constructor.call(this,SVG.create("rect"))},SVG.Rect.prototype=new SVG.Shape,SVG.Ellipse=function(){this.constructor.call(this,SVG.create("ellipse"))},SVG.Ellipse.prototype=new SVG.Shape,SVG.extend(SVG.Ellipse,{move:function(e,t){return this.attrs.x=e,this.attrs.y=t,this.center()},size:function(e,t){return this.attr({rx:e/2,ry:(t!=null?t:e)/2}).center()},center:function(e,t){return this.attr({cx:e||(this.attrs.x||0)+(this.attrs.rx||0),cy:t||(this.attrs.y||0)+(this.attrs.ry||0)})}}),SVG.Line=function(){this.constructor.call(this,SVG.create("line"))},SVG.Line.prototype=new SVG.Shape,SVG.extend(SVG.Line,{move:function(e,t){var n=this.bbox();return this.attr({x1:this.attr("x1")-n.x+e,y1:this.attr("y1")-n.y+t,x2:this.attr("x2")-n.x+e,y2:this.attr("y2")-n.y+t})},center:function(e,t){var n=this.bbox();return this.move(e-n.width/2,t-n.height/2)},size:function(e,t){var n=this.bbox();return this.attr(this.attr("x1")<this.attr("x2")?"x2":"x1",n.x+e),this.attr(this.attr("y1")<this.attr("y2")?"y2":"y1",n.y+t)}}),SVG.extend(SVG.Container,{line:function(e,t,n,r){return this.put((new SVG.Line).attr({x1:e,y1:t,x2:n,y2:r}))}}),SVG.Poly={plot:function(e){return this.attr("points",e||"0,0"),this}},SVG.Polyline=function(){this.constructor.call(this,SVG.create("polyline"))},SVG.Polyline.prototype=new SVG.Shape,SVG.extend(SVG.Polyline,SVG.Poly),SVG.Polygon=function(){this.constructor.call(this,SVG.create("polygon"))},SVG.Polygon.prototype=new SVG.Shape,SVG.extend(SVG.Polygon,SVG.Poly),SVG.Path=function(){this.constructor.call(this,SVG.create("path"))},SVG.Path.prototype=new SVG.Shape,SVG.extend(SVG.Path,{move:function(e,t){this.transform({x:e,y:t})},plot:function(e){return this.attr("d",e||"M0,0")}}),SVG.Image=function(){this.constructor.call(this,SVG.create("image"))},SVG.Image.prototype=new SVG.Shape,SVG.extend(SVG.Image,{load:function(e){return this.src=e,e?this.attr("xlink:href",e,SVG.xlink):this}});var e=["size","family","weight","stretch","variant","style"];SVG.Text=function(){this.constructor.call(this,SVG.create("text")),this.style={"font-size":16,"font-family":"Helvetica","text-anchor":"start"},this.leading=1.2},SVG.Text.prototype=new SVG.Shape,SVG.extend(SVG.Text,{text:function(e){this.content=e=e||"text",this.lines=[];var t,n,r,i=this._style(),s=this.doc(),o=e.split("\n"),u=this.style["font-size"];while(this.node.hasChildNodes())this.node.removeChild(this.node.lastChild);for(t=0,n=o.length;t<n;t++)r=(new SVG.TSpan).text(o[t]).attr({dy:u*this.leading-(t==0?u*.3:0),x:this.attrs.x||0,style:i}),this.node.appendChild(r.node),this.lines.push(r);return this.attr("style",i)},_style:function(){var t,n="";for(t=e.length-1;t>=0;t--)this.style["font-"+e[t]]!=null&&(n+="font-"+e[t]+":"+this.style["font-"+e[t]]+";");return n+="text-anchor:"+this.style["text-anchor"]+";",n}}),SVG.TSpan=function(){this.constructor.call(this,SVG.create("tspan"))},SVG.TSpan.prototype=new SVG.Shape,SVG.extend(SVG.TSpan,{text:function(e){return this.node.appendChild(document.createTextNode(e)),this}}),SVG.Nested=function(){this.constructor.call(this,SVG.create("svg")),this.attr("style","overflow:visible")},SVG.Nested.prototype=new SVG.Container,SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var t=function(e,t){return t=="color"?e:e+"-"+t};["fill","stroke"].forEach(function(e){var n={};n[e]=function(n){var r;if(typeof n=="string"||SVG.Color.isRgb(n)||SVG.Color.isHsb(n))this.attr(e,n);else for(index=SVG["_"+e].length-1;index>=0;index--)n[SVG["_"+e][index]]!=null&&this.attr(t(e,SVG["_"+e][index]),n[SVG["_"+e][index]]);return this},SVG.extend(SVG.Shape,SVG.FX,n)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(e,t,n){return this.transform({rotation:e||0,cx:t,cy:n})},skew:function(e,t){return this.transform({skewX:e||0,skewY:t||0})},scale:function(e,t){return this.transform({scaleX:e,scaleY:t==null?e:t})},opacity:function(e){return this.attr("opacity",e)}}),SVG.Text&&SVG.extend(SVG.Text,{font:function(t){var n,r={};for(n in t)n=="leading"?r[n]=t[n]:n=="anchor"?r["text-anchor"]=t[n]:e.indexOf(n)>-1?r["font-"+n]=t[n]:void 0;return this.attr(r).text(this.content)}})}).call(this);
\ No newline at end of file +/* svg.js v0.9 - svg regex color viewbox bbox element container fx event group arrange defs mask pattern gradient doc shape wrap rect ellipse line poly path image text nested sugar - svgjs.com/license */ +(function(){this.SVG=function(e){if(SVG.supported)return new SVG.Doc(e)},this.svg=function(e){return console.warn("WARNING: svg() is deprecated, please use SVG() instead."),SVG(e)},SVG.ns="http://www.w3.org/2000/svg",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(e){return"Svgjs"+e.charAt(0).toUpperCase()+e.slice(1)+"Element"+SVG.did++},SVG.create=function(e){var t=document.createElementNS(this.ns,e);return t.setAttribute("id",this.eid(e)),t},SVG.extend=function(){var e,t,n,r;e=Array.prototype.slice.call(arguments),t=e.pop();for(r=e.length-1;r>=0;r--)if(e[r])for(n in t)e[r].prototype[n]=t[n]},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}();if(!SVG.supported)return!1;SVG.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+),([\d\.]+)\)/,hsb:/hsb\((\d+),(\d+),(\d+),([\d\.]+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isHsb:/^hsb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/},SVG.Color=function(e){var t;this.r=0,this.g=0,this.b=0,typeof e=="string"?SVG.regex.isRgb.test(e)?(t=SVG.regex.rgb.exec(e.replace(/\s/g,"")),this.r=parseInt(m[1]),this.g=parseInt(m[2]),this.b=parseInt(m[3])):SVG.regex.isHex.test(e)?(t=SVG.regex.hex.exec(this._fullHex(e)),this.r=parseInt(t[1],16),this.g=parseInt(t[2],16),this.b=parseInt(t[3],16)):SVG.regex.isHsb.test(e)&&(t=SVG.regex.hsb.exec(e.replace(/\s/g,"")),e=this._hsbToRgb(t[1],t[2],t[3])):typeof e=="object"&&(SVG.Color.isHsb(e)&&(e=this._hsbToRgb(e.h,e.s,e.b)),this.r=e.r,this.g=e.g,this.b=e.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 this.r/255*.3+this.g/255*.59+this.b/255*.11},_hsbToRgb:function(e,t,n){var i,s;e=parseInt(e)%360,e<0&&(e+=360),t=parseInt(t),t=t>100?100:t,n=parseInt(n),n=(n<0?0:n>100?100:n)*255/100,i=n*t/100,s=i*(e*256/60%256)/256;switch(Math.floor(e/60)){case 0:r=n,g=n-i+s,b=n-i;break;case 1:r=n-s,g=n,b=n-i;break;case 2:r=n-i,g=n,b=n-i+s;break;case 3:r=n-i,g=n-s,b=n;break;case 4:r=n-i+s,g=n-i,b=n;break;case 5:r=n,g=n-i,b=n-s}return{r:Math.floor(r+.5),g:Math.floor(g+.5),b:Math.floor(b+.5)}},_fullHex:function(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},_compToHex:function(e){var t=e.toString(16);return t.length==1?"0"+t:t}}),SVG.Color.test=function(e){return e+="",SVG.regex.isHex.test(e)||SVG.regex.isRgb.test(e)||SVG.regex.isHsb.test(e)},SVG.Color.isRgb=function(e){return typeof e.r=="number"},SVG.Color.isHsb=function(e){return typeof e.h=="number"},SVG.ViewBox=function(e){var t,n,r,i,s=e.bbox(),o=(e.attr("viewBox")||"").match(/[\d\.]+/g);this.x=s.x,this.y=s.y,this.width=e.node.offsetWidth||e.attr("width"),this.height=e.node.offsetHeight||e.attr("height"),o&&(t=parseFloat(o[0]),n=parseFloat(o[1]),r=parseFloat(o[2])-t,i=parseFloat(o[3])-n,this.zoom=this.width/this.height>r/i?this.height/i:this.width/r,this.x=t,this.y=n,this.width=r,this.height=i),this.zoom=this.zoom||1},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(e){var t=e.node.getBBox();this.x=t.x+e.trans.x,this.y=t.y+e.trans.y,this.cx=t.x+e.trans.x+t.width/2,this.cy=t.y+e.trans.y+t.height/2,this.width=t.width,this.height=t.height},SVG.Element=function(e){this.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},this.styles={},this.trans={x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0};if(this.node=e)this.type=e.nodeName,this.attrs.id=e.getAttribute("id")},SVG.extend(SVG.Element,{move:function(e,t){return this.attr({x:e,y:t})},center:function(e,t){var n=this.bbox();return this.move(e-n.width/2,t-n.height/2)},size:function(e,t){return this.attr({width:e,height:t})},clone:function(){var e;if(this instanceof SVG.Wrap)e=this.parent[this.child.node.nodeName](),e.attrs=this.attrs,e.child.trans=this.child.trans,e.child.attr(this.child.attrs).transform({}),e.plot&&e.plot(this.child.attrs[this.child instanceof SVG.Path?"d":"points"]);else{var t=this.node.nodeName;e=t=="rect"?this.parent[t](this.attrs.width,this.attrs.height):t=="ellipse"?this.parent[t](this.attrs.rx*2,this.attrs.ry*2):t=="image"?this.parent[t](this.src):t=="text"?this.parent[t](this.content):t=="g"?this.parent.group():this.parent[t](),e.attr(this.attrs)}return e.trans=this.trans,e.transform({})},remove:function(){return this.parent&&this.parent.remove(this),this},doc:function(){return this._parent(SVG.Doc)},nested:function(){return this._parent(SVG.Nested)},attr:function(e,t,n){if(arguments.length<2){if(typeof e!="object")return this._isStyle(e)?e=="text"?this.content:e=="leading"?this.leading():this.style(e):this.attrs[e]||this.node.getAttribute(e);for(t in e)this.attr(t,e[t])}else if(t===null)this.node.removeAttribute(e);else{if(e=="style")return this.style(t);this.attrs[e]=t;if(e=="x"&&this._isText())for(var r=this.lines.length-1;r>=0;r--)this.lines[r].attr(e,t);else{e=="stroke-width"&&this.attr("stroke",parseFloat(t)>0?this.attrs.stroke:null);if(SVG.Color.test(t)||SVG.Color.isRgb(t)||SVG.Color.isHsb(t))t=(new SVG.Color(t)).toHex();n!=null?this.node.setAttributeNS(n,e,t):this.node.setAttribute(e,t)}this._isStyle(e)&&(e=="text"?this.text(t):e=="leading"?this.leading(t):this.style(e,t),this.rebuild&&this.rebuild())}return this},transform:function(e,t){if(typeof e=="string"){if(arguments.length<2)return this.trans[e];var n={};return n[e]=t,this.transform(n)}var n=[];for(t in e)e[t]!=null&&(this.trans[t]=e[t]);return e=this.trans,e.rotation!=0&&n.push("rotate("+e.rotation+","+(e.cx!=null?e.cx:this.bbox().cx)+","+(e.cy!=null?e.cy:this.bbox().cy)+")"),n.push("scale("+e.scaleX+","+e.scaleY+")"),e.skewX!=0&&n.push("skewX("+e.skewX+")"),e.skewY!=0&&n.push("skewY("+e.skewY+")"),n.push("translate("+e.x+","+e.y+")"),this.attr("transform",n.join(" "))},style:function(e,t){if(arguments.length==0)return this.attr("style");if(arguments.length<2)if(typeof e=="object")for(t in e)this.style(t,e[t]);else{if(!SVG.regex.isCss.test(e))return this.styles[e];e=e.split(";");for(var n=0;n<e.length;n++)t=e[n].split(":"),t.length==2&&this.style(t[0].replace(/\s+/g,""),t[1].replace(/^\s+/,"").replace(/\s+$/,""))}else t===null?delete this.styles[e]:this.styles[e]=t;e="";for(t in this.styles)e+=t+":"+this.styles[t]+";";return this.node.setAttribute("style",e),this},data:function(e,t,n){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?t:JSON.stringify(t));return this},bbox:function(){return new SVG.BBox(this)},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"},_parent:function(e){var t=this;while(t!=null&&!(t instanceof e))t=t.parent;return t},_isStyle:function(e){return typeof e=="string"?SVG.regex.isStyle.test(e):!1},_isText:function(){return this instanceof SVG.Text}}),SVG.Container=function(e){this.constructor.call(this,e)},SVG.Container.prototype=new SVG.Element,SVG.extend(SVG.Container,{add:function(e,t){if(!this.has(e)){t=t==null?this.children().length:t;if(e.parent){var n=e.parent.children().indexOf(e);e.parent.children().splice(n,1)}this.children().splice(t,0,e),this.node.insertBefore(e.node,this.node.childNodes[t]||null),e.parent=this}return this},put:function(e,t){return this.add(e,t),e},has:function(e){return this.children().indexOf(e)>=0},children:function(){return this._children||(this._children=[])},each:function(e){var t,n=this.children();for(t=0,length=n.length;t<length;t++)n[t]instanceof SVG.Shape&&e.apply(n[t],[t,n]);return this},remove:function(e){var t=this.children().indexOf(e);return this.children().splice(t,1),this.node.removeChild(e.node),e.parent=null,this},defs:function(){return this._defs||(this._defs=this.put(new SVG.Defs,0))},level:function(){return this.remove(this.defs()).put(this.defs(),0)},group:function(){return this.put(new SVG.G)},rect:function(e,t){return this.put((new SVG.Rect).size(e,t))},circle:function(e){return this.ellipse(e)},ellipse:function(e,t){return this.put((new SVG.Ellipse).size(e,t))},polyline:function(e){return this.put(new SVG.Wrap(new SVG.Polyline)).plot(e)},polygon:function(e){return this.put(new SVG.Wrap(new SVG.Polygon)).plot(e)},path:function(e){return this.put(new SVG.Wrap(new SVG.Path)).plot(e)},image:function(e,t,n){return t=t!=null?t:100,this.put((new SVG.Image).load(e).size(t,n!=null?n:t))},text:function(e){return this.put((new SVG.Text).text(e))},nested:function(){return this.put(new SVG.Nested)},gradient:function(e,t){return this.defs().gradient(e,t)},pattern:function(e,t,n){return this.defs().pattern(e,t,n)},mask:function(){return this.defs().put(new SVG.Mask)},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(){return arguments.length==0?new SVG.ViewBox(this):this.attr("viewBox",Array.prototype.slice.call(arguments).join(" "))},clear:function(){this._children=[];while(this.node.hasChildNodes())this.node.removeChild(this.node.lastChild);return this}}),SVG.FX=function(e){this.target=e},SVG.extend(SVG.FX,{animate:function(e,t){e=e==null?1e3:e,t=t||"<>";var n,r,i,s=this.target,o=this,u=(new Date).getTime(),a=u+e;return this.interval=setInterval(function(){var f,l,c=(new Date).getTime(),h=c>a?1:(c-u)/e;if(n==null){n=[];for(l in o.attrs)n.push(l)}if(r==null){r=[];for(l in o.trans)r.push(l)}if(i==null){i=[];for(l in o.styles)i.push(l)}h=t=="<>"?-Math.cos(h*Math.PI)/2+.5:t==">"?Math.sin(h*Math.PI/2):t=="<"?-Math.cos(h*Math.PI/2)+1:t=="-"?h:typeof t=="function"?t(h):h,o._move?s.move(o._at(o._move.x,h),o._at(o._move.y,h)):o._center&&s.center(o._at(o._center.x,h),o._at(o._center.y,h)),o._size&&s.size(o._at(o._size.width,h),o._at(o._size.height,h));for(f=n.length-1;f>=0;f--)s.attr(n[f],o._at(o.attrs[n[f]],h));for(f=r.length-1;f>=0;f--)s.transform(r[f],o._at(o.trans[r[f]],h));for(f=i.length-1;f>=0;f--)s.style(i[f],o._at(o.styles[i[f]],h));o._during&&o._during.call(s,h,function(e,t){return o._at({from:e,to:t},h)}),c>a&&(clearInterval(o.interval),o._after?o._after.apply(s,[o]):o.stop())},e>10?10:e),this},attr:function(e,t,n){if(typeof e=="object")for(var r in e)this.attr(r,e[r]);else this.attrs[e]={from:this.target.attr(e),to:t};return this},transform:function(e,t){for(var n in e)this.trans[n]={from:this.target.trans[n],to:e[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},move:function(e,t){var n=this.target.bbox();return this._move={x:{from:n.x,to:e},y:{from:n.y,to:t}},this},size:function(e,t){if(this.target instanceof SVG.Text)this.attr("font-size",e);else{var n=this.target.bbox();this._size={width:{from:n.width,to:e},height:{from:n.height,to:t}}}return this},center:function(e,t){var n=this.target.bbox();return this._move={x:{from:n.cx,to:e-n.width/2},y:{from:n.cy,to:t-n.height/2}},this},during:function(e){return this._during=e,this},after:function(e){return this._after=e,this},stop:function(){return clearInterval(this.interval),this.attrs={},this.trans={},this.styles={},this._move=null,this._size=null,this._after=null,this._during=null,this},_at:function(e,t){return typeof e.from=="number"?e.from+(e.to-e.from)*t:SVG.regex.unit.test(e.to)?this._unit(e,t):e.to&&(e.to.r||SVG.Color.test(e.to))?this._color(e,t):t<1?e.from:e.to},_unit:function(e,t){var n,r;return n=SVG.regex.unit.exec(e.from.toString()),r=parseFloat(n[1]),n=SVG.regex.unit.exec(e.to),r+(parseFloat(n[1])-r)*t+n[2]},_color:function(e,t){var n,r;return t=t<0?0:t>1?1:t,n=new SVG.Color(e.from),r=new SVG.Color(e.to),(new SVG.Color({r:~~(n.r+(r.r-n.r)*t),g:~~(n.g+(r.g-n.g)*t),b:~~(n.b+(r.b-n.b)*t)})).toHex()}}),SVG.extend(SVG.Element,{animate:function(e,t){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(e,t)},stop:function(){return this.fx.stop(),this}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchend","touchmove","touchcancel"].forEach(function(e){SVG.Element.prototype[e]=function(t){var n=this;return this.node["on"+e]=typeof t=="function"?function(){return t.apply(n,arguments)}:null,this}}),SVG.on=function(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent("on"+t,n)},SVG.off=function(e,t,n){e.removeEventListener?e.removeEventListener(t,n,!1):e.detachEvent("on"+t,n)},SVG.extend(SVG.Element,{on:function(e,t){return SVG.on(this.node,e,t),this},off:function(e,t){return SVG.off(this.node,e,t),this}}),SVG.G=function(){this.constructor.call(this,SVG.create("g"))},SVG.G.prototype=new SVG.Container,SVG.extend(SVG.G,{move:function(e,t){return this.transform({x:e,y:t})},defs:function(){return this.doc().defs()}}),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.remove(this).put(this,this.position()+1)},backward:function(){this.parent.level();var e=this.position();return e>1&&this.parent.remove(this).add(this,e-1),this},front:function(){return this.parent.remove(this).put(this)},back:function(){return this.parent.level(),this.position()>1&&this.parent.remove(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(e){return this.mask=e instanceof SVG.Mask?e:this.parent.mask().add(e),this.attr("mask","url(#"+this.mask.attr("id")+")")}}),SVG.Pattern=function(e){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(e,t,n){var r=this.put(new SVG.Pattern);return n(r),r.attr({x:0,y:0,width:e,height:t,patternUnits:"userSpaceOnUse"})}}),SVG.Gradient=function(e){this.constructor.call(this,SVG.create(e+"Gradient")),this.type=e},SVG.Gradient.prototype=new SVG.Container,SVG.extend(SVG.Gradient,{from:function(e,t){return this.type=="radial"?this.attr({fx:e+"%",fy:t+"%"}):this.attr({x1:e+"%",y1:t+"%"})},to:function(e,t){return this.type=="radial"?this.attr({cx:e+"%",cy:t+"%"}):this.attr({x2:e+"%",y2:t+"%"})},radius:function(e){return this.type=="radial"?this.attr({r:e+"%"}):this},at:function(e){return this.put(new SVG.Stop(e))},update:function(e){while(this.node.hasChildNodes())this.node.removeChild(this.node.lastChild);return e(this),this},fill:function(){return"url(#"+this.attr("id")+")"}}),SVG.extend(SVG.Defs,{gradient:function(e,t){var n=this.put(new SVG.Gradient(e));return t(n),n}}),SVG.Stop=function(e){this.constructor.call(this,SVG.create("stop")),this.update(e)},SVG.Stop.prototype=new SVG.Element,SVG.extend(SVG.Stop,{update:function(e){var t,n=["opacity","color"];for(t=n.length-1;t>=0;t--)e[n[t]]!=null&&this.style("stop-"+n[t],e[n[t]]);return this.attr("offset",(e.offset!=null?e.offset:this.attrs.offset||0)+"%")}}),SVG.Doc=function(e){this.constructor.call(this,SVG.create("svg")),this.parent=typeof e=="string"?document.getElementById(e):e,this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xlink",SVG.xlink,SVG.ns).defs(),this.stage()},SVG.Doc.prototype=new SVG.Container,SVG.Doc.prototype.stage=function(){var e,t=this,n=document.createElement("div");return n.style.cssText="position:relative;height:100%;",t.parent.appendChild(n),n.appendChild(t.node),e=function(){document.readyState==="complete"?(t.style("position:absolute;"),setTimeout(function(){t.style("position:relative;"),t.parent.removeChild(t.node.parentNode),t.node.parentNode.removeChild(t.node),t.parent.appendChild(t.node)},5)):setTimeout(e,10)},e(),this},SVG.Shape=function(e){this.constructor.call(this,e)},SVG.Shape.prototype=new SVG.Element,SVG.Wrap=function(e){this.constructor.call(this,SVG.create("g")),this.node.insertBefore(e.node,null),this.child=e,this.type=e.node.nodeName},SVG.Wrap.prototype=new SVG.Shape,SVG.extend(SVG.Wrap,{move:function(e,t){return this.transform({x:e,y:t})},size:function(e,t){var n=e/this._b.width;return this.child.transform({scaleX:n,scaleY:t!=null?t/this._b.height:n}),this},center:function(e,t){return this.move(e+this._b.width*this.child.trans.scaleX/-2,t+this._b.height*this.child.trans.scaleY/-2)},attr:function(e,t,n){if(typeof e=="object")for(t in e)this.attr(t,e[t]);else{if(arguments.length<2)return e=="transform"?this.attrs[e]:this.child.attrs[e];e=="transform"?(this.attrs[e]=t,n!=null?this.node.setAttributeNS(n,e,t):this.node.setAttribute(e,t)):this.child.attr(e,t,n)}return this},plot:function(e){return this.child.plot(e),this._b=this.child.bbox(),this.child.transform({x:-this._b.x,y:-this._b.y}),this}}),SVG.Rect=function(){this.constructor.call(this,SVG.create("rect"))},SVG.Rect.prototype=new SVG.Shape,SVG.Ellipse=function(){this.constructor.call(this,SVG.create("ellipse"))},SVG.Ellipse.prototype=new SVG.Shape,SVG.extend(SVG.Ellipse,{move:function(e,t){return this.attrs.x=e,this.attrs.y=t,this.center()},size:function(e,t){return this.attr({rx:e/2,ry:(t!=null?t:e)/2}).center()},center:function(e,t){return this.attr({cx:e!=null?e:(this.attrs.x||0)+(this.attrs.rx||0),cy:t!=null?t:(this.attrs.y||0)+(this.attrs.ry||0)})}}),SVG.Line=function(){this.constructor.call(this,SVG.create("line"))},SVG.Line.prototype=new SVG.Shape,SVG.extend(SVG.Line,{move:function(e,t){var n=this.bbox();return this.attr({x1:this.attr("x1")-n.x+e,y1:this.attr("y1")-n.y+t,x2:this.attr("x2")-n.x+e,y2:this.attr("y2")-n.y+t})},center:function(e,t){var n=this.bbox();return this.move(e-n.width/2,t-n.height/2)},size:function(e,t){var n=this.bbox();return this.attr(this.attr("x1")<this.attr("x2")?"x2":"x1",n.x+e).attr(this.attr("y1")<this.attr("y2")?"y2":"y1",n.y+t)}}),SVG.extend(SVG.Container,{line:function(e,t,n,r){return this.put((new SVG.Line).attr({x1:e,y1:t,x2:n,y2:r}))}}),SVG.Poly={plot:function(e){return this.attr("points",e||"0,0"),this}},SVG.Polyline=function(){this.constructor.call(this,SVG.create("polyline"))},SVG.Polyline.prototype=new SVG.Shape,SVG.extend(SVG.Polyline,SVG.Poly),SVG.Polygon=function(){this.constructor.call(this,SVG.create("polygon"))},SVG.Polygon.prototype=new SVG.Shape,SVG.extend(SVG.Polygon,SVG.Poly),SVG.Path=function(){this.constructor.call(this,SVG.create("path"))},SVG.Path.prototype=new SVG.Shape,SVG.extend(SVG.Path,{move:function(e,t){this.transform({x:e,y:t})},plot:function(e){return this.attr("d",e||"M0,0")}}),SVG.Image=function(){this.constructor.call(this,SVG.create("image"))},SVG.Image.prototype=new SVG.Shape,SVG.extend(SVG.Image,{load:function(e){return this.src=e,e?this.attr("xlink:href",e,SVG.xlink):this}});var e="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","text-anchor":"start"},this._leading=1.2},SVG.Text.prototype=new SVG.Shape,SVG.extend(SVG.Text,{text:function(e){if(e==null)return this.content;this.clear(),this.content=SVG.regex.isBlank.test(e)?"text":e;var t,n,r=e.split("\n");for(t=0,n=r.length;t<n;t++)this.tspan(r[t]);return this.attr("style",this.style())},tspan:function(e){var t=(new SVG.TSpan).text(e);return this.node.appendChild(t.node),this.lines.push(t),t.attr("style",this.style())},center:function(e,t){var n=this.style("text-anchor"),r=this.bbox(),e=n=="start"?e-r.width/2:n=="end"?e+r.width/2:e;return this.move(e,t-r.height/2)},size:function(e){return this.attr("font-size",e)},leading:function(e){return e==null?this._leading:(this._leading=e,this.rebuild())},rebuild:function(){var e,t,n=this.styles["font-size"];for(e=0,t=this.lines.length;e<t;e++)this.lines[e].attr({dy:n*this._leading-(e==0?n*.3:0),x:this.attrs.x||0,style:this.style()});return this},clear:function(){while(this.node.hasChildNodes())this.node.removeChild(this.node.lastChild);return this.lines=[],this}}),SVG.TSpan=function(){this.constructor.call(this,SVG.create("tspan"))},SVG.TSpan.prototype=new SVG.Shape,SVG.extend(SVG.TSpan,{text:function(e){return this.node.appendChild(document.createTextNode(e)),this}}),SVG.Nested=function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},SVG.Nested.prototype=new SVG.Container,SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var t=function(e,t){return t=="color"?e:e+"-"+t};["fill","stroke"].forEach(function(e){var n={};n[e]=function(n){var r;if(typeof n=="string"||SVG.Color.isRgb(n)||SVG.Color.isHsb(n))this.attr(e,n);else for(index=SVG["_"+e].length-1;index>=0;index--)n[SVG["_"+e][index]]!=null&&this.attr(t(e,SVG["_"+e][index]),n[SVG["_"+e][index]]);return this},SVG.extend(SVG.Shape,SVG.FX,n)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(e,t,n){return this.transform({rotation:e||0,cx:t,cy:n})},skew:function(e,t){return this.transform({skewX:e||0,skewY:t||0})},scale:function(e,t){return this.transform({scaleX:e,scaleY:t==null?e:t})},opacity:function(e){return this.attr("opacity",e)}}),SVG.Text&&SVG.extend(SVG.Text,SVG.FX,{font:function(t){for(var n in t)n=="anchor"?this.attr("text-anchor",t[n]):e.indexOf(n)>-1?this.attr("font-"+n,t[n]):this.attr(n,t[n]);return this}})}).call(this);
\ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..a9070d3 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "svg.js" +, "description": "A lightweight library for manipulating and animating SVG." +, "url": "http://svgjs.com" +, "homepage": "http://svgjs.com" +, "keywords": ["svg", "vector", "graphics", "animation"] +, "author": "Wout Fierens <wout@impinc.co.uk>" +, "main": "dist/svg.js" +, "version": "v0.9" +, "jam": { + "include": [ + "dist/svg.js" + , "README.md" + , "MIT-LICENSE" + ] + } +, "maintainers": [ + { + "name": "Wout Fierens" + , "email": "wout@impinc.co.uk" + , "web": "http://impinc.co.uk" + } + ] +, "licenses": [ + { + "type": "MIT" + , "url": "http://www.opensource.org/licenses/mit-license.php" + } + ] +, "repositories": [ + { + "type": "git" + , "url": "https://github.com/wout/svg.js.git" + } + ] +, "github": "https://github.com/wout/svg.js" +}
\ No newline at end of file diff --git a/src/arrange.js b/src/arrange.js index 47134ed..3ef486f 100644 --- a/src/arrange.js +++ b/src/arrange.js @@ -24,11 +24,9 @@ SVG.extend(SVG.Element, { } // Send given element one step backward , backward: function() { - var i - this.parent.level() - i = this.position() + var i = this.position() if (i > 1) this.parent.remove(this).add(this, i - 1) diff --git a/src/bbox.js b/src/bbox.js index d70ac68..6b9885a 100644 --- a/src/bbox.js +++ b/src/bbox.js @@ -8,7 +8,7 @@ SVG.BBox = function(element) { this.y = box.y + element.trans.y /* add the center */ - this.cx = box.x + element.trans.x + box.width / 2 + this.cx = box.x + element.trans.x + box.width / 2 this.cy = box.y + element.trans.y + box.height / 2 /* plain width and height */ diff --git a/src/color.js b/src/color.js index dde2c65..89c811d 100644 --- a/src/color.js +++ b/src/color.js @@ -42,7 +42,6 @@ SVG.Color = function(color) { this.r = color.r this.g = color.g this.b = color.b - } } @@ -26,9 +26,9 @@ SVG.Doc.prototype = new SVG.Container // Basically it sets the position of the svg node to absolute // when the dom is loaded, and resets it to relative a few milliseconds later. SVG.Doc.prototype.stage = function() { - var check, - element = this, - wrapper = document.createElement('div') + var check + , element = this + , wrapper = document.createElement('div') /* set temp wrapper to position relative */ wrapper.style.cssText = 'position:relative;height:100%;' @@ -40,10 +40,10 @@ SVG.Doc.prototype.stage = function() { /* check for dom:ready */ check = function() { if (document.readyState === 'complete') { - element.attr('style', 'position:absolute;') + element.style('position:absolute;') setTimeout(function() { /* set position back to relative */ - element.attr('style', 'position:relative;') + element.style('position:relative;') /* remove temp wrapper */ element.parent.removeChild(element.node.parentNode) diff --git a/src/element.js b/src/element.js index e11f650..2e7ebb2 100644 --- a/src/element.js +++ b/src/element.js @@ -23,7 +23,7 @@ SVG.Element = function(node) { } /* initialize style store */ - this.style = {} + this.styles = {} /* initialize transformation store with defaults */ this.trans = { @@ -112,6 +112,7 @@ SVG.extend(SVG.Element, { , remove: function() { if (this.parent) this.parent.remove(this) + return this } // Get parent document @@ -127,24 +128,29 @@ SVG.extend(SVG.Element, { if (arguments.length < 2) { /* apply every attribute individually if an object is passed */ if (typeof a == 'object') - for (v in a) this.attr(v, a[v]) + for (v in a) + this.attr(v, a[v]) /* act as a getter for style attributes */ else if (this._isStyle(a)) return a == 'text' ? this.content : a == 'leading' ? - this[a] : - this.style[a] + this.leading() : + this.style(a) /* act as a getter if the first and only argument is not an object */ else - return this.attrs[a] + return this.attrs[a] || this.node.getAttribute(a) } else if (v === null) { /* remove value */ this.node.removeAttribute(a) + } else if (a == 'style') { + /* redirect to the style method */ + return this.style(v) + } else { /* store value */ this.attrs[a] = v @@ -175,28 +181,38 @@ SVG.extend(SVG.Element, { a == 'text' ? this.text(v) : a == 'leading' ? - this[a] = v : - this.style[a] = v - - this.text(this.content) + this.leading(v) : + this.style(a, v) + + /* rebuild if required */ + if (this.rebuild) + this.rebuild() } } return this } // Manage transformations -, transform: function(o) { - /* act as a getter if the first argument is a string */ - if (typeof o === 'string') - return this.trans[o] +, transform: function(o, v) { + if (typeof o === 'string') { + /* act as a getter if only one string argument is given */ + if (arguments.length < 2) + return this.trans[o] + /* apply transformations as object if key value arguments are given*/ + var transform = {} + transform[o] = v + + return this.transform(transform) + } + /* ... otherwise continue as a setter */ - var key, transform = [] + var transform = [] /* merge values */ - for (key in o) - if (o[key] != null) - this.trans[key] = o[key] + for (v in o) + if (o[v] != null) + this.trans[v] = o[v] /* alias current transformations */ o = this.trans @@ -227,6 +243,52 @@ SVG.extend(SVG.Element, { /* add only te required transformations */ return this.attr('transform', transform.join(' ')) } + // Dynamic style generator +, style: function(s, v) { + if (arguments.length == 0) { + /* get full style */ + return this.attr('style') + + } 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(':') + + if (v.length == 2) + this.style(v[0].replace(/\s+/g, ''), v[1].replace(/^\s+/,'').replace(/\s+$/,'')) + } + } else { + /* act as a getter if the first and only argument is not an object */ + return this.styles[s] + } + + } else if (v === null) { + /* remove value */ + delete this.styles[s] + + } else { + /* store value */ + this.styles[s] = v + } + + /* rebuild style string */ + s = '' + for (v in this.styles) + s += v + ':' + this.styles[v] + ';' + + /* apply style */ + this.node.setAttribute('style', s) + + return this + } // Store data values on svg nodes , data: function(a, v, r) { if (arguments.length < 2) { @@ -257,19 +319,15 @@ SVG.extend(SVG.Element, { } // Show element , show: function() { - this.node.style.display = '' - - return this + return this.style('display', '') } // Hide element , hide: function() { - this.node.style.display = 'none' - - return this + return this.style('display', 'none') } // Is element visible? , visible: function() { - return this.node.style.display != 'none' + return this.style('display') != 'none' } // Private: find svg parent by instance , _parent: function(parent) { @@ -282,7 +340,7 @@ SVG.extend(SVG.Element, { } // Private: tester method for style detection , _isStyle: function(attr) { - return typeof attr == 'string' && this._isText() ? (/^font|text|leading/).test(attr) : false + return typeof attr == 'string' ? SVG.regex.isStyle.test(attr) : false } // Private: element type tester , _isText: function() { diff --git a/src/ellipse.js b/src/ellipse.js index f9398cf..6ff5608 100644 --- a/src/ellipse.js +++ b/src/ellipse.js @@ -17,15 +17,15 @@ SVG.extend(SVG.Ellipse, { }, // Custom size function size: function(width, height) { - return this. - attr({ rx: width / 2, ry: (height != null ? height : width) / 2 }). - center() + return this + .attr({ rx: width / 2, ry: (height != null ? height : width) / 2 }) + .center() }, // Custom center function center: function(x, y) { return this.attr({ - cx: x || (this.attrs.x || 0) + (this.attrs.rx || 0), - cy: y || (this.attrs.y || 0) + (this.attrs.ry || 0) + cx: x != null ? x : (this.attrs.x || 0) + (this.attrs.rx || 0) + , cy: y != null ? y : (this.attrs.y || 0) + (this.attrs.ry || 0) }) } diff --git a/src/event.js b/src/event.js index df3cd6a..84022c7 100644 --- a/src/event.js +++ b/src/event.js @@ -3,28 +3,27 @@ // rect.click(function() { // this.fill({ color: '#f06' }) // }) -;[ 'click', - 'dblclick', - 'mousedown', - 'mouseup', - 'mouseover', - 'mouseout', - 'mousemove', - 'mouseenter', - 'mouseleave', - 'touchstart', - 'touchend', - 'touchmove', - 'touchcancel' ].forEach(function(event) { +;[ 'click' +, 'dblclick' +, 'mousedown' +, 'mouseup' +, 'mouseover' +, 'mouseout' +, 'mousemove' +, 'mouseenter' +, 'mouseleave' +, 'touchstart' +, 'touchend' +, 'touchmove' +, 'touchcancel' ].forEach(function(event) { /* add event to SVG.Element */ SVG.Element.prototype[event] = function(f) { var self = this /* bind event to element rather than element node */ - this.node['on' + event] = typeof f == 'function' - ? function() { return f.apply(self, arguments); } - : null + this.node['on' + event] = typeof f == 'function' ? + function() { return f.apply(self, arguments) } : null return this } @@ -54,11 +53,11 @@ SVG.extend(SVG.Element, { SVG.on(this.node, event, listener) return this - }, + } // Unbind event from listener - off: function(event, listener) { +, off: function(event, listener) { SVG.off(this.node, event, listener) return this } -});
\ No newline at end of file +})
\ No newline at end of file @@ -11,7 +11,7 @@ SVG.extend(SVG.FX, { duration = duration == null ? 1000 : duration ease = ease || '<>' - var akeys, tkeys, tvalues + var akeys, tkeys, skeys , element = this.target , fx = this , start = new Date().getTime() @@ -20,24 +20,29 @@ SVG.extend(SVG.FX, { /* start animation */ this.interval = setInterval(function(){ // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby. - var index + var i, key , time = new Date().getTime() , pos = time > finish ? 1 : (time - start) / duration /* collect attribute keys */ if (akeys == null) { akeys = [] - for (var key in fx.attrs) + for (key in fx.attrs) akeys.push(key) } /* collect transformation keys */ if (tkeys == null) { tkeys = [] - for (var key in fx.trans) + for (key in fx.trans) tkeys.push(key) - - tvalues = {} + } + + /* collect style keys */ + if (skeys == null) { + skeys = [] + for (key in fx.styles) + skeys.push(key) } /* apply easing */ @@ -57,23 +62,23 @@ SVG.extend(SVG.FX, { if (fx._move) element.move(fx._at(fx._move.x, pos), fx._at(fx._move.y, pos)) else if (fx._center) - element.move(fx._at(fx._center.x, pos), fx._at(fx._center.y, pos)) + element.center(fx._at(fx._center.x, pos), fx._at(fx._center.y, pos)) /* run all size properties */ if (fx._size) element.size(fx._at(fx._size.width, pos), fx._at(fx._size.height, pos)) /* animate attributes */ - for (index = akeys.length - 1; index >= 0; index--) - element.attr(akeys[index], fx._at(fx.attrs[akeys[index]], pos)) + for (i = akeys.length - 1; i >= 0; i--) + element.attr(akeys[i], fx._at(fx.attrs[akeys[i]], pos)) /* animate transformations */ - if (tkeys.length > 0) { - for (index = tkeys.length - 1; index >= 0; index--) - tvalues[tkeys[index]] = fx._at(fx.trans[tkeys[index]], pos) - - element.transform(tvalues) - } + for (i = tkeys.length - 1; i >= 0; i--) + element.transform(tkeys[i], fx._at(fx.trans[tkeys[i]], pos)) + + /* animate styles */ + for (i = skeys.length - 1; i >= 0; i--) + element.style(skeys[i], fx._at(fx.styles[skeys[i]], pos)) /* callback for each keyframe */ if (fx._during) @@ -100,12 +105,23 @@ SVG.extend(SVG.FX, { else this.attrs[a] = { from: this.target.attr(a), to: v } - return this; + return this } // Add animatable transformations -, transform: function(o) { - for (var key in o) - this.trans[key] = { from: this.target.trans[key], to: o[key] } +, transform: function(t, v) { + for (var key in t) + this.trans[key] = { from: this.target.trans[key], to: t[key] } + + return this + } + // Add animatable styles +, style: function(s, v) { + if (typeof s == 'object') + for (var key in s) + this.style(key, s[key]) + + else + this.styles[s] = { from: this.target.style(s), to: v } return this } @@ -122,11 +138,18 @@ SVG.extend(SVG.FX, { } // Add animatable size , size: function(width, height) { - var box = this.target.bbox() - - this._size = { - width: { from: box.width, to: width } - , height: { from: box.height, to: height } + if (this.target instanceof SVG.Text) { + /* animate font size for Text elements */ + this.attr('font-size', width) + + } else { + /* animate bbox based size for all other elements */ + var box = this.target.bbox() + + this._size = { + width: { from: box.width, to: width } + , height: { from: box.height, to: height } + } } return this @@ -162,6 +185,7 @@ SVG.extend(SVG.FX, { /* reset storage for properties that need animation */ this.attrs = {} this.trans = {} + this.styles = {} this._move = null this._size = null this._after = null diff --git a/src/gradient.js b/src/gradient.js index 73975c5..4c30b43 100644 --- a/src/gradient.js +++ b/src/gradient.js @@ -15,25 +15,25 @@ SVG.extend(SVG.Gradient, { return this.type == 'radial' ? this.attr({ fx: x + '%', fy: y + '%' }) : this.attr({ x1: x + '%', y1: y + '%' }) - }, + } // To position - to: function(x, y) { +, to: function(x, y) { return this.type == 'radial' ? this.attr({ cx: x + '%', cy: y + '%' }) : this.attr({ x2: x + '%', y2: y + '%' }) - }, + } // Radius for radial gradient - radius: function(radius) { +, radius: function(radius) { return this.type == 'radial' ? this.attr({ r: radius + '%' }) : this - }, + } // Add a color stop - at: function(stop) { +, at: function(stop) { return this.put(new SVG.Stop(stop)) - }, + } // Update gradient - update: function(block) { +, update: function(block) { /* remove all stops */ while (this.node.hasChildNodes()) this.node.removeChild(this.node.lastChild) @@ -42,9 +42,9 @@ SVG.extend(SVG.Gradient, { block(this) return this - }, + } // Return the fill id - fill: function() { +, fill: function() { return 'url(#' + this.attr('id') + ')' } @@ -81,20 +81,16 @@ SVG.extend(SVG.Stop, { /* add color stops */ update: function(o) { - var index, - style = '', - attr = ['opacity', 'color'] + var index + , attr = ['opacity', 'color'] /* build style attribute */ for (index = attr.length - 1; index >= 0; index--) if (o[attr[index]] != null) - style += 'stop-' + attr[index] + ':' + o[attr[index]] + ';' + this.style('stop-' + attr[index], o[attr[index]]) /* set attributes */ - return this.attr({ - offset: (o.offset != null ? o.offset : this.attrs.offset || 0) + '%', - style: style - }) + return this.attr('offset', (o.offset != null ? o.offset : this.attrs.offset || 0) + '%') } }) diff --git a/src/line.js b/src/line.js index 1e783e4..0712f1e 100644 --- a/src/line.js +++ b/src/line.js @@ -12,24 +12,25 @@ SVG.extend(SVG.Line, { var bbox = this.bbox() return this.attr({ - x1: this.attr('x1') - bbox.x + x, - y1: this.attr('y1') - bbox.y + y, - x2: this.attr('x2') - bbox.x + x, - y2: this.attr('y2') - bbox.y + y + x1: this.attr('x1') - bbox.x + x + , y1: this.attr('y1') - bbox.y + y + , x2: this.attr('x2') - bbox.x + x + , y2: this.attr('y2') - bbox.y + y }) - }, + } // Move element by its center - center: function(x, y) { +, center: function(x, y) { var bbox = this.bbox() return this.move(x - bbox.width / 2, y - bbox.height / 2) - }, + } // Set line size by width and height - size: function(width, height) { +, size: function(width, height) { var bbox = this.bbox() - this.attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', bbox.x + width) - return this.attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', bbox.y + height) + return this + .attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', bbox.x + width) + .attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', bbox.y + height) } }) @@ -37,10 +38,10 @@ SVG.extend(SVG.Line, { SVG.extend(SVG.Container, { line: function(x1, y1, x2, y2) { return this.put(new SVG.Line().attr({ - x1: x1, - y1: y1, - x2: x2, - y2: y2 + x1: x1 + , y1: y1 + , x2: x2 + , y2: y2 })) } })
\ No newline at end of file diff --git a/src/nested.js b/src/nested.js index 9d4401c..537d610 100644 --- a/src/nested.js +++ b/src/nested.js @@ -1,7 +1,7 @@ SVG.Nested = function() { this.constructor.call(this, SVG.create('svg')) - this.attr('style', 'overflow:visible') + this.style('overflow', 'visible') } // Inherit from SVG.Container diff --git a/src/path.js b/src/path.js index e62f8d1..2d3558f 100644 --- a/src/path.js +++ b/src/path.js @@ -6,18 +6,17 @@ SVG.Path = function() { SVG.Path.prototype = new SVG.Shape() SVG.extend(SVG.Path, { - - /* move using transform */ + // Move using transform move: function(x, y) { this.transform({ x: x, y: y }) - }, + } - /* set path data */ - plot: function(data) { + // Set path data +, plot: function(data) { return this.attr('d', data || 'M0,0') } -});
\ No newline at end of file +})
\ No newline at end of file diff --git a/src/pattern.js b/src/pattern.js index 7343d6a..c5f51e0 100644 --- a/src/pattern.js +++ b/src/pattern.js @@ -25,11 +25,11 @@ SVG.extend(SVG.Defs, { block(element) return element.attr({ - x: 0, - y: 0, - width: width, - height: height, - patternUnits: 'userSpaceOnUse' + x: 0 + , y: 0 + , width: width + , height: height + , patternUnits: 'userSpaceOnUse' }) } diff --git a/src/poly.js b/src/poly.js index 9e89570..ce5a70c 100644 --- a/src/poly.js +++ b/src/poly.js @@ -12,7 +12,7 @@ SVG.Polyline = function() { } // Inherit from SVG.Shape -SVG.Polyline.prototype = new SVG.Shape() +SVG.Polyline.prototype = new SVG.Shape // Add polygon-specific functions SVG.extend(SVG.Polyline, SVG.Poly) @@ -22,7 +22,7 @@ SVG.Polygon = function() { } // Inherit from SVG.Shape -SVG.Polygon.prototype = new SVG.Shape() +SVG.Polygon.prototype = new SVG.Shape // Add polygon-specific functions SVG.extend(SVG.Polygon, SVG.Poly)
\ No newline at end of file diff --git a/src/rect.js b/src/rect.js index 0a398be..1a3bf19 100644 --- a/src/rect.js +++ b/src/rect.js @@ -3,4 +3,4 @@ SVG.Rect = function() { } // Inherit from SVG.Shape -SVG.Rect.prototype = new SVG.Shape()
\ No newline at end of file +SVG.Rect.prototype = new SVG.Shape
\ No newline at end of file diff --git a/src/regex.js b/src/regex.js index e89dc85..adf82b4 100644 --- a/src/regex.js +++ b/src/regex.js @@ -1,18 +1,33 @@ // 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 - -, rgb: /rgb\((\d+),(\d+),(\d+),([\d\.]+)\)/ - -, hsb: /hsb\((\d+),(\d+),(\d+),([\d\.]+)\)/ - -, isHex: /^#/i - -, isRgb: /^rgb\(/ + /* parse rgb value */ +, rgb: /rgb\((\d+),(\d+),(\d+),([\d\.]+)\)/ + + /* parse hsb value */ +, hsb: /hsb\((\d+),(\d+),(\d+),([\d\.]+)\)/ -, isHsb: /^hsb\(/ + /* test hex value */ +, isHex: /^#[a-f0-9]{3,6}$/i + /* test rgb value */ +, isRgb: /^rgb\(/ + + /* test hsb value */ +, isHsb: /^hsb\(/ + + /* test css declaration */ +, isCss: /[^:]+:[^;]+;?/ + + /* test css property */ +, isStyle: /^font|text|leading|cursor/ + + /* test for blank string */ +, isBlank: /^(\s+)?$/ + }
\ No newline at end of file diff --git a/src/shape.js b/src/shape.js index a11bbc4..0594ed7 100644 --- a/src/shape.js +++ b/src/shape.js @@ -3,4 +3,4 @@ SVG.Shape = function(element) { } // Inherit from SVG.Element -SVG.Shape.prototype = new SVG.Element()
\ No newline at end of file +SVG.Shape.prototype = new SVG.Element
\ No newline at end of file diff --git a/src/sugar.js b/src/sugar.js index 14868e3..1f7339a 100644 --- a/src/sugar.js +++ b/src/sugar.js @@ -63,23 +63,19 @@ SVG.extend(SVG.Element, SVG.FX, { if (SVG.Text) { - SVG.extend(SVG.Text, { + SVG.extend(SVG.Text, SVG.FX, { // Set font font: function(o) { - var key, attr = {} - - for (key in o) - key == 'leading' ? - attr[key] = o[key] : + for (var key in o) key == 'anchor' ? - attr['text-anchor'] = o[key] : + this.attr('text-anchor', o[key]) : _styleAttr.indexOf(key) > -1 ? - attr['font-'+ key] = o[key] : - void 0 - - return this.attr(attr).text(this.content) + this.attr('font-'+ key, o[key]) : + this.attr(key, o[key]) + + return this } - + }) } @@ -1,56 +1,60 @@ -// Use the `svg()` function to create a SVG document within a given html element. The first argument can either be an id of the element or the selected element itself. +// Use the `SVG()` function to create a SVG document within a given html element. The first argument can either be an id of the element or the selected element itself. // -// var draw = svg('paper').size(300, 300) +// var draw = SVG('paper').size(300, 300) // var rect = draw.rect(100, 100).attr({ fill: '#f06' }) -// Shortcut for creating a svg document -this.svg = function(element) { +// The main wrapping element +this.SVG = function(element) { if (SVG.supported) return new SVG.Doc(element) } -// The main wrapping element -this.SVG = { - /* default namespaces */ - ns: 'http://www.w3.org/2000/svg' -, xlink: 'http://www.w3.org/1999/xlink' +// DEPRECATED!!! Use SVG() instead +this.svg = function(element) { + console.warn('WARNING: svg() is deprecated, please use SVG() instead.') + return SVG(element) +} + +// Default namespaces +SVG.ns = 'http://www.w3.org/2000/svg' +SVG.xlink = 'http://www.w3.org/1999/xlink' + +// Element id sequence +SVG.did = 1000 + +// Get next named element id +SVG.eid = function(name) { + return 'Svgjs' + name.charAt(0).toUpperCase() + name.slice(1) + 'Element' + (SVG.did++) +} + +// Method for element creation +SVG.create = function(name) { + /* create element */ + var element = document.createElementNS(this.ns, name) - /* element id sequence */ -, did: 1000 - - // Get next named element id -, eid: function(name) { - return 'Svgjs' + name.charAt(0).toUpperCase() + name.slice(1) + 'Element' + (SVG.did++) - } - // Method for element creation -, create: function(name) { - /* create element */ - var element = document.createElementNS(this.ns, name) - - /* apply unique id */ - element.setAttribute('id', this.eid(name)) - - return element - } + /* apply unique id */ + element.setAttribute('id', this.eid(name)) + + return element +} + // Method for extending objects -, extend: function() { - var modules, methods, key, i - - /* get list of modules */ - modules = Array.prototype.slice.call(arguments) - - /* get object with extensions */ - methods = modules.pop() - - for (i = modules.length - 1; i >= 0; i--) - if (modules[i]) - for (key in methods) - modules[i].prototype[key] = methods[key] - } +SVG.extend = function() { + var modules, methods, key, i + + /* get list of modules */ + modules = Array.prototype.slice.call(arguments) + + /* get object with extensions */ + methods = modules.pop() + for (i = modules.length - 1; i >= 0; i--) + if (modules[i]) + for (key in methods) + modules[i].prototype[key] = methods[key] } // svg support test @@ -59,4 +63,4 @@ SVG.supported = (function() { !! document.createElementNS(SVG.ns,'svg').createSVGRect })() -if (!SVG.supported) return false;
\ No newline at end of file +if (!SVG.supported) return false
\ No newline at end of file diff --git a/src/text.js b/src/text.js index 6201269..3feb514 100644 --- a/src/text.js +++ b/src/text.js @@ -1,77 +1,117 @@ // List font style attributes as they should be applied to style -var _styleAttr = ['size', 'family', 'weight', 'stretch', 'variant', 'style'] +var _styleAttr = ('size family weight stretch variant style').split(' ') SVG.Text = function() { this.constructor.call(this, SVG.create('text')) /* define default style */ - this.style = { 'font-size': 16, 'font-family': 'Helvetica', 'text-anchor': 'start' } - this.leading = 1.2 + this.styles = { + 'font-size': 16 + , 'font-family': 'Helvetica' + , 'text-anchor': 'start' + } + + this._leading = 1.2 } // Inherit from SVG.Element -SVG.Text.prototype = new SVG.Shape() +SVG.Text.prototype = new SVG.Shape SVG.extend(SVG.Text, { // Set the text content text: function(text) { - /* update the content */ - this.content = text = text || 'text' - this.lines = [] + /* act as getter */ + if (text == null) + return this.content - var index, length, tspan, - style = this._style(), - parent = this.doc(), - lines = text.split("\n"), - size = this.style['font-size'] + /* remove existing lines */ + this.clear() - /* remove existing child nodes */ - while (this.node.hasChildNodes()) - this.node.removeChild(this.node.lastChild) + /* update the content */ + this.content = SVG.regex.isBlank.test(text) ? 'text' : text + + var i, il + , lines = text.split('\n') /* build new lines */ - for (index = 0, length = lines.length; index < length; index++) { - /* create new tspan and set attributes */ - tspan = new SVG.TSpan(). - text(lines[index]). - attr({ - dy: size * this.leading - (index == 0 ? size * 0.3 : 0), - x: (this.attrs.x || 0), - style: style - }) + for (i = 0, il = lines.length; i < il; i++) + this.tspan(lines[i]) - /* add new tspan */ - this.node.appendChild(tspan.node) - this.lines.push(tspan) - } - /* set style */ - return this.attr('style', style) - }, - - // Build style based on _styleAttr - _style: function() { - var index, style = '' + return this.attr('style', this.style()) + } + // Create a tspan +, tspan: function(text) { + var tspan = new SVG.TSpan().text(text) - for (index = _styleAttr.length - 1; index >= 0; index--) - if (this.style['font-' + _styleAttr[index]] != null) - style += 'font-' + _styleAttr[index] + ':' + this.style['font-' + _styleAttr[index]] + ';' + /* add new tspan */ + this.node.appendChild(tspan.node) + this.lines.push(tspan) - style += 'text-anchor:' + this.style['text-anchor'] + ';' - - return style + return tspan.attr('style', this.style()) + } + // Move element by its center +, center: function(x, y) { + var anchor = this.style('text-anchor') + , box = this.bbox() + , x = anchor == 'start' ? + x - box.width / 2 : + anchor == 'end' ? + x + box.width / 2 : x + + return this.move(x, y - box.height / 2) + } + // Set font size +, size: function(size) { + return this.attr('font-size', size) + } + // Set / get leading +, leading: function(value) { + /* act as getter */ + if (value == null) + return this._leading + + /* act as setter */ + this._leading = value + + return this.rebuild() + } + // rebuild appearance type +, rebuild: function() { + var i, il + , size = this.styles['font-size'] + + /* define position of all lines */ + for (i = 0, il = this.lines.length; i < il; i++) + this.lines[i].attr({ + dy: size * this._leading - (i == 0 ? size * 0.3 : 0) + , x: (this.attrs.x || 0) + , style: this.style() + }) + + return this + } + // Clear all lines +, clear: function() { + /* remove existing child nodes */ + while (this.node.hasChildNodes()) + this.node.removeChild(this.node.lastChild) + + this.lines = [] + + return this } }) - +// tspan class SVG.TSpan = function() { this.constructor.call(this, SVG.create('tspan')) } // Inherit from SVG.Shape -SVG.TSpan.prototype = new SVG.Shape() +SVG.TSpan.prototype = new SVG.Shape // Include the container object SVG.extend(SVG.TSpan, { @@ -82,4 +122,4 @@ SVG.extend(SVG.TSpan, { return this } -});
\ No newline at end of file +})
\ No newline at end of file diff --git a/src/wrap.js b/src/wrap.js index d3c6b8f..9fa10a2 100644 --- a/src/wrap.js +++ b/src/wrap.js @@ -14,30 +14,30 @@ SVG.extend(SVG.Wrap, { // Move wrapper around move: function(x, y) { return this.transform({ - x: x, - y: y + x: x + , y: y }) - }, + } // Set the actual size in pixels - size: function(width, height) { +, size: function(width, height) { var scale = width / this._b.width this.child.transform({ - scaleX: scale, - scaleY: height != null ? height / this._b.height : scale + scaleX: scale + , scaleY: height != null ? height / this._b.height : scale }) return this - }, + } // Move by center - center: function(x, y) { +, center: function(x, y) { return this.move( - x + (this._b.width * this.child.trans.scaleX) / -2, - y + (this._b.height * this.child.trans.scaleY) / -2 + x + (this._b.width * this.child.trans.scaleX) / -2 + , y + (this._b.height * this.child.trans.scaleY) / -2 ) - }, + } // Create distributed attr - attr: function(a, v, n) { +, attr: function(a, v, n) { /* call individual attributes if an object is given */ if (typeof a == 'object') { for (v in a) this.attr(v, a[v]) @@ -60,9 +60,9 @@ SVG.extend(SVG.Wrap, { } return this - }, + } // Distribute plot method to child - plot: function(data) { +, plot: function(data) { /* plot new shape */ this.child.plot(data) @@ -71,8 +71,8 @@ SVG.extend(SVG.Wrap, { /* reposition element withing wrapper */ this.child.transform({ - x: -this._b.x, - y: -this._b.y + x: -this._b.x + , y: -this._b.y }) return this |