From e2304534e0cfb6f6f4ab8c37ea5275ae26cd455a Mon Sep 17 00:00:00 2001 From: wout Date: Mon, 3 Feb 2014 15:14:47 +0100 Subject: [PATCH] Implemented SVG.invent function and bumped to v1.0rc3 --- CHANGELOG.md | 6 + README.md | 56 +- Rakefile | 4 +- bower.json | 2 +- component.json | 2 +- dist/svg.js | 2797 +++++++++++++++++++++++----------------------- dist/svg.min.js | 4 +- package.json | 2 +- src/clip.js | 64 +- src/container.js | 41 +- src/defs.js | 13 +- src/doc.js | 184 +-- src/element.js | 764 ++++++------- src/ellipse.js | 107 +- src/gradient.js | 154 ++- src/group.js | 61 +- src/hyperlink.js | 54 +- src/image.js | 38 +- src/inventor.js | 23 + src/line.js | 131 ++- src/mask.js | 62 +- src/nested.js | 28 +- src/parent.js | 188 ++-- src/path.js | 90 +- src/poly.js | 51 +- src/rect.js | 28 +- src/set.js | 175 +-- src/shape.js | 14 +- src/svg.js | 30 +- src/text.js | 362 +++--- src/textpath.js | 77 +- src/use.js | 41 +- 32 files changed, 2902 insertions(+), 2751 deletions(-) create mode 100644 src/inventor.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 57af1d1..7c4e1f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.0rc3 (03/02/2014) + +- fix for html-less documents +- added the `SVG.invent` function to ease invention of new elements +- using `SVG.invent` to generate core shapes as well for leaner code + # v1.0rc2 (01/02/2014) - added `index()` method to `SVG.Parent` and `SVG.Set` diff --git a/README.md b/README.md index 44603bc..508230e 100644 --- a/README.md +++ b/README.md @@ -2311,7 +2311,61 @@ __`returns`: `object`__ ## Extending functionality -### SVG.extend +### SVG.invent() +Creating your own custom elements with svg.js is piece of cake thanks to the `SVG.invent` function. For the sake of this example, lets "invent" a shape. We want a `rect` with rounded corners that are always proportional to the size. The new shape lives in the `SVG` namespace and is called `Rounded`. Here is how we achieve that. + +```javascript +SVG.Rounded = SVG.invent({ + // Define the type of element that should be created + create: 'rect' + + // Specify from which existing class this shape inherits +, inherit: SVG.Shape + + // Add custom methods to invented shape +, extend: { + // Create method to proportionally scale the rounded corners + size: function(width, height) { + return this.attr({ + width: width + , height: height + , rx: width / 5 + , ry: height / 5 + }) + } + } + + // Add create method to parent elements +, construct: { + // Create a rounded element + rounded: function(width, height) { + return this.put(new SVG.Rounded).size(width, height) + } + + } +}) +``` + +To create the element in your drawing: + +```javascript +var rounded = draw.rounded(200, 100) +``` + +That's, the invention is now ready to be used! + +The `SVG.invent()` function always expectes an object. The object accepts the following configuration values: + +- `create`: can be either a string with the node name (e.g. `rect`, `ellipse`, ...) or a custom initializer function; `[required]` +- `inherit`: the desired svg.js class to inherit from (e.g. `SVG.Shape`, `SVG.Element`, `SVG.Container`, SVG.Rect`, ...); `[optional]` +- `extend`: an object with the methods that should eb applied to the element's prototype; `[optional]` +- `construct`: an objects with the methods to create the element on the parent element; `[optional]` +- `parent`: an svg.js parent class on which the methods in the passed `construct` object should be available; `[optional]` + +Svg.js uses the `SVG.invent()` function`to create all internal elements, so have a look at the source to see how this function is used in various ways. + + +### SVG.extend() Svg.js has a modular structure. It is very easy to add you own methods at different levels. Let's say we want to add a method to all shape types then we would add our method to SVG.Shape: ```javascript diff --git a/Rakefile b/Rakefile index 2ff1635..b61dbc7 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ -SVGJS_VERSION = 'v1.0rc2' +SVGJS_VERSION = 'v1.0rc3' # all available modules in the correct loading order -MODULES = %w[ svg regex default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader ] +MODULES = %w[ svg inventor regex default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader ] # how many bytes in a "kilobyte" KILO = 1024 diff --git a/bower.json b/bower.json index c28022d..0a64710 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "svg.js", - "version": "1.0.0-rc2", + "version": "1.0.0-rc3", "homepage": "http://svgjs.com/", "authors": [ "Wout Fierens " diff --git a/component.json b/component.json index 9527fd0..9094c2a 100644 --- a/component.json +++ b/component.json @@ -2,7 +2,7 @@ "name": "svg.js", "repo": "wout/svg.js", "description": "A lightweight library for manipulating and animating SVG", - "version": "1.0.0-rc2", + "version": "1.0.0-rc3", "keywords": ["svg"], "author": "Wout Fierens ", "main": "dist/svg.js", diff --git a/dist/svg.js b/dist/svg.js index 0aa269e..4a4f9db 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -1,12 +1,15 @@ -/* svg.js v1.0rc2-15-g3754d89 - svg regex default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader - svgjs.com/license */ +/* svg.js v1.0rc3 - svg inventor regex default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader - svgjs.com/license */ ;(function() { this.SVG = function(element) { - if (!SVG.parser) - SVG.prepare() + if (SVG.supported) { + element = new SVG.Doc(element) - if (SVG.supported) - return new SVG.Doc(element) + if (!SVG.parser) + SVG.prepare(element) + + return element + } } // Default namespaces @@ -60,15 +63,15 @@ } // Initialize parsing element - SVG.prepare = function() { - /* select document body and create svg element*/ - var body = document.getElementsByTagName('body')[0] || document.getElementsByTagName('svg')[0] - , draw = new SVG.Doc(body).size(2, 2).style('opacity:0;position:fixed;left:100%;top:100%;') + SVG.prepare = function(element) { + /* select document body and create invisible svg element */ + var body = document.getElementsByTagName('body')[0] + , draw = (body ? new SVG.Doc(body) : element.nested()).size(2, 2) /* create parser object */ SVG.parser = { - body: body - , draw: draw + body: body || element.parent + , draw: draw.style('opacity:0;position:fixed;left:100%;top:100%;overflow:hidden') , poly: draw.polygon().node , path: draw.path().node } @@ -82,6 +85,29 @@ if (!SVG.supported) return false + SVG.invent = function(config) { + /* create element initializer */ + var initializer = typeof config.create == 'function' ? + config.create : + function() { + this.constructor.call(this, SVG.create(config.create)) + } + + /* inherit prototype */ + if (config.inherit) + initializer.prototype = new config.inherit + + /* extend with methods */ + if (config.extend) + SVG.extend(initializer, config.extend) + + /* attach construct method to parent */ + if (config.construct) + SVG.extend(config.parent || SVG.Container, config.construct) + + return initializer + } + SVG.regex = { /* test a given value */ test: function(value, test) { @@ -1044,548 +1070,559 @@ }) - SVG.Element = function(node) { - /* make stroke value accessible dynamically */ - this._stroke = SVG.defaults.attrs.stroke - - /* initialize style store */ - this.styles = {} - - /* initialize transformation store with defaults */ - this.trans = SVG.defaults.trans() - - /* keep reference to the element node */ - if (this.node = node) { - this.type = node.nodeName - this.node.instance = this + SVG.Element = SVG.invent({ + // Initialize node + create: function(node) { + /* make stroke value accessible dynamically */ + this._stroke = SVG.defaults.attrs.stroke + + /* initialize style store */ + this.styles = {} + + /* initialize transformation store with defaults */ + this.trans = SVG.defaults.trans() + + /* keep reference to the element node */ + if (this.node = node) { + this.type = node.nodeName + this.node.instance = this + } } - } - // - SVG.extend(SVG.Element, { - // Move over x-axis - x: function(x) { - if (x) { - x = new SVG.Number(x) - x.value /= this.trans.scaleX + // Add class methods + , extend: { + // Move over x-axis + x: function(x) { + if (x) { + x = new SVG.Number(x) + x.value /= this.trans.scaleX + } + return this.attr('x', x) } - return this.attr('x', x) - } - // Move over y-axis - , y: function(y) { - if (y) { - y = new SVG.Number(y) - y.value /= this.trans.scaleY + // Move over y-axis + , y: function(y) { + if (y) { + y = new SVG.Number(y) + y.value /= this.trans.scaleY + } + return this.attr('y', y) } - return this.attr('y', y) - } - // Move by center over x-axis - , cx: function(x) { - return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2) - } - // Move by center over y-axis - , cy: function(y) { - return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2) - } - // Move element to given x and y values - , move: function(x, y) { - return this.x(x).y(y) - } - // Move element by its center - , center: function(x, y) { - return this.cx(x).cy(y) - } - // Set width of element - , width: function(width) { - return this.attr('width', width) - } - // Set height of element - , height: function(height) { - return this.attr('height', height) - } - // Set element size to given width and height - , size: function(width, height) { - var p = this._proportionalSize(width, height) - - return this.attr({ - width: new SVG.Number(p.width) - , height: new SVG.Number(p.height) - }) - } - // Clone element - , clone: function() { - var clone , attr - , type = this.type - - /* invoke shape method with shape-specific arguments */ - clone = type == 'rect' || type == 'ellipse' ? - this.parent[type](0,0) : - type == 'line' ? - this.parent[type](0,0,0,0) : - type == 'image' ? - this.parent[type](this.src) : - type == 'text' ? - this.parent[type](this.content) : - type == 'path' ? - this.parent[type](this.attr('d')) : - type == 'polyline' || type == 'polygon' ? - this.parent[type](this.attr('points')) : - type == 'g' ? - this.parent.group() : - this.parent[type]() - - /* apply attributes attributes */ - attr = this.attr() - delete attr.id - clone.attr(attr) - - /* copy transformations */ - clone.trans = this.trans - - /* apply attributes and translations */ - return clone.transform({}) - } - // Remove element - , remove: function() { - if (this.parent) - this.parent.removeElement(this) - - return this - } - // Replace element - , replace: function(element) { - this.after(element).remove() + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2) + } + // Move element to given x and y values + , move: function(x, y) { + return this.x(x).y(y) + } + // Move element by its center + , center: function(x, y) { + return this.cx(x).cy(y) + } + // Set width of element + , width: function(width) { + return this.attr('width', width) + } + // Set height of element + , height: function(height) { + return this.attr('height', height) + } + // Set element size to given width and height + , size: function(width, height) { + var p = this._proportionalSize(width, height) - return element - } - // Add element to given container and return self - , addTo: function(parent) { - return parent.put(this) - } - // Add element to given container and return container - , putIn: function(parent) { - return parent.add(this) - } - // Get parent document - , doc: function(type) { - return this._parent(type || SVG.Doc) - } - // Set svg element attribute - , attr: function(a, v, n) { - if (a == null) { - /* get an object of attributes */ - a = {} - v = this.node.attributes - for (n = v.length - 1; n >= 0; n--) - a[v[n].nodeName] = SVG.regex.test(v[n].nodeValue, 'isNumber') ? parseFloat(v[n].nodeValue) : v[n].nodeValue + return this.attr({ + width: new SVG.Number(p.width) + , height: new SVG.Number(p.height) + }) + } + // Clone element + , clone: function() { + var clone , attr + , type = this.type - return a + /* invoke shape method with shape-specific arguments */ + clone = type == 'rect' || type == 'ellipse' ? + this.parent[type](0,0) : + type == 'line' ? + this.parent[type](0,0,0,0) : + type == 'image' ? + this.parent[type](this.src) : + type == 'text' ? + this.parent[type](this.content) : + type == 'path' ? + this.parent[type](this.attr('d')) : + type == 'polyline' || type == 'polygon' ? + this.parent[type](this.attr('points')) : + type == 'g' ? + this.parent.group() : + this.parent[type]() - } else if (typeof a == 'object') { - /* apply every attribute individually if an object is passed */ - for (v in a) this.attr(v, a[v]) + /* apply attributes attributes */ + attr = this.attr() + delete attr.id + clone.attr(attr) + + /* copy transformations */ + clone.trans = this.trans + + /* apply attributes and translations */ + return clone.transform({}) + } + // Remove element + , remove: function() { + if (this.parent) + this.parent.removeElement(this) + + return this + } + // Replace element + , replace: function(element) { + this.after(element).remove() + + return element + } + // Add element to given container and return self + , addTo: function(parent) { + return parent.put(this) + } + // Add element to given container and return container + , putIn: function(parent) { + return parent.add(this) + } + // Get parent document + , doc: function(type) { + return this._parent(type || SVG.Doc) + } + // Set svg element attribute + , attr: function(a, v, n) { + if (a == null) { + /* get an object of attributes */ + a = {} + v = this.node.attributes + for (n = v.length - 1; n >= 0; n--) + a[v[n].nodeName] = SVG.regex.test(v[n].nodeValue, 'isNumber') ? parseFloat(v[n].nodeValue) : v[n].nodeValue + + return a + + } else if (typeof a == 'object') { + /* apply every attribute individually if an object is passed */ + for (v in a) this.attr(v, a[v]) + + } else if (v === null) { + /* remove value */ + this.node.removeAttribute(a) + + } else if (v == null) { + /* act as a getter for style attributes */ + if (this._isStyle(a)) { + return a == 'text' ? + this.content : + a == 'leading' && this.leading ? + this.leading() : + this.style(a) + + /* act as a getter if the first and only argument is not an object */ + } else { + v = this.node.getAttribute(a) + return v == null ? + SVG.defaults.attrs[a] : + SVG.regex.test(v, 'isNumber') ? + parseFloat(v) : v + } + + } else if (a == 'style') { + /* redirect to the style method */ + return this.style(v) - } else if (v === null) { - /* remove value */ - this.node.removeAttribute(a) - - } else if (v == null) { - /* act as a getter for style attributes */ - if (this._isStyle(a)) { - return a == 'text' ? - this.content : - a == 'leading' && this.leading ? - this.leading() : - this.style(a) - - /* act as a getter if the first and only argument is not an object */ } else { - v = this.node.getAttribute(a) - return v == null ? - SVG.defaults.attrs[a] : - SVG.regex.test(v, 'isNumber') ? - parseFloat(v) : v - } - - } else if (a == 'style') { - /* redirect to the style method */ - return this.style(v) - - } else { - /* treat x differently on text elements */ - if (a == 'x' && Array.isArray(this.lines)) - for (n = this.lines.length - 1; n >= 0; n--) - this.lines[n].attr(a, v) - - /* BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 */ - if (a == 'stroke-width') - this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null) - else if (a == 'stroke') - this._stroke = v - - /* ensure full hex color */ - if (SVG.Color.test(v) || SVG.Color.isRgb(v)) - v = new SVG.Color(v) - - /* ensure correct numeric values */ - else if (typeof v === 'number') - v = new SVG.Number(v) - - /* parse array values */ - else if (Array.isArray(v)) - v = new SVG.Array(v) - - /* set give attribute on node */ - n != null ? - this.node.setAttributeNS(n, a, v.toString()) : - this.node.setAttribute(a, v.toString()) - - /* if the passed argument belongs in the style as well, add it there */ - if (this._isStyle(a)) { - a == 'text' ? - this.text(v) : - a == 'leading' && this.leading ? - this.leading(v) : - this.style(a, v) + /* treat x differently on text elements */ + if (a == 'x' && Array.isArray(this.lines)) + for (n = this.lines.length - 1; n >= 0; n--) + this.lines[n].attr(a, v) - /* rebuild if required */ - if (this.rebuild) - this.rebuild(a, v) + /* BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 */ + if (a == 'stroke-width') + this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null) + else if (a == 'stroke') + this._stroke = v + + /* ensure full hex color */ + if (SVG.Color.test(v) || SVG.Color.isRgb(v)) + v = new SVG.Color(v) + + /* ensure correct numeric values */ + else if (typeof v === 'number') + v = new SVG.Number(v) + + /* parse array values */ + else if (Array.isArray(v)) + v = new SVG.Array(v) + + /* set give attribute on node */ + n != null ? + this.node.setAttributeNS(n, a, v.toString()) : + this.node.setAttribute(a, v.toString()) + + /* if the passed argument belongs in the style as well, add it there */ + if (this._isStyle(a)) { + a == 'text' ? + this.text(v) : + a == 'leading' && this.leading ? + this.leading(v) : + this.style(a, v) + + /* rebuild if required */ + if (this.rebuild) + this.rebuild(a, v) + } } + + return this } - - return this - } - // Manage transformations - , transform: function(o, v) { - - if (arguments.length == 0) { - /* act as a getter if no argument is given */ - return this.trans + // Manage transformations + , transform: function(o, v) { - } else if (typeof o === 'string') { - /* act as a getter if only one string argument is given */ - if (arguments.length < 2) - return this.trans[o] + if (arguments.length == 0) { + /* act as a getter if no argument is given */ + return this.trans + + } else 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) + } - /* apply transformations as object if key value arguments are given*/ - var transform = {} - transform[o] = v + /* ... otherwise continue as a setter */ + var transform = [] + + /* parse matrix */ + o = this._parseMatrix(o) + + /* merge values */ + for (v in o) + if (o[v] != null) + this.trans[v] = o[v] + + /* compile matrix */ + this.trans.matrix = this.trans.a + + ' ' + this.trans.b + + ' ' + this.trans.c + + ' ' + this.trans.d + + ' ' + this.trans.e + + ' ' + this.trans.f + + /* alias current transformations */ + o = this.trans + + /* add matrix */ + if (o.matrix != SVG.defaults.matrix) + transform.push('matrix(' + o.matrix + ')') - return this.transform(transform) + /* add rotation */ + if (o.rotation != 0) + transform.push('rotate(' + o.rotation + ' ' + (o.cx == null ? this.bbox().cx : o.cx) + ' ' + (o.cy == null ? this.bbox().cy : o.cy) + ')') + + /* add scale */ + if (o.scaleX != 1 || o.scaleY != 1) + transform.push('scale(' + o.scaleX + ' ' + o.scaleY + ')') + + /* add skew on x axis */ + if (o.skewX != 0) + transform.push('skewX(' + o.skewX + ')') + + /* add skew on y axis */ + if (o.skewY != 0) + transform.push('skewY(' + o.skewY + ')') + + /* add translation */ + if (o.x != 0 || o.y != 0) + transform.push('translate(' + new SVG.Number(o.x / o.scaleX) + ' ' + new SVG.Number(o.y / o.scaleY) + ')') + + /* update transformations, even if there are none */ + if (transform.length == 0) + this.node.removeAttribute('transform') + else + this.node.setAttribute('transform', transform.join(' ')) + + return this } - - /* ... otherwise continue as a setter */ - var transform = [] - - /* parse matrix */ - o = this._parseMatrix(o) - - /* merge values */ - for (v in o) - if (o[v] != null) - this.trans[v] = o[v] - - /* compile matrix */ - this.trans.matrix = this.trans.a - + ' ' + this.trans.b - + ' ' + this.trans.c - + ' ' + this.trans.d - + ' ' + this.trans.e - + ' ' + this.trans.f - - /* alias current transformations */ - o = this.trans - - /* add matrix */ - if (o.matrix != SVG.defaults.matrix) - transform.push('matrix(' + o.matrix + ')') - - /* add rotation */ - if (o.rotation != 0) - transform.push('rotate(' + o.rotation + ' ' + (o.cx == null ? this.bbox().cx : o.cx) + ' ' + (o.cy == null ? this.bbox().cy : o.cy) + ')') - - /* add scale */ - if (o.scaleX != 1 || o.scaleY != 1) - transform.push('scale(' + o.scaleX + ' ' + o.scaleY + ')') - - /* add skew on x axis */ - if (o.skewX != 0) - transform.push('skewX(' + o.skewX + ')') - - /* add skew on y axis */ - if (o.skewY != 0) - transform.push('skewY(' + o.skewY + ')') - - /* add translation */ - if (o.x != 0 || o.y != 0) - transform.push('translate(' + new SVG.Number(o.x / o.scaleX) + ' ' + new SVG.Number(o.y / o.scaleY) + ')') - - /* update transformations, even if there are none */ - if (transform.length == 0) - this.node.removeAttribute('transform') - else - this.node.setAttribute('transform', transform.join(' ')) - - return this - } - // 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]) + // Dynamic style generator + , style: function(s, v) { + if (arguments.length == 0) { + /* get full style */ + return this.attr('style') || '' - } else if (SVG.regex.isCss.test(s)) { - /* parse css string */ - s = s.split(';') + } 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(':') + /* 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+$/,'')) + 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 || SVG.regex.test(v, 'isBlank')) { + /* remove value */ + delete this.styles[s] + } else { - /* act as a getter if the first and only argument is not an object */ - return this.styles[s] + /* store value */ + this.styles[s] = v } - - } else if (v === null || SVG.regex.test(v, 'isBlank')) { - /* 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 */ + if (s == '') + this.node.removeAttribute('style') + else + this.node.setAttribute('style', s) + + return this } - - /* rebuild style string */ - s = '' - for (v in this.styles) - s += v + ':' + this.styles[v] + ';' - - /* apply style */ - if (s == '') - this.node.removeAttribute('style') - else - this.node.setAttribute('style', s) - - return this - } - // Get bounding box - , bbox: function() { - return new SVG.BBox(this) - } - // Get rect box - , rbox: function() { - return new SVG.RBox(this) - } - // Checks whether the given point inside the bounding box of the element - , inside: function(x, y) { - var box = this.bbox() - - return x > box.x - && y > box.y - && x < box.x + box.width - && y < box.y + box.height - } - // Show element - , show: function() { - return this.style('display', '') - } - // Hide element - , hide: function() { - return this.style('display', 'none') - } - // Is element visible? - , visible: function() { - return this.style('display') != 'none' - } - // Return id on string conversion - , toString: function() { - return this.attr('id') - } - // Private: find svg parent by instance - , _parent: function(parent) { - var element = this - - while (element != null && !(element instanceof parent)) - element = element.parent - - return element - } - // Private: tester method for style detection - , _isStyle: function(a) { - return typeof a == 'string' ? SVG.regex.test(a, 'isStyle') : false - } - // Private: parse a matrix string - , _parseMatrix: function(o) { - if (o.matrix) { - /* split matrix string */ - var m = o.matrix.replace(/\s/g, '').split(',') - - /* pasrse values */ - if (m.length == 6) { - o.a = parseFloat(m[0]) - o.b = parseFloat(m[1]) - o.c = parseFloat(m[2]) - o.d = parseFloat(m[3]) - o.e = parseFloat(m[4]) - o.f = parseFloat(m[5]) - } + // Get bounding box + , bbox: function() { + return new SVG.BBox(this) } - - return o - } - // Private: calculate proportional width and height values when necessary - , _proportionalSize: function(width, height) { - if (width == null || height == null) { + // Get rect box + , rbox: function() { + return new SVG.RBox(this) + } + // Checks whether the given point inside the bounding box of the element + , inside: function(x, y) { var box = this.bbox() + + return x > box.x + && y > box.y + && x < box.x + box.width + && y < box.y + box.height + } + // Show element + , show: function() { + return this.style('display', '') + } + // Hide element + , hide: function() { + return this.style('display', 'none') + } + // Is element visible? + , visible: function() { + return this.style('display') != 'none' + } + // Return id on string conversion + , toString: function() { + return this.attr('id') + } + // Private: find svg parent by instance + , _parent: function(parent) { + var element = this + + while (element != null && !(element instanceof parent)) + element = element.parent - if (height == null) - height = box.height / box.width * width - else if (width == null) - width = box.width / box.height * height + return element } - - return { - width: width - , height: height + // Private: tester method for style detection + , _isStyle: function(a) { + return typeof a == 'string' ? SVG.regex.test(a, 'isStyle') : false + } + // Private: parse a matrix string + , _parseMatrix: function(o) { + if (o.matrix) { + /* split matrix string */ + var m = o.matrix.replace(/\s/g, '').split(',') + + /* pasrse values */ + if (m.length == 6) { + o.a = parseFloat(m[0]) + o.b = parseFloat(m[1]) + o.c = parseFloat(m[2]) + o.d = parseFloat(m[3]) + o.e = parseFloat(m[4]) + o.f = parseFloat(m[5]) + } + } + + return o + } + // Private: calculate proportional width and height values when necessary + , _proportionalSize: function(width, height) { + if (width == null || height == null) { + var box = this.bbox() + + if (height == null) + height = box.height / box.width * width + else if (width == null) + width = box.width / box.height * height + } + + return { + width: width + , height: height + } } } }) - SVG.Parent = function(element) { - this.constructor.call(this, element) - } + SVG.Parent = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) + } - // Inherit from SVG.Element - SVG.Parent.prototype = new SVG.Element + // Inherit from + , inherit: SVG.Element - // - SVG.extend(SVG.Parent, { - // Returns all child elements - children: function() { - return this._children || (this._children = []) - } - // Add given element at a position - , add: function(element, i) { - if (!this.has(element)) { - /* define insertion index if none given */ - i = i == null ? this.children().length : i - - /* remove references from previous parent */ - if (element.parent) - element.parent.children().splice(element.parent.index(element), 1) - - /* add element references */ - this.children().splice(i, 0, element) - this.node.insertBefore(element.node, this.node.childNodes[i] || null) - element.parent = this + // Add class methods + , extend: { + // Returns all child elements + children: function() { + return this._children || (this._children = []) } + // Add given element at a position + , add: function(element, i) { + if (!this.has(element)) { + /* define insertion index if none given */ + i = i == null ? this.children().length : i + + /* remove references from previous parent */ + if (element.parent) + element.parent.children().splice(element.parent.index(element), 1) + + /* add element references */ + this.children().splice(i, 0, element) + this.node.insertBefore(element.node, this.node.childNodes[i] || null) + element.parent = this + } - /* reposition defs */ - if (this._defs) { - this.node.removeChild(this._defs.node) - this.node.appendChild(this._defs.node) + /* reposition defs */ + if (this._defs) { + this.node.removeChild(this._defs.node) + this.node.appendChild(this._defs.node) + } + + return this } - - return this - } - // Basically does the same as `add()` but returns the added element instead - , put: function(element, i) { - this.add(element, i) - return element - } - // Checks if the given element is a child - , has: function(element) { - return this.index(element) >= 0 - } - // Gets index of given element - , index: function(element) { - return this.children().indexOf(element) - } - // Get a element at the given index - , get: function(i) { - return this.children()[i] - } - // Get first child, skipping the defs node - , first: function() { - return this.children()[0] - } - // Get the last child - , last: function() { - return this.children()[this.children().length - 1] - } - // Iterates over all children and invokes a given block - , each: function(block, deep) { - var i, il - , children = this.children() - - for (i = 0, il = children.length; i < il; i++) { - if (children[i] instanceof SVG.Element) - block.apply(children[i], [i, children]) - - if (deep && (children[i] instanceof SVG.Container)) - children[i].each(block, deep) + // Basically does the same as `add()` but returns the added element instead + , put: function(element, i) { + this.add(element, i) + return element } - - return this - } - // Remove a child element at a position - , removeElement: function(element) { - this.children().splice(this.index(element), 1) - this.node.removeChild(element.node) - element.parent = null + // Checks if the given element is a child + , has: function(element) { + return this.index(element) >= 0 + } + // Gets index of given element + , index: function(element) { + return this.children().indexOf(element) + } + // Get a element at the given index + , get: function(i) { + return this.children()[i] + } + // Get first child, skipping the defs node + , first: function() { + return this.children()[0] + } + // Get the last child + , last: function() { + return this.children()[this.children().length - 1] + } + // Iterates over all children and invokes a given block + , each: function(block, deep) { + var i, il + , children = this.children() + + for (i = 0, il = children.length; i < il; i++) { + if (children[i] instanceof SVG.Element) + block.apply(children[i], [i, children]) + + if (deep && (children[i] instanceof SVG.Container)) + children[i].each(block, deep) + } - return this - } - // Remove all elements in this container - , clear: function() { - /* remove children */ - for (var i = this.children().length - 1; i >= 0; i--) - this.removeElement(this.children()[i]) + return this + } + // Remove a child element at a position + , removeElement: function(element) { + this.children().splice(this.index(element), 1) + this.node.removeChild(element.node) + element.parent = null + + return this + } + // Remove all elements in this container + , clear: function() { + /* remove children */ + for (var i = this.children().length - 1; i >= 0; i--) + this.removeElement(this.children()[i]) - /* remove defs node */ - if (this._defs) - this._defs.clear() + /* remove defs node */ + if (this._defs) + this._defs.clear() - return this - } - , // Get defs - defs: function() { - return this.doc().defs() + return this + } + , // Get defs + defs: function() { + return this.doc().defs() + } } + }) - SVG.Container = function(element) { - this.constructor.call(this, element) - } - - // Inherit from SVG.Parent - SVG.Container.prototype = new SVG.Parent - - // - SVG.extend(SVG.Container, { - // Get the viewBox and calculate the zoom value - viewbox: function(v) { - if (arguments.length == 0) - /* act as a getter if there are no arguments */ - return new SVG.ViewBox(this) - - /* otherwise act as a setter */ - v = arguments.length == 1 ? - [v.x, v.y, v.width, v.height] : - [].slice.call(arguments) - - return this.attr('viewBox', v) + + SVG.Container = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) + } + + // Inherit from + , inherit: SVG.Parent + + // Add class methods + , extend: { + // Get the viewBox and calculate the zoom value + viewbox: function(v) { + if (arguments.length == 0) + /* act as a getter if there are no arguments */ + return new SVG.ViewBox(this) + + /* otherwise act as a setter */ + v = arguments.length == 1 ? + [v.x, v.y, v.width, v.height] : + [].slice.call(arguments) + + return this.attr('viewBox', v) + } } }) @@ -2125,48 +2162,48 @@ } }) - SVG.Defs = function() { - this.constructor.call(this, SVG.create('defs')) - } + SVG.Defs = SVG.invent({ + // Initialize node + create: 'defs' - // Inherits from SVG.Container - SVG.Defs.prototype = new SVG.Container + // Inherit from + , inherit: SVG.Container + }) - SVG.G = function() { - this.constructor.call(this, SVG.create('g')) - } - - // Inherit from SVG.Container - SVG.G.prototype = new SVG.Container - - // - SVG.extend(SVG.G, { - // Move over x-axis - x: function(x) { - return x == null ? this.trans.x : this.transform('x', x) - } - // Move over y-axis - , y: function(y) { - return y == null ? this.trans.y : this.transform('y', y) - } - // Move by center over x-axis - , cx: function(x) { - return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) - } - // Move by center over y-axis - , cy: function(y) { - return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) + SVG.G = SVG.invent({ + // Initialize node + create: 'g' + + // Inherit from + , inherit: SVG.Container + + // Add class methods + , extend: { + // Move over x-axis + x: function(x) { + return x == null ? this.trans.x : this.transform('x', x) + } + // Move over y-axis + , y: function(y) { + return y == null ? this.trans.y : this.transform('y', y) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) + } } - }) - - // - SVG.extend(SVG.Container, { - // Create a group element - group: function() { - return this.put(new SVG.G) + // Add parent method + , construct: { + // Create a group element + group: function() { + return this.put(new SVG.G) + } } - }) SVG.extend(SVG.Element, { @@ -2234,34 +2271,45 @@ }) - SVG.Mask = function() { - this.constructor.call(this, SVG.create('mask')) + SVG.Mask = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('mask')) - /* keep references to masked elements */ - this.targets = [] - } + /* keep references to masked elements */ + this.targets = [] + } - // Inherit from SVG.Container - SVG.Mask.prototype = new SVG.Container + // Inherit from + , inherit: SVG.Container - // - SVG.extend(SVG.Mask, { - // Unmask all masked elements and remove itself - remove: function() { - /* unmask all targets */ - for (var i = this.targets.length - 1; i >= 0; i--) - if (this.targets[i]) - this.targets[i].unmask() - delete this.targets - - /* remove mask from parent */ - this.parent.removeElement(this) - - return this + // Add class methods + , extend: { + // Unmask all masked elements and remove itself + remove: function() { + /* unmask all targets */ + for (var i = this.targets.length - 1; i >= 0; i--) + if (this.targets[i]) + this.targets[i].unmask() + delete this.targets + + /* remove mask from parent */ + this.parent.removeElement(this) + + return this + } + } + + // Add parent method + , construct: { + // Create masking element + mask: function() { + return this.defs().put(new SVG.Mask) + } } }) - // + SVG.extend(SVG.Element, { // Distribute mask to svg element maskWith: function(element) { @@ -2281,40 +2329,43 @@ } }) + + + SVG.Clip = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('clipPath')) - // - SVG.extend(SVG.Container, { - // Create masking element - mask: function() { - return this.defs().put(new SVG.Mask) + /* keep references to clipped elements */ + this.targets = [] } - - }) - - SVG.Clip = function() { - this.constructor.call(this, SVG.create('clipPath')) - /* keep references to clipped elements */ - this.targets = [] - } + // Inherit from + , inherit: SVG.Container - // Inherit from SVG.Container - SVG.Clip.prototype = new SVG.Container + // Add class methods + , extend: { + // Unclip all clipped elements and remove itself + remove: function() { + /* unclip all targets */ + for (var i = this.targets.length - 1; i >= 0; i--) + if (this.targets[i]) + this.targets[i].unclip() + delete this.targets - // - SVG.extend(SVG.Clip, { - // Unclip all clipped elements and remove itself - remove: function() { - /* unclip all targets */ - for (var i = this.targets.length - 1; i >= 0; i--) - if (this.targets[i]) - this.targets[i].unclip() - delete this.targets - - /* remove clipPath from parent */ - this.parent.removeElement(this) - - return this + /* remove clipPath from parent */ + this.parent.removeElement(this) + + return this + } + } + + // Add parent method + , construct: { + // Create clipping element + clip: function() { + return this.defs().put(new SVG.Clip) + } } }) @@ -2338,72 +2389,72 @@ } }) - - // - SVG.extend(SVG.Container, { - // Create clipping element - clip: function() { - return this.defs().put(new SVG.Clip) - } - - }) - SVG.Gradient = function(type) { - this.constructor.call(this, SVG.create(type + 'Gradient')) - - /* store type */ - this.type = type - } - - // Inherit from SVG.Container - SVG.Gradient.prototype = new SVG.Container - - // - SVG.extend(SVG.Gradient, { - // From position - from: function(x, y) { - return this.type == 'radial' ? - this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) : - this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) }) - } - // To position - , to: function(x, y) { - return this.type == 'radial' ? - this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) : - this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) }) - } - // Radius for radial gradient - , radius: function(r) { - return this.type == 'radial' ? - this.attr({ r: new SVG.Number(r) }) : - this - } - // Add a color stop - , at: function(stop) { - return this.put(new SVG.Stop(stop)) - } - // Update gradient - , update: function(block) { - /* remove all stops */ - this.clear() + SVG.Gradient = SVG.invent({ + // Initialize node + create: function(type) { + this.constructor.call(this, SVG.create(type + 'Gradient')) - /* invoke passed block */ - block(this) - - return this - } - // Return the fill id - , fill: function() { - return 'url(#' + this.attr('id') + ')' + /* store type */ + this.type = type } - // Alias string convertion to fill - , toString: function() { - return this.fill() + + // Inherit from + , inherit: SVG.Container + + // Add class methods + , extend: { + // From position + from: function(x, y) { + return this.type == 'radial' ? + this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) : + this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) }) + } + // To position + , to: function(x, y) { + return this.type == 'radial' ? + this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) : + this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) }) + } + // Radius for radial gradient + , radius: function(r) { + return this.type == 'radial' ? + this.attr({ r: new SVG.Number(r) }) : + this + } + // Add a color stop + , at: function(stop) { + return this.put(new SVG.Stop().update(stop)) + } + // Update gradient + , update: function(block) { + /* remove all stops */ + this.clear() + + /* invoke passed block */ + block(this) + + return this + } + // Return the fill id + , fill: function() { + return 'url(#' + this.attr('id') + ')' + } + // Alias string convertion to fill + , toString: function() { + return this.fill() + } } + // Add parent method + , construct: { + // Create gradient element in defs + gradient: function(type, block) { + return this.defs().gradient(type, block) + } + } }) - // SVG.extend(SVG.Defs, { // define gradient gradient: function(type, block) { @@ -2417,356 +2468,367 @@ }) - // - SVG.extend(SVG.Container, { - // Create gradient element in defs - gradient: function(type, block) { - return this.defs().gradient(type, block) - } - - }) - - - SVG.Stop = function(stop) { - this.constructor.call(this, SVG.create('stop')) - - /* immediatelly build stop */ - this.update(stop) - } + SVG.Stop = SVG.invent({ + // Initialize node + create: 'stop' - // Inherit from SVG.Element - SVG.Stop.prototype = new SVG.Element + // Inherit from + , inherit: SVG.Element - // - SVG.extend(SVG.Stop, { - // add color stops - update: function(o) { - /* set attributes */ - if (o.opacity != null) this.attr('stop-opacity', o.opacity) - if (o.color != null) this.attr('stop-color', o.color) - if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) + // Add class methods + , extend: { + // add color stops + update: function(o) { + /* set attributes */ + if (o.opacity != null) this.attr('stop-opacity', o.opacity) + if (o.color != null) this.attr('stop-color', o.color) + if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) - return this + return this + } } - - }) + }) - SVG.Doc = function(element) { - /* ensure the presence of a html element */ - this.parent = typeof element == 'string' ? - document.getElementById(element) : - element - - /* If the target is an svg element, use that element as the main wrapper. - This allows svg.js to work with svg documents as well. */ - this.constructor - .call(this, this.parent.nodeName == 'svg' ? this.parent : SVG.create('svg')) - - /* set svg element attributes */ - this - .attr({ xmlns: SVG.ns, version: '1.1', width: '100%', height: '100%' }) - .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) - - /* create the node */ - this._defs = new SVG.Defs - this._defs.parent = this - this.node.appendChild(this._defs.node) - - /* turno of sub pixel offset by default */ - this.doSubPixelOffsetFix = false - - /* ensure correct rendering */ - if (this.parent.nodeName != 'svg') - this.stage() - } - - // Inherits from SVG.Container - SVG.Doc.prototype = new SVG.Container - - // - SVG.extend(SVG.Doc, { - // Hack for safari preventing text to be rendered in one line. - // 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. - // It also handles sub-pixel offset rendering properly. - stage: function() { - var check - , element = this - , wrapper = document.createElement('div') - - /* set temporary wrapper to position relative */ - wrapper.style.cssText = 'position:relative;height:100%;' - - /* put element into wrapper */ - element.parent.appendChild(wrapper) - wrapper.appendChild(element.node) - - /* check for dom:ready */ - check = function() { - if (document.readyState === 'complete') { - element.style('position:absolute;') - setTimeout(function() { - /* set position back to relative */ - element.style('position:relative;overflow:hidden;') - - /* remove temporary wrapper */ - element.parent.removeChild(element.node.parentNode) - element.node.parentNode.removeChild(element.node) - element.parent.appendChild(element.node) - - /* after wrapping is done, fix sub-pixel offset */ - element.subPixelOffsetFix() - - /* make sure sub-pixel offset is fixed every time the window is resized */ - SVG.on(window, 'resize', function() { + SVG.Doc = SVG.invent({ + // Initialize node + create: function(element) { + /* ensure the presence of a html element */ + this.parent = typeof element == 'string' ? + document.getElementById(element) : + element + + /* If the target is an svg element, use that element as the main wrapper. + This allows svg.js to work with svg documents as well. */ + this.constructor + .call(this, this.parent.nodeName == 'svg' ? this.parent : SVG.create('svg')) + + /* set svg element attributes */ + this + .attr({ xmlns: SVG.ns, version: '1.1', width: '100%', height: '100%' }) + .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) + + /* create the node */ + this._defs = new SVG.Defs + this._defs.parent = this + this.node.appendChild(this._defs.node) + + /* turno of sub pixel offset by default */ + this.doSubPixelOffsetFix = false + + /* ensure correct rendering */ + if (this.parent.nodeName != 'svg') + this.stage() + } + + // Inherit from + , inherit: SVG.Container + + // Add class methods + , extend: { + // Hack for safari preventing text to be rendered in one line. + // 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. + // It also handles sub-pixel offset rendering properly. + stage: function() { + var check + , element = this + , wrapper = document.createElement('div') + + /* set temporary wrapper to position relative */ + wrapper.style.cssText = 'position:relative;height:100%;' + + /* put element into wrapper */ + element.parent.appendChild(wrapper) + wrapper.appendChild(element.node) + + /* check for dom:ready */ + check = function() { + if (document.readyState === 'complete') { + element.style('position:absolute;') + setTimeout(function() { + /* set position back to relative */ + element.style('position:relative;overflow:hidden;') + + /* remove temporary wrapper */ + element.parent.removeChild(element.node.parentNode) + element.node.parentNode.removeChild(element.node) + element.parent.appendChild(element.node) + + /* after wrapping is done, fix sub-pixel offset */ element.subPixelOffsetFix() - }) - - }, 5) - } else { - setTimeout(check, 10) + + /* make sure sub-pixel offset is fixed every time the window is resized */ + SVG.on(window, 'resize', function() { + element.subPixelOffsetFix() + }) + + }, 5) + } else { + setTimeout(check, 10) + } } - } - check() + check() - return this - } + return this + } - // Creates and returns defs element - , defs: function() { - return this._defs - } + // Creates and returns defs element + , defs: function() { + return this._defs + } - // Fix for possible sub-pixel offset. See: - // https://bugzilla.mozilla.org/show_bug.cgi?id=608812 - , subPixelOffsetFix: function() { - if (this.doSubPixelOffsetFix) { - var pos = this.node.getScreenCTM() + // Fix for possible sub-pixel offset. See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=608812 + , subPixelOffsetFix: function() { + if (this.doSubPixelOffsetFix) { + var pos = this.node.getScreenCTM() + + if (pos) + this + .style('left', (-pos.e % 1) + 'px') + .style('top', (-pos.f % 1) + 'px') + } - if (pos) - this - .style('left', (-pos.e % 1) + 'px') - .style('top', (-pos.f % 1) + 'px') + return this } - - return this - } - , fixSubPixelOffset: function() { - this.doSubPixelOffsetFix = true + , fixSubPixelOffset: function() { + this.doSubPixelOffsetFix = true - return this + return this + } } }) - SVG.Shape = function(element) { - this.constructor.call(this, element) - } - - // Inherit from SVG.Element - SVG.Shape.prototype = new SVG.Element - SVG.Use = function() { - this.constructor.call(this, SVG.create('use')) - } - - // Inherit from SVG.Shape - SVG.Use.prototype = new SVG.Shape + SVG.Shape = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) + } - // - SVG.extend(SVG.Use, { - // Use element as a reference - element: function(element) { - /* store target element */ - this.target = element + // Inherit from + , inherit: SVG.Element - /* set lined element */ - return this.attr('href', '#' + element, SVG.xlink) + }) + + SVG.Use = SVG.invent({ + // Initialize node + create: 'use' + + // Inherit from + , inherit: SVG.Shape + + // Add class methods + , extend: { + // Use element as a reference + element: function(element) { + /* store target element */ + this.target = element + + /* set lined element */ + return this.attr('href', '#' + element, SVG.xlink) + } } - }) - - // - SVG.extend(SVG.Container, { - // Create a use element - use: function(element) { - return this.put(new SVG.Use).element(element) + // Add parent method + , construct: { + // Create a use element + use: function(element) { + return this.put(new SVG.Use).element(element) + } } - }) - SVG.Rect = function() { - this.constructor.call(this, SVG.create('rect')) - } - - // Inherit from SVG.Shape - SVG.Rect.prototype = new SVG.Shape - - // - SVG.extend(SVG.Container, { - // Create a rect element - rect: function(width, height) { - return this.put(new SVG.Rect().size(width, height)) - } - + SVG.Rect = SVG.invent({ + // Initialize node + create: 'rect' + + // Inherit from + , inherit: SVG.Shape + + // Add parent method + , construct: { + // Create a rect element + rect: function(width, height) { + return this.put(new SVG.Rect().size(width, height)) + } + + } + }) - SVG.Ellipse = function() { - this.constructor.call(this, SVG.create('ellipse')) - } - - // Inherit from SVG.Shape - SVG.Ellipse.prototype = new SVG.Shape + SVG.Ellipse = SVG.invent({ + // Initialize node + create: 'ellipse' + + // Inherit from + , inherit: SVG.Shape + + // Add class methods + , extend: { + // Move over x-axis + x: function(x) { + return x == null ? this.cx() - this.attr('rx') : this.cx(x + this.attr('rx')) + } + // Move over y-axis + , y: function(y) { + return y == null ? this.cy() - this.attr('ry') : this.cy(y + this.attr('ry')) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX)) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY)) + } + // Set width of element + , width: function(width) { + return width == null ? this.attr('rx') * 2 : this.attr('rx', new SVG.Number(width).divide(2)) + } + // Set height of element + , height: function(height) { + return height == null ? this.attr('ry') * 2 : this.attr('ry', new SVG.Number(height).divide(2)) + } + // Custom size function + , size: function(width, height) { + var p = this._proportionalSize(width, height) - // - SVG.extend(SVG.Ellipse, { - // Move over x-axis - x: function(x) { - return x == null ? this.cx() - this.attr('rx') : this.cx(x + this.attr('rx')) - } - // Move over y-axis - , y: function(y) { - return y == null ? this.cy() - this.attr('ry') : this.cy(y + this.attr('ry')) - } - // Move by center over x-axis - , cx: function(x) { - return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX)) - } - // Move by center over y-axis - , cy: function(y) { - return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY)) - } - // Set width of element - , width: function(width) { - return width == null ? this.attr('rx') * 2 : this.attr('rx', new SVG.Number(width).divide(2)) - } - // Set height of element - , height: function(height) { - return height == null ? this.attr('ry') * 2 : this.attr('ry', new SVG.Number(height).divide(2)) + return this.attr({ + rx: new SVG.Number(p.width).divide(2) + , ry: new SVG.Number(p.height).divide(2) + }) + } + } - // Custom size function - , size: function(width, height) { - var p = this._proportionalSize(width, height) - return this.attr({ - rx: new SVG.Number(p.width).divide(2) - , ry: new SVG.Number(p.height).divide(2) - }) + // Add parent method + , construct: { + // Create circle element, based on ellipse + circle: function(size) { + return this.ellipse(size, size) + } + // Create an ellipse + , ellipse: function(width, height) { + return this.put(new SVG.Ellipse).size(width, height).move(0, 0) + } + } - - }) - // - SVG.extend(SVG.Container, { - // Create circle element, based on ellipse - circle: function(size) { - return this.ellipse(size, size) - } - // Create an ellipse - , ellipse: function(width, height) { - return this.put(new SVG.Ellipse).size(width, height).move(0, 0) - } - }) - - // Usage: - - // draw.ellipse(200, 100) - SVG.Line = function() { - this.constructor.call(this, SVG.create('line')) - } - - // Inherit from SVG.Shape - SVG.Line.prototype = new SVG.Shape - - // Add required methods - SVG.extend(SVG.Line, { - // Move over x-axis - x: function(x) { - var b = this.bbox() - - return x == null ? b.x : this.attr({ - x1: this.attr('x1') - b.x + x - , x2: this.attr('x2') - b.x + x - }) - } - // Move over y-axis - , y: function(y) { - var b = this.bbox() - - return y == null ? b.y : this.attr({ - y1: this.attr('y1') - b.y + y - , y2: this.attr('y2') - b.y + y - }) - } - // Move by center over x-axis - , cx: function(x) { - var half = this.bbox().width / 2 - return x == null ? this.x() + half : this.x(x - half) - } - // Move by center over y-axis - , cy: function(y) { - var half = this.bbox().height / 2 - return y == null ? this.y() + half : this.y(y - half) - } - // Set width of element - , width: function(width) { - var b = this.bbox() + SVG.Line = SVG.invent({ + // Initialize node + create: 'line' + + // Inherit from + , inherit: SVG.Shape + + // Add class methods + , extend: { + // Move over x-axis + x: function(x) { + var b = this.bbox() + + return x == null ? b.x : this.attr({ + x1: this.attr('x1') - b.x + x + , x2: this.attr('x2') - b.x + x + }) + } + // Move over y-axis + , y: function(y) { + var b = this.bbox() + + return y == null ? b.y : this.attr({ + y1: this.attr('y1') - b.y + y + , y2: this.attr('y2') - b.y + y + }) + } + // Move by center over x-axis + , cx: function(x) { + var half = this.bbox().width / 2 + return x == null ? this.x() + half : this.x(x - half) + } + // Move by center over y-axis + , cy: function(y) { + var half = this.bbox().height / 2 + return y == null ? this.y() + half : this.y(y - half) + } + // Set width of element + , width: function(width) { + var b = this.bbox() - return width == null ? b.width : this.attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', b.x + width) - } - // Set height of element - , height: function(height) { - var b = this.bbox() + return width == null ? b.width : this.attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', b.x + width) + } + // Set height of element + , height: function(height) { + var b = this.bbox() - return height == null ? b.height : this.attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', b.y + height) - } - // Set line size by width and height - , size: function(width, height) { - var p = this._proportionalSize(width, height) + return height == null ? b.height : this.attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', b.y + height) + } + // Set line size by width and height + , size: function(width, height) { + var p = this._proportionalSize(width, height) - return this.width(p.width).height(p.height) - } - // Set path data - , plot: function(x1, y1, x2, y2) { - return this.attr({ - x1: x1 - , y1: y1 - , x2: x2 - , y2: y2 - }) + return this.width(p.width).height(p.height) + } + // Set path data + , plot: function(x1, y1, x2, y2) { + return this.attr({ + x1: x1 + , y1: y1 + , x2: x2 + , y2: y2 + }) + } } - }) - - // - SVG.extend(SVG.Container, { - // Create a line element - line: function(x1, y1, x2, y2) { - return this.put(new SVG.Line().plot(x1, y1, x2, y2)) + // Add parent method + , construct: { + // Create a line element + line: function(x1, y1, x2, y2) { + return this.put(new SVG.Line().plot(x1, y1, x2, y2)) + } } - }) - SVG.Polyline = function() { - this.constructor.call(this, SVG.create('polyline')) - } - - // Inherit from SVG.Shape - SVG.Polyline.prototype = new SVG.Shape + SVG.Polyline = SVG.invent({ + // Initialize node + create: 'polyline' + + // Inherit from + , inherit: SVG.Shape + + // Add parent method + , construct: { + // Create a wrapped polyline element + polyline: function(p) { + return this.put(new SVG.Polyline).plot(p) + } + } + }) - SVG.Polygon = function() { - this.constructor.call(this, SVG.create('polygon')) - } + SVG.Polygon = SVG.invent({ + // Initialize node + create: 'polygon' - // Inherit from SVG.Shape - SVG.Polygon.prototype = new SVG.Shape + // Inherit from + , inherit: SVG.Shape + + // Add parent method + , construct: { + // Create a wrapped polygon element + polygon: function(p) { + return this.put(new SVG.Polygon).plot(p) + } + } + }) // Add polygon-specific functions SVG.extend(SVG.Polyline, SVG.Polygon, { @@ -2808,397 +2870,389 @@ } }) - - // - SVG.extend(SVG.Container, { - // Create a wrapped polyline element - polyline: function(p) { - return this.put(new SVG.Polyline).plot(p) - } - // Create a wrapped polygon element - , polygon: function(p) { - return this.put(new SVG.Polygon).plot(p) - } - - }) - SVG.Path = function() { - this.constructor.call(this, SVG.create('path')) - } - - // Inherit from SVG.Shape - SVG.Path.prototype = new SVG.Shape - - SVG.extend(SVG.Path, { - // Plot new poly points - plot: function(p) { - return this.attr('d', (this.array = new SVG.PathArray(p, [{ type:'M',x:0,y:0 }]))) - } - // Move by left top corner - , move: function(x, y) { - return this.attr('d', this.array.move(x, y)) - } - // Move by left top corner over x-axis - , x: function(x) { - return x == null ? this.bbox().x : this.move(x, this.bbox().y) - } - // Move by left top corner over y-axis - , y: function(y) { - return y == null ? this.bbox().y : this.move(this.bbox().x, y) - } - // Set element size to given width and height - , size: function(width, height) { - var p = this._proportionalSize(width, height) - - return this.attr('d', this.array.size(p.width, p.height)) - } - // Set width of element - , width: function(width) { - return width == null ? this.bbox().width : this.size(width, this.bbox().height) - } - // Set height of element - , height: function(height) { - return height == null ? this.bbox().height : this.size(this.bbox().width, height) + SVG.Path = SVG.invent({ + // Initialize node + create: 'path' + + // Inherit from + , inherit: SVG.Shape + + // Add class methods + , extend: { + // Plot new poly points + plot: function(p) { + return this.attr('d', (this.array = new SVG.PathArray(p, [{ type:'M',x:0,y:0 }]))) + } + // Move by left top corner + , move: function(x, y) { + return this.attr('d', this.array.move(x, y)) + } + // Move by left top corner over x-axis + , x: function(x) { + return x == null ? this.bbox().x : this.move(x, this.bbox().y) + } + // Move by left top corner over y-axis + , y: function(y) { + return y == null ? this.bbox().y : this.move(this.bbox().x, y) + } + // Set element size to given width and height + , size: function(width, height) { + var p = this._proportionalSize(width, height) + + return this.attr('d', this.array.size(p.width, p.height)) + } + // Set width of element + , width: function(width) { + return width == null ? this.bbox().width : this.size(width, this.bbox().height) + } + // Set height of element + , height: function(height) { + return height == null ? this.bbox().height : this.size(this.bbox().width, height) + } } - - }) - - // - SVG.extend(SVG.Container, { - // Create a wrapped path element - path: function(d) { - return this.put(new SVG.Path).plot(d) + + // Add parent method + , construct: { + // Create a wrapped path element + path: function(d) { + return this.put(new SVG.Path).plot(d) + } } - }) - SVG.Image = function() { - this.constructor.call(this, SVG.create('image')) - } - - // Inherit from SVG.Element - SVG.Image.prototype = new SVG.Shape - - // - SVG.extend(SVG.Image, { - // (re)load image - load: function(url) { - return (url ? this.attr('href', (this.src = url), SVG.xlink) : this) + SVG.Image = SVG.invent({ + // Initialize node + create: 'image' + + // Inherit from + , inherit: SVG.Shape + + // Add class methods + , extend: { + // (re)load image + load: function(url) { + return (url ? this.attr('href', (this.src = url), SVG.xlink) : this) + } } - }) - - // - SVG.extend(SVG.Container, { - // Create image element, load image and set its size - image: function(source, width, height) { - width = width != null ? width : 100 - return this.put(new SVG.Image().load(source).size(width, height != null ? height : width)) + + // Add parent method + , construct: { + // Create image element, load image and set its size + image: function(source, width, height) { + width = width != null ? width : 100 + return this.put(new SVG.Image().load(source).size(width, height != null ? height : width)) + } } - }) var _styleAttr = ('size family weight stretch variant style').split(' ') - SVG.Text = function() { - this.constructor.call(this, SVG.create('text')) - - /* define default style */ - this.styles = { - 'font-size': 16 - , 'font-family': 'Helvetica, Arial, sans-serif' - , 'text-anchor': 'start' - } - - this._leading = new SVG.Number('1.2em') - this._rebuild = true - } - - // Inherit from SVG.Element - SVG.Text.prototype = new SVG.Shape - - // - SVG.extend(SVG.Text, { - // Move over x-axis - x: function(x, a) { - /* act as getter */ - if (x == null) - return a ? this.attr('x') : this.bbox().x + SVG.Text = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('text')) - /* set x taking anchor in mind */ - if (!a) { - a = this.style('text-anchor') - x = a == 'start' ? x : a == 'end' ? x + this.bbox().width : x + this.bbox().width / 2 + /* define default style */ + this.styles = { + 'font-size': 16 + , 'font-family': 'Helvetica, Arial, sans-serif' + , 'text-anchor': 'start' } - - /* move lines as well if no textPath si present */ - if (!this.textPath) - this.lines.each(function() { if (this.newLined) this.x(x) }) - - return this.attr('x', x) - } - // Move center over x-axis - , cx: function(x, a) { - return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) - } - // Move center over y-axis - , cy: function(y, a) { - return y == null ? this.bbox().cy : this.y(a ? y : y - this.bbox().height / 2) - } - // Move element to given x and y values - , move: function(x, y, a) { - return this.x(x, a).y(y) - } - // Move element by its center - , center: function(x, y, a) { - return this.cx(x, a).cy(y, a) - } - // Set the text content - , text: function(text) { - /* act as getter */ - if (text == null) - return this.content - - /* remove existing lines */ - this.clear() - if (typeof text === 'function') { - this._rebuild = false + this._leading = new SVG.Number('1.2em') + this._rebuild = true + } - text.call(this, this) + // Inherit from + , inherit: SVG.Shape - } else { - this._rebuild = true + // Add class methods + , extend: { + // Move over x-axis + x: function(x, a) { + /* act as getter */ + if (x == null) + return a ? this.attr('x') : this.bbox().x + + /* set x taking anchor in mind */ + if (!a) { + a = this.style('text-anchor') + x = a == 'start' ? x : a == 'end' ? x + this.bbox().width : x + this.bbox().width / 2 + } + + /* move lines as well if no textPath si present */ + if (!this.textPath) + this.lines.each(function() { if (this.newLined) this.x(x) }) - /* make sure text is not blank */ - text = SVG.regex.isBlank.test(text) ? 'text' : text + return this.attr('x', x) + } + // Move center over x-axis + , cx: function(x, a) { + return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) + } + // Move center over y-axis + , cy: function(y, a) { + return y == null ? this.bbox().cy : this.y(a ? y : y - this.bbox().height / 2) + } + // Move element to given x and y values + , move: function(x, y, a) { + return this.x(x, a).y(y) + } + // Move element by its center + , center: function(x, y, a) { + return this.cx(x, a).cy(y, a) + } + // Set the text content + , text: function(text) { + /* act as getter */ + if (text == null) + return this.content - var i, il - , lines = text.split('\n') + /* remove existing lines */ + this.clear() - /* build new lines */ - for (i = 0, il = lines.length; i < il; i++) - this.tspan(lines[i]).newLine() + if (typeof text === 'function') { + this._rebuild = false - this.rebuild() - } - - return this - } - // Create a tspan - , tspan: function(text) { - var node = this.textPath ? this.textPath.node : this.node - , tspan = new SVG.TSpan().text(text) - , style = this.style() - - /* add new tspan */ - node.appendChild(tspan.node) - this.lines.add(tspan) + text.call(this, this) + + } else { + this._rebuild = true - /* add style if any */ - if (!SVG.regex.isBlank.test(style)) - tspan.style(style) + /* make sure text is not blank */ + text = SVG.regex.isBlank.test(text) ? 'text' : text + + var i, il + , lines = text.split('\n') + + /* build new lines */ + for (i = 0, il = lines.length; i < il; i++) + this.tspan(lines[i]).newLine() - /* store content */ - this.content += text + this.rebuild() + } + + return this + } + // Create a tspan + , tspan: function(text) { + var node = this.textPath ? this.textPath.node : this.node + , tspan = new SVG.TSpan().text(text) + , style = this.style() + + /* add new tspan */ + node.appendChild(tspan.node) + this.lines.add(tspan) - /* store text parent */ - tspan.parent = this + /* add style if any */ + if (!SVG.regex.isBlank.test(style)) + tspan.style(style) - return tspan - } - // 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 */ - value = new SVG.Number(value) - this._leading = value - - /* apply leading */ - this.lines.each(function() { - if (this.newLined) - this.attr('dy', value) - }) + /* store content */ + this.content += text - return this - } - // rebuild appearance type - , rebuild: function() { - var self = this + /* store text parent */ + tspan.parent = this - /* define position of all lines */ - if (this._rebuild) { - this.lines.attr({ - x: this.attr('x') - , dy: this._leading - , style: this.style() + return tspan + } + // 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 */ + value = new SVG.Number(value) + this._leading = value + + /* apply leading */ + this.lines.each(function() { + if (this.newLined) + this.attr('dy', value) }) + + return this } + // rebuild appearance type + , rebuild: function() { + var self = this + + /* define position of all lines */ + if (this._rebuild) { + this.lines.attr({ + x: this.attr('x') + , dy: this._leading + , style: this.style() + }) + } - return this - } - // Clear all lines - , clear: function() { - var node = this.textPath ? this.textPath.node : this.node + return this + } + // Clear all lines + , clear: function() { + var node = this.textPath ? this.textPath.node : this.node - /* remove existing child nodes */ - while (node.hasChildNodes()) - node.removeChild(node.lastChild) - - /* refresh lines */ - delete this.lines - this.lines = new SVG.Set - - /* initialize content */ - this.content = '' + /* remove existing child nodes */ + while (node.hasChildNodes()) + node.removeChild(node.lastChild) + + /* refresh lines */ + delete this.lines + this.lines = new SVG.Set + + /* initialize content */ + this.content = '' - return this + return this + } } - }) - - // - SVG.extend(SVG.Container, { - // Create text element - text: function(text) { - return this.put(new SVG.Text).text(text) + // Add parent method + , construct: { + // Create text element + text: function(text) { + return this.put(new SVG.Text).text(text) + } } - }) - // tspan class - SVG.TSpan = function() { - this.constructor.call(this, SVG.create('tspan')) - } + SVG.TSpan = SVG.invent({ + // Initialize node + create: 'tspan' - // Inherit from SVG.Shape - SVG.TSpan.prototype = new SVG.Shape + // Inherit from + , inherit: SVG.Shape - // Include the container object - SVG.extend(SVG.TSpan, { - // Set text content - text: function(text) { - this.node.appendChild(document.createTextNode(text)) - - return this - } - // Shortcut dx - , dx: function(dx) { - return this.attr('dx', dx) - } - // Shortcut dy - , dy: function(dy) { - return this.attr('dy', dy) - } - // Create new line - , newLine: function() { - this.newLined = true - this.parent.content += '\n' - this.dy(this.parent._leading) - return this.attr('x', this.parent.x()) + // Add class methods + , extend: { + // Set text content + text: function(text) { + this.node.appendChild(document.createTextNode(text)) + + return this + } + // Shortcut dx + , dx: function(dx) { + return this.attr('dx', dx) + } + // Shortcut dy + , dy: function(dy) { + return this.attr('dy', dy) + } + // Create new line + , newLine: function() { + this.newLined = true + this.parent.content += '\n' + this.dy(this.parent._leading) + return this.attr('x', this.parent.x()) + } } - + }) + - SVG.TextPath = function() { - this.constructor.call(this, SVG.create('textPath')) - } + SVG.TextPath = SVG.invent({ + // Initialize node + create: 'textPath' - // Inherit from SVG.Element - SVG.TextPath.prototype = new SVG.Element + // Inherit from + , inherit: SVG.Element - // - SVG.extend(SVG.Text, { - // Create path for text to run on - path: function(d) { - /* create textPath element */ - this.textPath = new SVG.TextPath + // Define parent class + , parent: SVG.Text - /* move lines to textpath */ - while(this.node.hasChildNodes()) - this.textPath.node.appendChild(this.node.firstChild) + // Add parent method + , construct: { + // Create path for text to run on + path: function(d) { + /* create textPath element */ + this.textPath = new SVG.TextPath - /* add textPath element as child node */ - this.node.appendChild(this.textPath.node) + /* move lines to textpath */ + while(this.node.hasChildNodes()) + this.textPath.node.appendChild(this.node.firstChild) - /* create path in defs */ - this.track = this.doc().defs().path(d, true) + /* add textPath element as child node */ + this.node.appendChild(this.textPath.node) - /* create circular reference */ - this.textPath.parent = this + /* create path in defs */ + this.track = this.doc().defs().path(d, true) - /* link textPath to path and add content */ - this.textPath.attr('href', '#' + this.track, SVG.xlink) + /* create circular reference */ + this.textPath.parent = this - return this - } - // Plot path if any - , plot: function(d) { - if (this.track) this.track.plot(d) - return this - } + /* link textPath to path and add content */ + this.textPath.attr('href', '#' + this.track, SVG.xlink) + return this + } + // Plot path if any + , plot: function(d) { + if (this.track) this.track.plot(d) + return this + } + } }) - SVG.Nested = function() { - this.constructor.call(this, SVG.create('svg')) - - this.style('overflow', 'visible') - } - - // Inherit from SVG.Container - SVG.Nested.prototype = new SVG.Container + SVG.Nested = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('svg')) + + this.style('overflow', 'visible') + } - // - SVG.extend(SVG.Container, { - // Create nested svg document + // Inherit from + , inherit: SVG.Container + + // Add parent method + , construct: { + // Create nested svg document nested: function() { - return this.put(new SVG.Nested) + return this.put(new SVG.Nested) + } } - }) - SVG.A = function() { - this.constructor.call(this, SVG.create('a')) - } - - // Inherit from SVG.Parent - SVG.A.prototype = new SVG.Container - - // - SVG.extend(SVG.A, { - // Link url - to: function(url) { - return this.attr('href', url, SVG.xlink) - } - // Link show attribute - , show: function(target) { - return this.attr('show', target, SVG.xlink) - } - // Link target attribute - , target: function(target) { - return this.attr('target', target) - } - - }) - - // - SVG.extend(SVG.Container, { - // Create a hyperlink element - link: function(url) { - return this.put(new SVG.A).to(url) + SVG.A = SVG.invent({ + // Initialize node + create: 'a' + + // Inherit from + , inherit: SVG.Container + + // Add class methods + , extend: { + // Link url + to: function(url) { + return this.attr('href', url, SVG.xlink) + } + // Link show attribute + , show: function(target) { + return this.attr('show', target, SVG.xlink) + } + // Link target attribute + , target: function(target) { + return this.attr('target', target) + } } + // Add parent method + , construct: { + // Create a hyperlink element + link: function(url) { + return this.put(new SVG.A).to(url) + } + } }) - // SVG.extend(SVG.Element, { // Create a hyperlink element linkTo: function(url) { @@ -3316,95 +3370,106 @@ - SVG.Set = function() { - /* set initial state */ - this.clear() - } + SVG.Set = SVG.invent({ + // Initialize + create: function() { + /* set initial state */ + this.clear() + } - // Set FX class - SVG.SetFX = function(set) { - /* store reference to set */ - this.set = set - } + // Add class methods + , extend: { + // Add element to set + add: function() { + var i, il, elements = [].slice.call(arguments) - // - SVG.extend(SVG.Set, { - // Add element to set - add: function() { - var i, il, elements = [].slice.call(arguments) + for (i = 0, il = elements.length; i < il; i++) + this.members.push(elements[i]) + + return this + } + // Remove element from set + , remove: function(element) { + var i = this.index(element) + + /* remove given child */ + if (i > -1) + this.members.splice(i, 1) - for (i = 0, il = elements.length; i < il; i++) - this.members.push(elements[i]) - - return this - } - // Remove element from set - , remove: function(element) { - var i = this.index(element) - - /* remove given child */ - if (i > -1) - this.members.splice(i, 1) + return this + } + // Iterate over all members + , each: function(block) { + for (var i = 0, il = this.members.length; i < il; i++) + block.apply(this.members[i], [i, this.members]) - return this - } - // Iterate over all members - , each: function(block) { - for (var i = 0, il = this.members.length; i < il; i++) - block.apply(this.members[i], [i, this.members]) + return this + } + // Restore to defaults + , clear: function() { + /* initialize store */ + this.members = [] - return this - } - // Restore to defaults - , clear: function() { - /* initialize store */ - this.members = [] + return this + } + // Checks if a given element is present in set + , has: function(element) { + return this.index(element) >= 0 + } + // retuns index of given element in set + , index: function(element) { + return this.members.indexOf(element) + } + // Get member at given index + , get: function(i) { + return this.members[i] + } + // Default value + , valueOf: function() { + return this.members + } + // Get the bounding box of all members included or empty box if set has no items + , bbox: function(){ + var box = new SVG.BBox() + + /* return an empty box of there are no members */ + if (this.members.length == 0) + return box + + /* get the first rbox and update the target bbox */ + var rbox = this.members[0].rbox() + box.x = rbox.x + box.y = rbox.y + box.width = rbox.width + box.height = rbox.height + + this.each(function() { + /* user rbox for correct position and visual representation */ + box = box.merge(this.rbox()) + }) - return this - } - // Checks if a given element is present in set - , has: function(element) { - return this.index(element) >= 0 - } - // retuns index of given element in set - , index: function(element) { - return this.members.indexOf(element) - } - // Get member at given index - , get: function(i) { - return this.members[i] + return box + } } - // Default value - , valueOf: function() { - return this.members + + // Add parent method + , construct: { + // Create a new set + set: function() { + return new SVG.Set + } } - // Get the bounding box of all members included or empty box if set has no items - , bbox: function(){ - var box = new SVG.BBox() - - /* return an empty box of there are no members */ - if (this.members.length == 0) - return box - - /* get the first rbox and update the target bbox */ - var rbox = this.members[0].rbox() - box.x = rbox.x - box.y = rbox.y - box.width = rbox.width - box.height = rbox.height - - this.each(function() { - /* user rbox for correct position and visual representation */ - box = box.merge(this.rbox()) - }) + }) - return box + SVG.SetFX = SVG.invent({ + // Initialize node + create: function(set) { + /* store reference to set */ + this.set = set } }) - - // Alias methods SVG.Set.inherit = function() { var m @@ -3445,16 +3510,6 @@ }) } - // - SVG.extend(SVG.Container, { - // Create a new set - set: function() { - return new SVG.Set - } - - }) - - diff --git a/dist/svg.min.js b/dist/svg.min.js index 2e2a5ea..bba1267 100644 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,2 +1,2 @@ -(function(){function t(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:t instanceof SVG.Color||t instanceof SVG.Number?t.at(e):1>e?t.from:t.to}if(this.SVG=function(t){return SVG.parser||SVG.prepare(),SVG.supported?new SVG.Doc(t):void 0},SVG.ns="http://www.w3.org/2000/svg",SVG.xmlns="http://www.w3.org/2000/xmlns/",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];SVG.Set&&SVG.Set.inherit&&SVG.Set.inherit()},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.prepare=function(){var t=document.getElementsByTagName("body")[0]||document.getElementsByTagName("svg")[0],e=new SVG.Doc(t).size(2,2).style("opacity:0;position:fixed;left:100%;top:100%;");SVG.parser={body:t,draw:e,poly:e.polygon().node,path:e.path().node}},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/},SVG.defaults={matrix:"1 0 0 1 0 0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000"},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},morph:function(t){return this.destination=new SVG.Color(t),this},at:function(t){return this.destination?(t=0>t?0:t>1?1:t,new SVG.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},SVG.Color.isColor=function(t){return SVG.Color.isRgb(t)||SVG.Color.test(t)},SVG.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},SVG.extend(SVG.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.lengtht;t++)-1==i.indexOf(this.value[t])&&i.push(this.value[t]);return this.value=i},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push(this.value[e]+(this.destination[e]-this.value[e])*t);return new SVG.Array(n)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)},split:function(t){return t.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")}}),SVG.PointArray=function(){this.constructor.apply(this,arguments)},SVG.PointArray.prototype=new SVG.Array,SVG.extend(SVG.PointArray,{toString:function(){for(var t=0,e=this.value.length,i=[];e>t;t++)i.push(this.value[t].join(","));return i.join(" ")},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push([this.value[e][0]+(this.destination[e][0]-this.value[e][0])*t,this.value[e][1]+(this.destination[e][1]-this.value[e][1])*t]);return new SVG.PointArray(n)},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;t=this.split(t);for(var e,i=0,n=t.length,r=[];n>i;i++)e=t[i].split(","),r.push([parseFloat(e[0]),parseFloat(e[1])]);return r},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.x;return this},bbox:function(){return this._cachedBBox?this._cachedBBox:(SVG.parser.poly.setAttribute("points",this.toString()),SVG.parser.poly.getBBox())}}),SVG.PathArray=function(t,e){this.constructor.call(this,t,e)},SVG.PathArray.prototype=new SVG.Array,SVG.extend(SVG.PathArray,{toString:function(){for(var t,e=0,i=this.value.length,n=[];i>e;e++){switch(t=[this.value[e].type],this.value[e].type){case"H":t.push(this.value[e].x);break;case"V":t.push(this.value[e].y);break;case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(this.value[e].type)&&t.push(this.value[e].x1,this.value[e].y1),/[CS]/.test(this.value[e].type)&&t.push(this.value[e].x2,this.value[e].y2),t.push(this.value[e].x,this.value[e].y);break;case"A":t.push(this.value[e].rx,this.value[e].ry,this.value[e].angle,this.value[e].largeArcFlag,this.value[e].sweepFlag,this.value[e].x,this.value[e].y)}n.push(t.join(" "))}return n.join(" ")},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x+=t;break;case"V":this.value[n].y+=e;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x+=t,this.value[n].y+=e,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1+=t,this.value[n].y1+=e),/[CS]/.test(this.value[n].type)&&(this.value[n].x2+=t,this.value[n].y2+=e);break;case"A":this.value[n].x+=t,this.value[n].y+=e}return this},size:function(t,e){for(var i=this.bbox(),n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x;break;case"V":this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x,this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1=(this.value[n].x1-i.x)*t/i.width+i.x,this.value[n].y1=(this.value[n].y1-i.y)*e/i.height+i.y),/[CS]/.test(this.value[n].type)&&(this.value[n].x2=(this.value[n].x2-i.x)*t/i.width+i.x,this.value[n].y2=(this.value[n].y2-i.y)*e/i.height+i.y);break;case"A":this.value[n].values.rx=this.value[n].values.rx*t/i.width,this.value[n].values.ry=this.value[n].values.ry*e/i.height,this.value[n].values.x=(this.value[n].values.x-i.x)*t/i.width+i.x,this.value[n].values.y=(this.value[n].values.y-i.y)*e/i.height+i.y}return this},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;var e,i,n,r,s,h,o,a,u,l,c,f=0,p=0;for(SVG.parser.path.setAttribute("d",t),c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){if(l=c.getItem(e),u=l.pathSegTypeAsLetter,/[MLHVCSQTA]/.test(u))"x"in l&&(f=l.x),"y"in l&&(p=l.y);else switch("x1"in l&&(s=f+l.x1),"x2"in l&&(o=f+l.x2),"y1"in l&&(h=p+l.y1),"y2"in l&&(a=p+l.y2),"x"in l&&(f+=l.x),"y"in l&&(p+=l.y),u){case"m":c.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(f,p),e);break;case"l":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(f,p),e);break;case"h":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(f),e);break;case"v":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(p),e);break;case"c":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(f,p,s,h,o,a),e);break;case"s":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(f,p,o,a),e);break;case"q":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(f,p,s,h),e);break;case"t":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(f,p),e);break;case"a":c.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(f,p,l.r1,l.r2,l.angle,l.largeArcFlag,l.sweepFlag),e);break;case"z":case"Z":f=n,p=r}/[Mm]/.test(u)&&(n=f,r=p)}for(t=[],c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){switch(l=c.getItem(e),u={},l.pathSegTypeAsLetter){case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(l.pathSegTypeAsLetter)&&(u.x1=l.x1,u.y1=l.y1),/[SC]/.test(l.pathSegTypeAsLetter)&&(u.x2=l.x2,u.y2=l.y2);break;case"A":u={r1:l.r1,r2:l.r2,a:l.angle,l:l.largeArcFlag,s:l.sweepFlag}}u.type=l.pathSegTypeAsLetter,u.x=l.x,u.y=l.y,t.push(u)}return t},bbox:function(){return this._cachedBBox?this._cachedBBox:(SVG.parser.path.setAttribute("d",this.toString()),SVG.parser.path.getBBox())}}),SVG.Number=function(t){switch(this.value=0,this.unit="",typeof t){case"number":this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;break;case"string":var e=t.match(SVG.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]&&(this.value/=100),this.unit=e[2]);break;default:t instanceof SVG.Number&&(this.value=t.value,this.unit=t.unit)}},SVG.extend(SVG.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:this.value)+this.unit},valueOf:function(){return this.value},plus:function(t){return this.value=this+new SVG.Number(t),this},minus:function(t){return this.plus(-new SVG.Number(t))},times:function(t){return this.value=this*new SVG.Number(t),this},divide:function(t){return this.value=this/new SVG.Number(t),this},to:function(t){return"string"==typeof t&&(this.unit=t),this},morph:function(t){return this.destination=new SVG.Number(t),this},at:function(t){return this.destination?new SVG.Number(this.destination).minus(this).times(t).plus(this):this}}),SVG.ViewBox=function(t){var e,i,n,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);for(n=new SVG.Number(t.width()),r=new SVG.Number(t.height());"%"==n.unit;)s*=n.value,n=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetWidth:t.width());for(;"%"==r.unit;)h*=r.value,r=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetHeight:t.height());this.x=o.x,this.y=o.y,this.width=n*s,this.height=r*h,this.zoom=1,a&&(e=parseFloat(a[0]),i=parseFloat(a[1]),n=parseFloat(a[2]),r=parseFloat(a[3]),this.zoom=this.width/this.height>n/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.RBox,{merge:function(t){var e=new SVG.RBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.Element=function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},SVG.extend(SVG.Element,{x:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({width:new SVG.Number(i.width),height:new SVG.Number(i.height)})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return this._isStyle(t)?"text"==t?this.content:"leading"==t&&this.leading?this.leading():this.style(t):(e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e);if("style"==t)return this.style(e);if("x"==t&&Array.isArray(this.lines))for(i=this.lines.length-1;i>=0;i--)this.lines[i].attr(t,e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),SVG.Color.test(e)||SVG.Color.isRgb(e)?e=new SVG.Color(e):"number"==typeof e?e=new SVG.Number(e):Array.isArray(e)&&(e=new SVG.Array(e)),null!=i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),this._isStyle(t)&&("text"==t?this.text(e):"leading"==t&&this.leading?this.leading(e):this.style(t,e),this.rebuild&&this.rebuild(t,e))}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(2>arguments.length)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+" "+this.trans.b+" "+this.trans.c+" "+this.trans.d+" "+this.trans.e+" "+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+" "+(null==t.cx?this.bbox().cx:t.cx)+" "+(null==t.cy?this.bbox().cy:t.cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+" "+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+new SVG.Number(t.x/t.scaleX)+" "+new SVG.Number(t.y/t.scaleY)+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(2>arguments.length)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;t.length>i;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&i.x+i.width>t&&i.y+i.height>e},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},toString:function(){return this.attr("id")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_isStyle:function(t){return"string"==typeof t?SVG.regex.test(t,"isStyle"):!1},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t},_proportionalSize:function(t,e){if(null==t||null==e){var i=this.bbox();null==e?e=i.height/i.width*t:null==t&&(t=i.width/i.height*e)}return{width:t,height:e}}}),SVG.Parent=function(t){this.constructor.call(this,t)},SVG.Parent.prototype=new SVG.Element,SVG.extend(SVG.Parent,{children:function(){return this._children||(this._children=[])},add:function(t,e){return this.has(t)||(e=null==e?this.children().length:e,t.parent&&t.parent.children().splice(t.parent.index(t),1),this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this),this._defs&&(this.node.removeChild(this._defs.node),this.node.appendChild(this._defs.node)),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return this.children().indexOf(t)},get:function(t){return this.children()[t]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;n>i;i++)r[i]instanceof SVG.Element&&t.apply(r[i],[i,r]),e&&r[i]instanceof SVG.Container&&r[i].each(t,e);return this},removeElement:function(t){return this.children().splice(this.index(t),1),this.node.removeChild(t.node),t.parent=null,this},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&this._defs.clear(),this},defs:function(){return this.doc().defs()}}),SVG.Container=function(t){this.constructor.call(this,t)},SVG.Container.prototype=new SVG.Parent,SVG.extend(SVG.Container,{viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}),SVG.FX=function(t){this.target=t},SVG.extend(SVG.FX,{animate:function(e,i,n){var r,s,h,o,a=this.target,u=this;return"object"==typeof e&&(n=e.delay,i=e.ease,e=e.duration),e=null==e?1e3:e,i=i||"<>",u.to=function(e){var n;if(e=0>e?0:e>1?1:e,null==r){r=[];for(o in u.attrs)r.push(o);if(a.morphArray&&(u._plot||r.indexOf("points")>-1)){var l,c=new a.morphArray(u._plot||u.attrs.points||a.array);u._size&&c.size(u._size.width.to,u._size.height.to),l=c.bbox(),u._x?c.move(u._x.to,l.y):u._cx&&c.move(u._cx.to-l.width/2,l.y),l=c.bbox(),u._y?c.move(l.x,u._y.to):u._cy&&c.move(l.x,u._cy.to-l.height/2),delete u._x,delete u._y,delete u._cx,delete u._cy,delete u._size,u._plot=a.array.morph(c)}}if(null==s){s=[];for(o in u.trans)s.push(o)}if(null==h){h=[];for(o in u.styles)h.push(o)}for(e="<>"==i?-Math.cos(e*Math.PI)/2+.5:">"==i?Math.sin(e*Math.PI/2):"<"==i?-Math.cos(e*Math.PI/2)+1:"-"==i?e:"function"==typeof i?i(e):e,u._plot?a.plot(u._plot.at(e)):(u._x?a.x(t(u._x,e)):u._cx&&a.cx(t(u._cx,e)),u._y?a.y(t(u._y,e)):u._cy&&a.cy(t(u._cy,e)),u._size&&a.size(t(u._size.width,e),t(u._size.height,e))),u._viewbox&&a.viewbox(t(u._viewbox.x,e),t(u._viewbox.y,e),t(u._viewbox.width,e),t(u._viewbox.height,e)),n=r.length-1;n>=0;n--)a.attr(r[n],t(u.attrs[r[n]],e));for(n=s.length-1;n>=0;n--)a.transform(s[n],t(u.trans[s[n]],e));for(n=h.length-1;n>=0;n--)a.style(h[n],t(u.styles[h[n]],e));u._during&&u._during.call(a,e,function(i,n){return t({from:i,to:n},e)})},"number"==typeof e&&(this.timeout=setTimeout(function(){var t=(new Date).getTime();u.situation={interval:1e3/60,start:t,play:!0,finish:t+e,duration:e},u.render=function(){if(u.situation.play===!0){var t=(new Date).getTime(),r=t>u.situation.finish?1:(t-u.situation.start)/e;u.to(r),t>u.situation.finish?(u._plot&&a.plot(new SVG.PointArray(u._plot.destination).settle()),u._loop===!0||"number"==typeof u._loop&&u._loop>1?("number"==typeof u._loop&&--u._loop,u.animate(e,i,n)):u._after?u._after.apply(a,[u]):u.stop()):requestAnimFrame(u.render)}else requestAnimFrame(u.render)},u.render()},n||0)),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else{var n=this.target.attr(t);this.attrs[t]=SVG.Color.isColor(n)?new SVG.Color(n).morph(e):SVG.regex.unit.test(n)?new SVG.Number(n).morph(e):{from:n,to:e}}return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},plot:function(t){return this._plot=t,this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var r=this.target.viewbox();this._viewbox={x:{from:r.x,to:t},y:{from:r.y,to:e},width:{from:r.width,to:i},height:{from:r.height,to:n}}}return this},update:function(t){return this.target instanceof SVG.Stop&&(null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},loop:function(t){return this._loop=t||!0,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),this.attrs={},this.trans={},this.styles={},this.situation={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._plot,delete this._loop,delete this._after,delete this._during,delete this._viewbox,this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var t=(new Date).getTime()-this.situation.pause;this.situation.finish+=t,this.situation.start+=t,this.situation.play=!0}return this}}),SVG.extend(SVG.Element,{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),SVG.extend(SVG.Element,SVG.FX,{relative:function(){var t,e=this;return{x:function(i){return t=e.bbox(),e.x(t.x+(i||0))},y:function(i){return t=e.bbox(),e.y(t.y+(i||0))},move:function(t,e){return this.x(t),this.y(e)}}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.Defs=function(){this.constructor.call(this,SVG.create("defs"))},SVG.Defs.prototype=new SVG.Container,SVG.G=function(){this.constructor.call(this,SVG.create("g"))},SVG.G.prototype=new SVG.Container,SVG.extend(SVG.G,{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)}}),SVG.extend(SVG.Container,{group:function(){return this.put(new SVG.G)}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){return this.parent.index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position();return this.parent.removeElement(this).put(this,t+1)},backward:function(){var t=this.position();return t>0&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.position()>0&&this.parent.removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent.add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent.add(t,e+1),this}}),SVG.Mask=function(){this.constructor.call(this,SVG.create("mask")),this.targets=[]},SVG.Mask.prototype=new SVG.Container,SVG.extend(SVG.Mask,{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return delete this.targets,this.parent.removeElement(this),this}}),SVG.extend(SVG.Element,{maskWith:function(t){return this.masker=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),SVG.extend(SVG.Container,{mask:function(){return this.defs().put(new SVG.Mask)}}),SVG.Clip=function(){this.constructor.call(this,SVG.create("clipPath")),this.targets=[]},SVG.Clip.prototype=new SVG.Container,SVG.extend(SVG.Clip,{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return delete this.targets,this.parent.removeElement(this),this}}),SVG.extend(SVG.Element,{clipWith:function(t){return this.clipper=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),SVG.extend(SVG.Container,{clip:function(){return this.defs().put(new SVG.Clip)}}),SVG.Gradient=function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},SVG.Gradient.prototype=new SVG.Container,SVG.extend(SVG.Gradient,{from:function(t,e){return"radial"==this.type?this.attr({fx:new SVG.Number(t),fy:new SVG.Number(e)}):this.attr({x1:new SVG.Number(t),y1:new SVG.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new SVG.Number(t),cy:new SVG.Number(e)}):this.attr({x2:new SVG.Number(t),y2:new SVG.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new SVG.Number(t)}):this},at:function(t){return this.put(new SVG.Stop(t))},update:function(t){return this.clear(),t(this),this},fill:function(){return"url(#"+this.attr("id")+")"},toString:function(){return this.fill()}}),SVG.extend(SVG.Defs,{gradient:function(t,e){var i=this.put(new SVG.Gradient(t));return e(i),i}}),SVG.extend(SVG.Container,{gradient:function(t,e){return this.defs().gradient(t,e)}}),SVG.Stop=function(t){this.constructor.call(this,SVG.create("stop")),this.update(t)},SVG.Stop.prototype=new SVG.Element,SVG.extend(SVG.Stop,{update:function(t){return null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset)),this}}),SVG.Doc=function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xmlns:xlink",SVG.xlink,SVG.xmlns),this._defs=new SVG.Defs,this._defs.parent=this,this.node.appendChild(this._defs.node),this.doSubPixelOffsetFix=!1,"svg"!=this.parent.nodeName&&this.stage()},SVG.Doc.prototype=new SVG.Container,SVG.extend(SVG.Doc,{stage:function(){var t,e=this,i=document.createElement("div");return i.style.cssText="position:relative;height:100%;",e.parent.appendChild(i),i.appendChild(e.node),t=function(){"complete"===document.readyState?(e.style("position:absolute;"),setTimeout(function(){e.style("position:relative;overflow:hidden;"),e.parent.removeChild(e.node.parentNode),e.node.parentNode.removeChild(e.node),e.parent.appendChild(e.node),e.subPixelOffsetFix(),SVG.on(window,"resize",function(){e.subPixelOffsetFix()})},5)):setTimeout(t,10)},t(),this},defs:function(){return this._defs},subPixelOffsetFix:function(){if(this.doSubPixelOffsetFix){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){return this.doSubPixelOffsetFix=!0,this}}),SVG.Shape=function(t){this.constructor.call(this,t)},SVG.Shape.prototype=new SVG.Element,SVG.Use=function(){this.constructor.call(this,SVG.create("use"))},SVG.Use.prototype=new SVG.Shape,SVG.extend(SVG.Use,{element:function(t){return this.target=t,this.attr("href","#"+t,SVG.xlink)}}),SVG.extend(SVG.Container,{use:function(t){return this.put(new SVG.Use).element(t)}}),SVG.Rect=function(){this.constructor.call(this,SVG.create("rect"))},SVG.Rect.prototype=new SVG.Shape,SVG.extend(SVG.Container,{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}),SVG.Ellipse=function(){this.constructor.call(this,SVG.create("ellipse"))},SVG.Ellipse.prototype=new SVG.Shape,SVG.extend(SVG.Ellipse,{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new SVG.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new SVG.Number(t).divide(this.trans.scaleY))},width:function(t){return null==t?2*this.attr("rx"):this.attr("rx",new SVG.Number(t).divide(2))},height:function(t){return null==t?2*this.attr("ry"):this.attr("ry",new SVG.Number(t).divide(2))},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({rx:new SVG.Number(i.width).divide(2),ry:new SVG.Number(i.height).divide(2)})}}),SVG.extend(SVG.Container,{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put(new SVG.Ellipse).size(t,e).move(0,0)}}),SVG.Line=function(){this.constructor.call(this,SVG.create("line"))},SVG.Line.prototype=new SVG.Shape,SVG.extend(SVG.Line,{x:function(t){var e=this.bbox();return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},width:function(t){var e=this.bbox();return null==t?e.width:this.attr(this.attr("x1")e;e++)this.tspan(n[e]).newLine();this.rebuild()}return this},tspan:function(t){var e=this.textPath?this.textPath.node:this.node,i=(new SVG.TSpan).text(t),n=this.style();return e.appendChild(i.node),this.lines.add(i),SVG.regex.isBlank.test(n)||i.style(n),this.content+=t,i.parent=this,i},size:function(t){return this.attr("font-size",t)},leading:function(t){return null==t?this._leading:(t=new SVG.Number(t),this._leading=t,this.lines.each(function(){this.newLined&&this.attr("dy",t)}),this)},rebuild:function(){return this._rebuild&&this.lines.attr({x:this.attr("x"),dy:this._leading,style:this.style()}),this},clear:function(){for(var t=this.textPath?this.textPath.node:this.node;t.hasChildNodes();)t.removeChild(t.lastChild);return delete this.lines,this.lines=new SVG.Set,this.content="",this}}),SVG.extend(SVG.Container,{text:function(t){return this.put(new SVG.Text).text(t)}}),SVG.TSpan=function(){this.constructor.call(this,SVG.create("tspan"))},SVG.TSpan.prototype=new SVG.Shape,SVG.extend(SVG.TSpan,{text:function(t){return this.node.appendChild(document.createTextNode(t)),this},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){return this.newLined=!0,this.parent.content+="\n",this.dy(this.parent._leading),this.attr("x",this.parent.x())}}),SVG.TextPath=function(){this.constructor.call(this,SVG.create("textPath"))},SVG.TextPath.prototype=new SVG.Element,SVG.extend(SVG.Text,{path:function(t){for(this.textPath=new SVG.TextPath;this.node.hasChildNodes();)this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t,!0),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,SVG.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}),SVG.Nested=function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},SVG.Nested.prototype=new SVG.Container,SVG.extend(SVG.Container,{nested:function(){return this.put(new SVG.Nested)}}),SVG.A=function(){this.constructor.call(this,SVG.create("a"))},SVG.A.prototype=new SVG.Container,SVG.extend(SVG.A,{to:function(t){return this.attr("href",t,SVG.xlink)},show:function(t){return this.attr("show",t,SVG.xlink)},target:function(t){return this.attr("target",t)}}),SVG.extend(SVG.Container,{link:function(t){return this.put(new SVG.A).to(t)}}),SVG.extend(SVG.Element,{linkTo:function(t){var e=new SVG.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent.put(e).put(this)}}),SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var i=function(t,e){return"color"==e?t:t+"-"+e};["fill","stroke"].forEach(function(t){var e={};e[t]=function(e){if("string"==typeof e||SVG.Color.isRgb(e)||e&&"function"==typeof e.fill)this.attr(t,e);else for(index=SVG["_"+t].length-1;index>=0;index--)null!=e[SVG["_"+t][index]]&&this.attr(i(t,SVG["_"+t][index]),e[SVG["_"+t][index]]);return this},SVG.extend(SVG.Element,SVG.FX,e)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.extend(SVG.Rect,SVG.Ellipse,{radius:function(t,e){return this.attr({rx:t,ry:e||t})}}),SVG.Text&&SVG.extend(SVG.Text,SVG.FX,{font:function(t){for(var i in t)"anchor"==i?this.attr("text-anchor",t[i]):e.indexOf(i)>-1?this.attr("font-"+i,t[i]):this.attr(i,t[i]);return this}}),SVG.Set=function(){this.clear()},SVG.SetFX=function(t){this.set=t},SVG.extend(SVG.Set,{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;e>t;t++)this.members.push(i[t]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;i>e;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},valueOf:function(){return this.members},bbox:function(){var t=new SVG.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}}),SVG.Set.inherit=function(){var t,e=[];for(var t in SVG.Shape.prototype)"function"==typeof SVG.Shape.prototype[t]&&"function"!=typeof SVG.Set.prototype[t]&&e.push(t);e.forEach(function(t){SVG.Set.prototype[t]=function(){for(var e=0,i=this.members.length;i>e;e++)this.members[e]&&"function"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return"animate"==t?this.fx||(this.fx=new SVG.SetFX(this)):this}}),e=[];for(var t in SVG.FX.prototype)"function"==typeof SVG.FX.prototype[t]&&"function"!=typeof SVG.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){SVG.SetFX.prototype[t]=function(){for(var e=0,i=this.set.members.length;i>e;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},SVG.extend(SVG.Container,{set:function(){return new SVG.Set}}),SVG.extend(SVG.Element,{data:function(t,e,i){if("object"==typeof t)for(e in t)this.data(e,t[e]);else if(2>arguments.length)try{return JSON.parse(this.attr("data-"+t))}catch(n){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:i===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this}}),SVG.extend(SVG.Element,{remember:function(t,e){if("object"==typeof arguments[0])for(var e in t)this.remember(e,t[e]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),"function"==typeof define&&define.amd?define(function(){return SVG}):"undefined"!=typeof exports&&(exports.SVG=SVG)}).call(this); \ No newline at end of file +(function(){function t(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:t instanceof SVG.Color||t instanceof SVG.Number?t.at(e):1>e?t.from:t.to}if(this.SVG=function(t){return SVG.supported?(t=new SVG.Doc(t),SVG.parser||SVG.prepare(t),t):void 0},SVG.ns="http://www.w3.org/2000/svg",SVG.xmlns="http://www.w3.org/2000/xmlns/",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];SVG.Set&&SVG.Set.inherit&&SVG.Set.inherit()},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.prepare=function(t){var e=document.getElementsByTagName("body")[0],i=(e?new SVG.Doc(e):t.nested()).size(2,2);SVG.parser={body:e||t.parent,draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polygon().node,path:i.path().node}},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,SVG.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&SVG.extend(e,t.extend),t.construct&&SVG.extend(t.parent||SVG.Container,t.construct),e},SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/},SVG.defaults={matrix:"1 0 0 1 0 0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000"},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},morph:function(t){return this.destination=new SVG.Color(t),this},at:function(t){return this.destination?(t=0>t?0:t>1?1:t,new SVG.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},SVG.Color.isColor=function(t){return SVG.Color.isRgb(t)||SVG.Color.test(t)},SVG.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},SVG.extend(SVG.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.lengtht;t++)-1==i.indexOf(this.value[t])&&i.push(this.value[t]);return this.value=i},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push(this.value[e]+(this.destination[e]-this.value[e])*t);return new SVG.Array(n)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)},split:function(t){return t.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")}}),SVG.PointArray=function(){this.constructor.apply(this,arguments)},SVG.PointArray.prototype=new SVG.Array,SVG.extend(SVG.PointArray,{toString:function(){for(var t=0,e=this.value.length,i=[];e>t;t++)i.push(this.value[t].join(","));return i.join(" ")},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push([this.value[e][0]+(this.destination[e][0]-this.value[e][0])*t,this.value[e][1]+(this.destination[e][1]-this.value[e][1])*t]);return new SVG.PointArray(n)},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;t=this.split(t);for(var e,i=0,n=t.length,r=[];n>i;i++)e=t[i].split(","),r.push([parseFloat(e[0]),parseFloat(e[1])]);return r},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.x;return this},bbox:function(){return this._cachedBBox?this._cachedBBox:(SVG.parser.poly.setAttribute("points",this.toString()),SVG.parser.poly.getBBox())}}),SVG.PathArray=function(t,e){this.constructor.call(this,t,e)},SVG.PathArray.prototype=new SVG.Array,SVG.extend(SVG.PathArray,{toString:function(){for(var t,e=0,i=this.value.length,n=[];i>e;e++){switch(t=[this.value[e].type],this.value[e].type){case"H":t.push(this.value[e].x);break;case"V":t.push(this.value[e].y);break;case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(this.value[e].type)&&t.push(this.value[e].x1,this.value[e].y1),/[CS]/.test(this.value[e].type)&&t.push(this.value[e].x2,this.value[e].y2),t.push(this.value[e].x,this.value[e].y);break;case"A":t.push(this.value[e].rx,this.value[e].ry,this.value[e].angle,this.value[e].largeArcFlag,this.value[e].sweepFlag,this.value[e].x,this.value[e].y)}n.push(t.join(" "))}return n.join(" ")},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x+=t;break;case"V":this.value[n].y+=e;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x+=t,this.value[n].y+=e,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1+=t,this.value[n].y1+=e),/[CS]/.test(this.value[n].type)&&(this.value[n].x2+=t,this.value[n].y2+=e);break;case"A":this.value[n].x+=t,this.value[n].y+=e}return this},size:function(t,e){for(var i=this.bbox(),n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x;break;case"V":this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x,this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1=(this.value[n].x1-i.x)*t/i.width+i.x,this.value[n].y1=(this.value[n].y1-i.y)*e/i.height+i.y),/[CS]/.test(this.value[n].type)&&(this.value[n].x2=(this.value[n].x2-i.x)*t/i.width+i.x,this.value[n].y2=(this.value[n].y2-i.y)*e/i.height+i.y);break;case"A":this.value[n].values.rx=this.value[n].values.rx*t/i.width,this.value[n].values.ry=this.value[n].values.ry*e/i.height,this.value[n].values.x=(this.value[n].values.x-i.x)*t/i.width+i.x,this.value[n].values.y=(this.value[n].values.y-i.y)*e/i.height+i.y}return this},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;var e,i,n,r,s,h,o,a,u,l,c,f=0,p=0;for(SVG.parser.path.setAttribute("d",t),c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){if(l=c.getItem(e),u=l.pathSegTypeAsLetter,/[MLHVCSQTA]/.test(u))"x"in l&&(f=l.x),"y"in l&&(p=l.y);else switch("x1"in l&&(s=f+l.x1),"x2"in l&&(o=f+l.x2),"y1"in l&&(h=p+l.y1),"y2"in l&&(a=p+l.y2),"x"in l&&(f+=l.x),"y"in l&&(p+=l.y),u){case"m":c.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(f,p),e);break;case"l":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(f,p),e);break;case"h":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(f),e);break;case"v":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(p),e);break;case"c":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(f,p,s,h,o,a),e);break;case"s":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(f,p,o,a),e);break;case"q":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(f,p,s,h),e);break;case"t":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(f,p),e);break;case"a":c.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(f,p,l.r1,l.r2,l.angle,l.largeArcFlag,l.sweepFlag),e);break;case"z":case"Z":f=n,p=r}/[Mm]/.test(u)&&(n=f,r=p)}for(t=[],c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){switch(l=c.getItem(e),u={},l.pathSegTypeAsLetter){case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(l.pathSegTypeAsLetter)&&(u.x1=l.x1,u.y1=l.y1),/[SC]/.test(l.pathSegTypeAsLetter)&&(u.x2=l.x2,u.y2=l.y2);break;case"A":u={r1:l.r1,r2:l.r2,a:l.angle,l:l.largeArcFlag,s:l.sweepFlag}}u.type=l.pathSegTypeAsLetter,u.x=l.x,u.y=l.y,t.push(u)}return t},bbox:function(){return this._cachedBBox?this._cachedBBox:(SVG.parser.path.setAttribute("d",this.toString()),SVG.parser.path.getBBox())}}),SVG.Number=function(t){switch(this.value=0,this.unit="",typeof t){case"number":this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;break;case"string":var e=t.match(SVG.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]&&(this.value/=100),this.unit=e[2]);break;default:t instanceof SVG.Number&&(this.value=t.value,this.unit=t.unit)}},SVG.extend(SVG.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:this.value)+this.unit},valueOf:function(){return this.value},plus:function(t){return this.value=this+new SVG.Number(t),this},minus:function(t){return this.plus(-new SVG.Number(t))},times:function(t){return this.value=this*new SVG.Number(t),this},divide:function(t){return this.value=this/new SVG.Number(t),this},to:function(t){return"string"==typeof t&&(this.unit=t),this},morph:function(t){return this.destination=new SVG.Number(t),this},at:function(t){return this.destination?new SVG.Number(this.destination).minus(this).times(t).plus(this):this}}),SVG.ViewBox=function(t){var e,i,n,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);for(n=new SVG.Number(t.width()),r=new SVG.Number(t.height());"%"==n.unit;)s*=n.value,n=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetWidth:t.width());for(;"%"==r.unit;)h*=r.value,r=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetHeight:t.height());this.x=o.x,this.y=o.y,this.width=n*s,this.height=r*h,this.zoom=1,a&&(e=parseFloat(a[0]),i=parseFloat(a[1]),n=parseFloat(a[2]),r=parseFloat(a[3]),this.zoom=this.width/this.height>n/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.RBox,{merge:function(t){var e=new SVG.RBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.Element=SVG.invent({create:function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},extend:{x:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({width:new SVG.Number(i.width),height:new SVG.Number(i.height)})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return this._isStyle(t)?"text"==t?this.content:"leading"==t&&this.leading?this.leading():this.style(t):(e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e);if("style"==t)return this.style(e);if("x"==t&&Array.isArray(this.lines))for(i=this.lines.length-1;i>=0;i--)this.lines[i].attr(t,e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),SVG.Color.test(e)||SVG.Color.isRgb(e)?e=new SVG.Color(e):"number"==typeof e?e=new SVG.Number(e):Array.isArray(e)&&(e=new SVG.Array(e)),null!=i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),this._isStyle(t)&&("text"==t?this.text(e):"leading"==t&&this.leading?this.leading(e):this.style(t,e),this.rebuild&&this.rebuild(t,e))}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(2>arguments.length)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+" "+this.trans.b+" "+this.trans.c+" "+this.trans.d+" "+this.trans.e+" "+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+" "+(null==t.cx?this.bbox().cx:t.cx)+" "+(null==t.cy?this.bbox().cy:t.cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+" "+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+new SVG.Number(t.x/t.scaleX)+" "+new SVG.Number(t.y/t.scaleY)+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(2>arguments.length)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;t.length>i;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&i.x+i.width>t&&i.y+i.height>e},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},toString:function(){return this.attr("id")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_isStyle:function(t){return"string"==typeof t?SVG.regex.test(t,"isStyle"):!1},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t},_proportionalSize:function(t,e){if(null==t||null==e){var i=this.bbox();null==e?e=i.height/i.width*t:null==t&&(t=i.width/i.height*e)}return{width:t,height:e}}}}),SVG.Parent=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Element,extend:{children:function(){return this._children||(this._children=[])},add:function(t,e){return this.has(t)||(e=null==e?this.children().length:e,t.parent&&t.parent.children().splice(t.parent.index(t),1),this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this),this._defs&&(this.node.removeChild(this._defs.node),this.node.appendChild(this._defs.node)),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return this.children().indexOf(t)},get:function(t){return this.children()[t]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;n>i;i++)r[i]instanceof SVG.Element&&t.apply(r[i],[i,r]),e&&r[i]instanceof SVG.Container&&r[i].each(t,e);return this},removeElement:function(t){return this.children().splice(this.index(t),1),this.node.removeChild(t.node),t.parent=null,this},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&this._defs.clear(),this},defs:function(){return this.doc().defs()}}}),SVG.Container=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Parent,extend:{viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),SVG.FX=function(t){this.target=t},SVG.extend(SVG.FX,{animate:function(e,i,n){var r,s,h,o,a=this.target,u=this;return"object"==typeof e&&(n=e.delay,i=e.ease,e=e.duration),e=null==e?1e3:e,i=i||"<>",u.to=function(e){var n;if(e=0>e?0:e>1?1:e,null==r){r=[];for(o in u.attrs)r.push(o);if(a.morphArray&&(u._plot||r.indexOf("points")>-1)){var l,c=new a.morphArray(u._plot||u.attrs.points||a.array);u._size&&c.size(u._size.width.to,u._size.height.to),l=c.bbox(),u._x?c.move(u._x.to,l.y):u._cx&&c.move(u._cx.to-l.width/2,l.y),l=c.bbox(),u._y?c.move(l.x,u._y.to):u._cy&&c.move(l.x,u._cy.to-l.height/2),delete u._x,delete u._y,delete u._cx,delete u._cy,delete u._size,u._plot=a.array.morph(c)}}if(null==s){s=[];for(o in u.trans)s.push(o)}if(null==h){h=[];for(o in u.styles)h.push(o)}for(e="<>"==i?-Math.cos(e*Math.PI)/2+.5:">"==i?Math.sin(e*Math.PI/2):"<"==i?-Math.cos(e*Math.PI/2)+1:"-"==i?e:"function"==typeof i?i(e):e,u._plot?a.plot(u._plot.at(e)):(u._x?a.x(t(u._x,e)):u._cx&&a.cx(t(u._cx,e)),u._y?a.y(t(u._y,e)):u._cy&&a.cy(t(u._cy,e)),u._size&&a.size(t(u._size.width,e),t(u._size.height,e))),u._viewbox&&a.viewbox(t(u._viewbox.x,e),t(u._viewbox.y,e),t(u._viewbox.width,e),t(u._viewbox.height,e)),n=r.length-1;n>=0;n--)a.attr(r[n],t(u.attrs[r[n]],e));for(n=s.length-1;n>=0;n--)a.transform(s[n],t(u.trans[s[n]],e));for(n=h.length-1;n>=0;n--)a.style(h[n],t(u.styles[h[n]],e));u._during&&u._during.call(a,e,function(i,n){return t({from:i,to:n},e)})},"number"==typeof e&&(this.timeout=setTimeout(function(){var t=(new Date).getTime();u.situation={interval:1e3/60,start:t,play:!0,finish:t+e,duration:e},u.render=function(){if(u.situation.play===!0){var t=(new Date).getTime(),r=t>u.situation.finish?1:(t-u.situation.start)/e;u.to(r),t>u.situation.finish?(u._plot&&a.plot(new SVG.PointArray(u._plot.destination).settle()),u._loop===!0||"number"==typeof u._loop&&u._loop>1?("number"==typeof u._loop&&--u._loop,u.animate(e,i,n)):u._after?u._after.apply(a,[u]):u.stop()):requestAnimFrame(u.render)}else requestAnimFrame(u.render)},u.render()},n||0)),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else{var n=this.target.attr(t);this.attrs[t]=SVG.Color.isColor(n)?new SVG.Color(n).morph(e):SVG.regex.unit.test(n)?new SVG.Number(n).morph(e):{from:n,to:e}}return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},plot:function(t){return this._plot=t,this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var r=this.target.viewbox();this._viewbox={x:{from:r.x,to:t},y:{from:r.y,to:e},width:{from:r.width,to:i},height:{from:r.height,to:n}}}return this},update:function(t){return this.target instanceof SVG.Stop&&(null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},loop:function(t){return this._loop=t||!0,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),this.attrs={},this.trans={},this.styles={},this.situation={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._plot,delete this._loop,delete this._after,delete this._during,delete this._viewbox,this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var t=(new Date).getTime()-this.situation.pause;this.situation.finish+=t,this.situation.start+=t,this.situation.play=!0}return this}}),SVG.extend(SVG.Element,{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),SVG.extend(SVG.Element,SVG.FX,{relative:function(){var t,e=this;return{x:function(i){return t=e.bbox(),e.x(t.x+(i||0))},y:function(i){return t=e.bbox(),e.y(t.y+(i||0))},move:function(t,e){return this.x(t),this.y(e)}}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.Defs=SVG.invent({create:"defs",inherit:SVG.Container}),SVG.G=SVG.invent({create:"g",inherit:SVG.Container,extend:{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)}},construct:{group:function(){return this.put(new SVG.G)}}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){return this.parent.index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position();return this.parent.removeElement(this).put(this,t+1)},backward:function(){var t=this.position();return t>0&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.position()>0&&this.parent.removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent.add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent.add(t,e+1),this}}),SVG.Mask=SVG.invent({create:function(){this.constructor.call(this,SVG.create("mask")),this.targets=[]},inherit:SVG.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return delete this.targets,this.parent.removeElement(this),this}},construct:{mask:function(){return this.defs().put(new SVG.Mask)}}}),SVG.extend(SVG.Element,{maskWith:function(t){return this.masker=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),SVG.Clip=SVG.invent({create:function(){this.constructor.call(this,SVG.create("clipPath")),this.targets=[]},inherit:SVG.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return delete this.targets,this.parent.removeElement(this),this}},construct:{clip:function(){return this.defs().put(new SVG.Clip)}}}),SVG.extend(SVG.Element,{clipWith:function(t){return this.clipper=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),SVG.Gradient=SVG.invent({create:function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},inherit:SVG.Container,extend:{from:function(t,e){return"radial"==this.type?this.attr({fx:new SVG.Number(t),fy:new SVG.Number(e)}):this.attr({x1:new SVG.Number(t),y1:new SVG.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new SVG.Number(t),cy:new SVG.Number(e)}):this.attr({x2:new SVG.Number(t),y2:new SVG.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new SVG.Number(t)}):this},at:function(t){return this.put((new SVG.Stop).update(t))},update:function(t){return this.clear(),t(this),this},fill:function(){return"url(#"+this.attr("id")+")"},toString:function(){return this.fill()}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),SVG.extend(SVG.Defs,{gradient:function(t,e){var i=this.put(new SVG.Gradient(t));return e(i),i}}),SVG.Stop=SVG.invent({create:"stop",inherit:SVG.Element,extend:{update:function(t){return null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset)),this}}}),SVG.Doc=SVG.invent({create:function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xmlns:xlink",SVG.xlink,SVG.xmlns),this._defs=new SVG.Defs,this._defs.parent=this,this.node.appendChild(this._defs.node),this.doSubPixelOffsetFix=!1,"svg"!=this.parent.nodeName&&this.stage()},inherit:SVG.Container,extend:{stage:function(){var t,e=this,i=document.createElement("div");return i.style.cssText="position:relative;height:100%;",e.parent.appendChild(i),i.appendChild(e.node),t=function(){"complete"===document.readyState?(e.style("position:absolute;"),setTimeout(function(){e.style("position:relative;overflow:hidden;"),e.parent.removeChild(e.node.parentNode),e.node.parentNode.removeChild(e.node),e.parent.appendChild(e.node),e.subPixelOffsetFix(),SVG.on(window,"resize",function(){e.subPixelOffsetFix()})},5)):setTimeout(t,10)},t(),this},defs:function(){return this._defs},subPixelOffsetFix:function(){if(this.doSubPixelOffsetFix){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){return this.doSubPixelOffsetFix=!0,this}}}),SVG.Shape=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Element}),SVG.Use=SVG.invent({create:"use",inherit:SVG.Shape,extend:{element:function(t){return this.target=t,this.attr("href","#"+t,SVG.xlink)}},construct:{use:function(t){return this.put(new SVG.Use).element(t)}}}),SVG.Rect=SVG.invent({create:"rect",inherit:SVG.Shape,construct:{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}}),SVG.Ellipse=SVG.invent({create:"ellipse",inherit:SVG.Shape,extend:{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new SVG.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new SVG.Number(t).divide(this.trans.scaleY))},width:function(t){return null==t?2*this.attr("rx"):this.attr("rx",new SVG.Number(t).divide(2))},height:function(t){return null==t?2*this.attr("ry"):this.attr("ry",new SVG.Number(t).divide(2))},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({rx:new SVG.Number(i.width).divide(2),ry:new SVG.Number(i.height).divide(2)})}},construct:{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put(new SVG.Ellipse).size(t,e).move(0,0)}}}),SVG.Line=SVG.invent({create:"line",inherit:SVG.Shape,extend:{x:function(t){var e=this.bbox();return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},width:function(t){var e=this.bbox();return null==t?e.width:this.attr(this.attr("x1")e;e++)this.tspan(n[e]).newLine();this.rebuild()}return this},tspan:function(t){var e=this.textPath?this.textPath.node:this.node,i=(new SVG.TSpan).text(t),n=this.style();return e.appendChild(i.node),this.lines.add(i),SVG.regex.isBlank.test(n)||i.style(n),this.content+=t,i.parent=this,i},size:function(t){return this.attr("font-size",t)},leading:function(t){return null==t?this._leading:(t=new SVG.Number(t),this._leading=t,this.lines.each(function(){this.newLined&&this.attr("dy",t)}),this)},rebuild:function(){return this._rebuild&&this.lines.attr({x:this.attr("x"),dy:this._leading,style:this.style()}),this},clear:function(){for(var t=this.textPath?this.textPath.node:this.node;t.hasChildNodes();)t.removeChild(t.lastChild);return delete this.lines,this.lines=new SVG.Set,this.content="",this}},construct:{text:function(t){return this.put(new SVG.Text).text(t)}}}),SVG.TSpan=SVG.invent({create:"tspan",inherit:SVG.Shape,extend:{text:function(t){return this.node.appendChild(document.createTextNode(t)),this},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){return this.newLined=!0,this.parent.content+="\n",this.dy(this.parent._leading),this.attr("x",this.parent.x())}}}),SVG.TextPath=SVG.invent({create:"textPath",inherit:SVG.Element,parent:SVG.Text,construct:{path:function(t){for(this.textPath=new SVG.TextPath;this.node.hasChildNodes();)this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t,!0),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,SVG.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}}),SVG.Nested=SVG.invent({create:function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},inherit:SVG.Container,construct:{nested:function(){return this.put(new SVG.Nested)}}}),SVG.A=SVG.invent({create:"a",inherit:SVG.Container,extend:{to:function(t){return this.attr("href",t,SVG.xlink)},show:function(t){return this.attr("show",t,SVG.xlink)},target:function(t){return this.attr("target",t)}},construct:{link:function(t){return this.put(new SVG.A).to(t)}}}),SVG.extend(SVG.Element,{linkTo:function(t){var e=new SVG.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent.put(e).put(this)}}),SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var i=function(t,e){return"color"==e?t:t+"-"+e};["fill","stroke"].forEach(function(t){var e={};e[t]=function(e){if("string"==typeof e||SVG.Color.isRgb(e)||e&&"function"==typeof e.fill)this.attr(t,e);else for(index=SVG["_"+t].length-1;index>=0;index--)null!=e[SVG["_"+t][index]]&&this.attr(i(t,SVG["_"+t][index]),e[SVG["_"+t][index]]);return this},SVG.extend(SVG.Element,SVG.FX,e)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.extend(SVG.Rect,SVG.Ellipse,{radius:function(t,e){return this.attr({rx:t,ry:e||t})}}),SVG.Text&&SVG.extend(SVG.Text,SVG.FX,{font:function(t){for(var i in t)"anchor"==i?this.attr("text-anchor",t[i]):e.indexOf(i)>-1?this.attr("font-"+i,t[i]):this.attr(i,t[i]);return this}}),SVG.Set=SVG.invent({create:function(){this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;e>t;t++)this.members.push(i[t]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;i>e;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},valueOf:function(){return this.members},bbox:function(){var t=new SVG.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(){return new SVG.Set}}}),SVG.SetFX=SVG.invent({create:function(t){this.set=t}}),SVG.Set.inherit=function(){var t,e=[];for(var t in SVG.Shape.prototype)"function"==typeof SVG.Shape.prototype[t]&&"function"!=typeof SVG.Set.prototype[t]&&e.push(t);e.forEach(function(t){SVG.Set.prototype[t]=function(){for(var e=0,i=this.members.length;i>e;e++)this.members[e]&&"function"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return"animate"==t?this.fx||(this.fx=new SVG.SetFX(this)):this}}),e=[];for(var t in SVG.FX.prototype)"function"==typeof SVG.FX.prototype[t]&&"function"!=typeof SVG.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){SVG.SetFX.prototype[t]=function(){for(var e=0,i=this.set.members.length;i>e;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},SVG.extend(SVG.Element,{data:function(t,e,i){if("object"==typeof t)for(e in t)this.data(e,t[e]);else if(2>arguments.length)try{return JSON.parse(this.attr("data-"+t))}catch(n){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:i===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this}}),SVG.extend(SVG.Element,{remember:function(t,e){if("object"==typeof arguments[0])for(var e in t)this.remember(e,t[e]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),"function"==typeof define&&define.amd?define(function(){return SVG}):"undefined"!=typeof exports&&(exports.SVG=SVG)}).call(this); \ No newline at end of file diff --git a/package.json b/package.json index 5335324..6d5ad4d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ , "keywords": ["svg", "vector", "graphics", "animation"] , "author": "Wout Fierens " , "main": "dist/svg.js" -, "version": "1.0.0-rc2" +, "version": "1.0.0-rc3" , "jam": { "include": [ "dist/svg.js" diff --git a/src/clip.js b/src/clip.js index 54f9d37..e8a5e35 100755 --- a/src/clip.js +++ b/src/clip.js @@ -1,27 +1,38 @@ -SVG.Clip = function() { - this.constructor.call(this, SVG.create('clipPath')) +SVG.Clip = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('clipPath')) - /* keep references to clipped elements */ - this.targets = [] -} - -// Inherit from SVG.Container -SVG.Clip.prototype = new SVG.Container - -// -SVG.extend(SVG.Clip, { - // Unclip all clipped elements and remove itself - remove: function() { - /* unclip all targets */ - for (var i = this.targets.length - 1; i >= 0; i--) - if (this.targets[i]) - this.targets[i].unclip() - delete this.targets + /* keep references to clipped elements */ + this.targets = [] + } - /* remove clipPath from parent */ - this.parent.removeElement(this) - - return this + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Unclip all clipped elements and remove itself + remove: function() { + /* unclip all targets */ + for (var i = this.targets.length - 1; i >= 0; i--) + if (this.targets[i]) + this.targets[i].unclip() + delete this.targets + + /* remove clipPath from parent */ + this.parent.removeElement(this) + + return this + } + } + + // Add parent method +, construct: { + // Create clipping element + clip: function() { + return this.defs().put(new SVG.Clip) + } } }) @@ -44,13 +55,4 @@ SVG.extend(SVG.Element, { return this.attr('clip-path', null) } -}) - -// -SVG.extend(SVG.Container, { - // Create clipping element - clip: function() { - return this.defs().put(new SVG.Clip) - } - }) \ No newline at end of file diff --git a/src/container.js b/src/container.js index 60f62c4..e63f2ae 100755 --- a/src/container.js +++ b/src/container.js @@ -1,24 +1,27 @@ -SVG.Container = function(element) { - this.constructor.call(this, element) -} +SVG.Container = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) + } -// Inherit from SVG.Parent -SVG.Container.prototype = new SVG.Parent + // Inherit from +, inherit: SVG.Parent -// -SVG.extend(SVG.Container, { - // Get the viewBox and calculate the zoom value - viewbox: function(v) { - if (arguments.length == 0) - /* act as a getter if there are no arguments */ - return new SVG.ViewBox(this) - - /* otherwise act as a setter */ - v = arguments.length == 1 ? - [v.x, v.y, v.width, v.height] : - [].slice.call(arguments) - - return this.attr('viewBox', v) + // Add class methods +, extend: { + // Get the viewBox and calculate the zoom value + viewbox: function(v) { + if (arguments.length == 0) + /* act as a getter if there are no arguments */ + return new SVG.ViewBox(this) + + /* otherwise act as a setter */ + v = arguments.length == 1 ? + [v.x, v.y, v.width, v.height] : + [].slice.call(arguments) + + return this.attr('viewBox', v) + } } }) \ No newline at end of file diff --git a/src/defs.js b/src/defs.js index 48a8f28..a50ad94 100755 --- a/src/defs.js +++ b/src/defs.js @@ -1,9 +1,8 @@ -// ### The defs node -// -SVG.Defs = function() { - this.constructor.call(this, SVG.create('defs')) -} +SVG.Defs = SVG.invent({ + // Initialize node + create: 'defs' -// Inherits from SVG.Container -SVG.Defs.prototype = new SVG.Container \ No newline at end of file + // Inherit from +, inherit: SVG.Container +}) \ No newline at end of file diff --git a/src/doc.js b/src/doc.js index 7e9eef3..5129dd9 100755 --- a/src/doc.js +++ b/src/doc.js @@ -1,112 +1,112 @@ -// ### This module accounts for the main svg document - -// -SVG.Doc = function(element) { - /* ensure the presence of a html element */ - this.parent = typeof element == 'string' ? - document.getElementById(element) : - element - - /* If the target is an svg element, use that element as the main wrapper. - This allows svg.js to work with svg documents as well. */ - this.constructor - .call(this, this.parent.nodeName == 'svg' ? this.parent : SVG.create('svg')) - - /* set svg element attributes */ - this - .attr({ xmlns: SVG.ns, version: '1.1', width: '100%', height: '100%' }) - .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) - - /* create the node */ - this._defs = new SVG.Defs - this._defs.parent = this - this.node.appendChild(this._defs.node) +SVG.Doc = SVG.invent({ + // Initialize node + create: function(element) { + /* ensure the presence of a html element */ + this.parent = typeof element == 'string' ? + document.getElementById(element) : + element + + /* If the target is an svg element, use that element as the main wrapper. + This allows svg.js to work with svg documents as well. */ + this.constructor + .call(this, this.parent.nodeName == 'svg' ? this.parent : SVG.create('svg')) + + /* set svg element attributes */ + this + .attr({ xmlns: SVG.ns, version: '1.1', width: '100%', height: '100%' }) + .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) + + /* create the node */ + this._defs = new SVG.Defs + this._defs.parent = this + this.node.appendChild(this._defs.node) - /* turno of sub pixel offset by default */ - this.doSubPixelOffsetFix = false - - /* ensure correct rendering */ - if (this.parent.nodeName != 'svg') - this.stage() -} + /* turno of sub pixel offset by default */ + this.doSubPixelOffsetFix = false + + /* ensure correct rendering */ + if (this.parent.nodeName != 'svg') + this.stage() + } -// Inherits from SVG.Container -SVG.Doc.prototype = new SVG.Container + // Inherit from +, inherit: SVG.Container -// -SVG.extend(SVG.Doc, { - // Hack for safari preventing text to be rendered in one line. - // 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. - // It also handles sub-pixel offset rendering properly. - stage: function() { - var check - , element = this - , wrapper = document.createElement('div') + // Add class methods +, extend: { + // Hack for safari preventing text to be rendered in one line. + // 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. + // It also handles sub-pixel offset rendering properly. + stage: function() { + var check + , element = this + , wrapper = document.createElement('div') - /* set temporary wrapper to position relative */ - wrapper.style.cssText = 'position:relative;height:100%;' + /* set temporary wrapper to position relative */ + wrapper.style.cssText = 'position:relative;height:100%;' - /* put element into wrapper */ - element.parent.appendChild(wrapper) - wrapper.appendChild(element.node) + /* put element into wrapper */ + element.parent.appendChild(wrapper) + wrapper.appendChild(element.node) - /* check for dom:ready */ - check = function() { - if (document.readyState === 'complete') { - element.style('position:absolute;') - setTimeout(function() { - /* set position back to relative */ - element.style('position:relative;overflow:hidden;') + /* check for dom:ready */ + check = function() { + if (document.readyState === 'complete') { + element.style('position:absolute;') + setTimeout(function() { + /* set position back to relative */ + element.style('position:relative;overflow:hidden;') - /* remove temporary wrapper */ - element.parent.removeChild(element.node.parentNode) - element.node.parentNode.removeChild(element.node) - element.parent.appendChild(element.node) + /* remove temporary wrapper */ + element.parent.removeChild(element.node.parentNode) + element.node.parentNode.removeChild(element.node) + element.parent.appendChild(element.node) - /* after wrapping is done, fix sub-pixel offset */ - element.subPixelOffsetFix() - - /* make sure sub-pixel offset is fixed every time the window is resized */ - SVG.on(window, 'resize', function() { + /* after wrapping is done, fix sub-pixel offset */ element.subPixelOffsetFix() - }) - - }, 5) - } else { - setTimeout(check, 10) + + /* make sure sub-pixel offset is fixed every time the window is resized */ + SVG.on(window, 'resize', function() { + element.subPixelOffsetFix() + }) + + }, 5) + } else { + setTimeout(check, 10) + } } - } - check() + check() - return this - } + return this + } - // Creates and returns defs element -, defs: function() { - return this._defs - } + // Creates and returns defs element + , defs: function() { + return this._defs + } - // Fix for possible sub-pixel offset. See: - // https://bugzilla.mozilla.org/show_bug.cgi?id=608812 -, subPixelOffsetFix: function() { - if (this.doSubPixelOffsetFix) { - var pos = this.node.getScreenCTM() + // Fix for possible sub-pixel offset. See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=608812 + , subPixelOffsetFix: function() { + if (this.doSubPixelOffsetFix) { + var pos = this.node.getScreenCTM() + + if (pos) + this + .style('left', (-pos.e % 1) + 'px') + .style('top', (-pos.f % 1) + 'px') + } - if (pos) - this - .style('left', (-pos.e % 1) + 'px') - .style('top', (-pos.f % 1) + 'px') + return this } - - return this - } -, fixSubPixelOffset: function() { - this.doSubPixelOffsetFix = true + , fixSubPixelOffset: function() { + this.doSubPixelOffsetFix = true - return this + return this + } } -}) \ No newline at end of file +}) diff --git a/src/element.js b/src/element.js index b75ed32..01146ab 100755 --- a/src/element.js +++ b/src/element.js @@ -1,420 +1,420 @@ -// ### Used by nearly every other module - -// -SVG.Element = function(node) { - /* make stroke value accessible dynamically */ - this._stroke = SVG.defaults.attrs.stroke - - /* initialize style store */ - this.styles = {} - - /* initialize transformation store with defaults */ - this.trans = SVG.defaults.trans() - - /* keep reference to the element node */ - if (this.node = node) { - this.type = node.nodeName - this.node.instance = this - } -} - -// -SVG.extend(SVG.Element, { - // Move over x-axis - x: function(x) { - if (x) { - x = new SVG.Number(x) - x.value /= this.trans.scaleX - } - return this.attr('x', x) - } - // Move over y-axis -, y: function(y) { - if (y) { - y = new SVG.Number(y) - y.value /= this.trans.scaleY - } - return this.attr('y', y) - } - // Move by center over x-axis -, cx: function(x) { - return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2) - } - // Move by center over y-axis -, cy: function(y) { - return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2) - } - // Move element to given x and y values -, move: function(x, y) { - return this.x(x).y(y) - } - // Move element by its center -, center: function(x, y) { - return this.cx(x).cy(y) - } - // Set width of element -, width: function(width) { - return this.attr('width', width) - } - // Set height of element -, height: function(height) { - return this.attr('height', height) - } - // Set element size to given width and height -, size: function(width, height) { - var p = this._proportionalSize(width, height) - - return this.attr({ - width: new SVG.Number(p.width) - , height: new SVG.Number(p.height) - }) - } - // Clone element -, clone: function() { - var clone , attr - , type = this.type +SVG.Element = SVG.invent({ + // Initialize node + create: function(node) { + /* make stroke value accessible dynamically */ + this._stroke = SVG.defaults.attrs.stroke - /* invoke shape method with shape-specific arguments */ - clone = type == 'rect' || type == 'ellipse' ? - this.parent[type](0,0) : - type == 'line' ? - this.parent[type](0,0,0,0) : - type == 'image' ? - this.parent[type](this.src) : - type == 'text' ? - this.parent[type](this.content) : - type == 'path' ? - this.parent[type](this.attr('d')) : - type == 'polyline' || type == 'polygon' ? - this.parent[type](this.attr('points')) : - type == 'g' ? - this.parent.group() : - this.parent[type]() + /* initialize style store */ + this.styles = {} - /* apply attributes attributes */ - attr = this.attr() - delete attr.id - clone.attr(attr) + /* initialize transformation store with defaults */ + this.trans = SVG.defaults.trans() - /* copy transformations */ - clone.trans = this.trans - - /* apply attributes and translations */ - return clone.transform({}) - } - // Remove element -, remove: function() { - if (this.parent) - this.parent.removeElement(this) - - return this + /* keep reference to the element node */ + if (this.node = node) { + this.type = node.nodeName + this.node.instance = this + } } - // Replace element -, replace: function(element) { - this.after(element).remove() - return element - } - // Add element to given container and return self -, addTo: function(parent) { - return parent.put(this) - } - // Add element to given container and return container -, putIn: function(parent) { - return parent.add(this) - } - // Get parent document -, doc: function(type) { - return this._parent(type || SVG.Doc) - } - // Set svg element attribute -, attr: function(a, v, n) { - if (a == null) { - /* get an object of attributes */ - a = {} - v = this.node.attributes - for (n = v.length - 1; n >= 0; n--) - a[v[n].nodeName] = SVG.regex.test(v[n].nodeValue, 'isNumber') ? parseFloat(v[n].nodeValue) : v[n].nodeValue + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + if (x) { + x = new SVG.Number(x) + x.value /= this.trans.scaleX + } + return this.attr('x', x) + } + // Move over y-axis + , y: function(y) { + if (y) { + y = new SVG.Number(y) + y.value /= this.trans.scaleY + } + return this.attr('y', y) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2) + } + // Move element to given x and y values + , move: function(x, y) { + return this.x(x).y(y) + } + // Move element by its center + , center: function(x, y) { + return this.cx(x).cy(y) + } + // Set width of element + , width: function(width) { + return this.attr('width', width) + } + // Set height of element + , height: function(height) { + return this.attr('height', height) + } + // Set element size to given width and height + , size: function(width, height) { + var p = this._proportionalSize(width, height) + + return this.attr({ + width: new SVG.Number(p.width) + , height: new SVG.Number(p.height) + }) + } + // Clone element + , clone: function() { + var clone , attr + , type = this.type - return a + /* invoke shape method with shape-specific arguments */ + clone = type == 'rect' || type == 'ellipse' ? + this.parent[type](0,0) : + type == 'line' ? + this.parent[type](0,0,0,0) : + type == 'image' ? + this.parent[type](this.src) : + type == 'text' ? + this.parent[type](this.content) : + type == 'path' ? + this.parent[type](this.attr('d')) : + type == 'polyline' || type == 'polygon' ? + this.parent[type](this.attr('points')) : + type == 'g' ? + this.parent.group() : + this.parent[type]() - } else if (typeof a == 'object') { - /* apply every attribute individually if an object is passed */ - for (v in a) this.attr(v, a[v]) + /* apply attributes attributes */ + attr = this.attr() + delete attr.id + clone.attr(attr) - } else if (v === null) { - /* remove value */ - this.node.removeAttribute(a) + /* copy transformations */ + clone.trans = this.trans - } else if (v == null) { - /* act as a getter for style attributes */ - if (this._isStyle(a)) { - return a == 'text' ? - this.content : - a == 'leading' && this.leading ? - this.leading() : - this.style(a) + /* apply attributes and translations */ + return clone.transform({}) + } + // Remove element + , remove: function() { + if (this.parent) + this.parent.removeElement(this) - /* act as a getter if the first and only argument is not an object */ - } else { - v = this.node.getAttribute(a) - return v == null ? - SVG.defaults.attrs[a] : - SVG.regex.test(v, 'isNumber') ? - parseFloat(v) : v - } - - } else if (a == 'style') { - /* redirect to the style method */ - return this.style(v) - - } else { - /* treat x differently on text elements */ - if (a == 'x' && Array.isArray(this.lines)) - for (n = this.lines.length - 1; n >= 0; n--) - this.lines[n].attr(a, v) + return this + } + // Replace element + , replace: function(element) { + this.after(element).remove() + + return element + } + // Add element to given container and return self + , addTo: function(parent) { + return parent.put(this) + } + // Add element to given container and return container + , putIn: function(parent) { + return parent.add(this) + } + // Get parent document + , doc: function(type) { + return this._parent(type || SVG.Doc) + } + // Set svg element attribute + , attr: function(a, v, n) { + if (a == null) { + /* get an object of attributes */ + a = {} + v = this.node.attributes + for (n = v.length - 1; n >= 0; n--) + a[v[n].nodeName] = SVG.regex.test(v[n].nodeValue, 'isNumber') ? parseFloat(v[n].nodeValue) : v[n].nodeValue + + return a + + } else if (typeof a == 'object') { + /* apply every attribute individually if an object is passed */ + for (v in a) this.attr(v, a[v]) + + } else if (v === null) { + /* remove value */ + this.node.removeAttribute(a) + + } else if (v == null) { + /* act as a getter for style attributes */ + if (this._isStyle(a)) { + return a == 'text' ? + this.content : + a == 'leading' && this.leading ? + this.leading() : + this.style(a) + + /* act as a getter if the first and only argument is not an object */ + } else { + v = this.node.getAttribute(a) + return v == null ? + SVG.defaults.attrs[a] : + SVG.regex.test(v, 'isNumber') ? + parseFloat(v) : v + } - /* BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 */ - if (a == 'stroke-width') - this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null) - else if (a == 'stroke') - this._stroke = v + } else if (a == 'style') { + /* redirect to the style method */ + return this.style(v) - /* ensure full hex color */ - if (SVG.Color.test(v) || SVG.Color.isRgb(v)) - v = new SVG.Color(v) + } else { + /* treat x differently on text elements */ + if (a == 'x' && Array.isArray(this.lines)) + for (n = this.lines.length - 1; n >= 0; n--) + this.lines[n].attr(a, v) + + /* BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 */ + if (a == 'stroke-width') + this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null) + else if (a == 'stroke') + this._stroke = v + + /* ensure full hex color */ + if (SVG.Color.test(v) || SVG.Color.isRgb(v)) + v = new SVG.Color(v) - /* ensure correct numeric values */ - else if (typeof v === 'number') - v = new SVG.Number(v) + /* ensure correct numeric values */ + else if (typeof v === 'number') + v = new SVG.Number(v) - /* parse array values */ - else if (Array.isArray(v)) - v = new SVG.Array(v) + /* parse array values */ + else if (Array.isArray(v)) + v = new SVG.Array(v) - /* set give attribute on node */ - n != null ? - this.node.setAttributeNS(n, a, v.toString()) : - this.node.setAttribute(a, v.toString()) - - /* if the passed argument belongs in the style as well, add it there */ - if (this._isStyle(a)) { - a == 'text' ? - this.text(v) : - a == 'leading' && this.leading ? - this.leading(v) : - this.style(a, v) + /* set give attribute on node */ + n != null ? + this.node.setAttributeNS(n, a, v.toString()) : + this.node.setAttribute(a, v.toString()) - /* rebuild if required */ - if (this.rebuild) - this.rebuild(a, v) + /* if the passed argument belongs in the style as well, add it there */ + if (this._isStyle(a)) { + a == 'text' ? + this.text(v) : + a == 'leading' && this.leading ? + this.leading(v) : + this.style(a, v) + + /* rebuild if required */ + if (this.rebuild) + this.rebuild(a, v) + } } + + return this } - - return this - } - // Manage transformations -, transform: function(o, v) { - - if (arguments.length == 0) { - /* act as a getter if no argument is given */ - return this.trans + // Manage transformations + , transform: function(o, v) { + + if (arguments.length == 0) { + /* act as a getter if no argument is given */ + return this.trans + + } else 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 transform = [] - } else if (typeof o === 'string') { - /* act as a getter if only one string argument is given */ - if (arguments.length < 2) - return this.trans[o] + /* parse matrix */ + o = this._parseMatrix(o) - /* apply transformations as object if key value arguments are given*/ - var transform = {} - transform[o] = v + /* merge values */ + for (v in o) + if (o[v] != null) + this.trans[v] = o[v] - return this.transform(transform) + /* compile matrix */ + this.trans.matrix = this.trans.a + + ' ' + this.trans.b + + ' ' + this.trans.c + + ' ' + this.trans.d + + ' ' + this.trans.e + + ' ' + this.trans.f + + /* alias current transformations */ + o = this.trans + + /* add matrix */ + if (o.matrix != SVG.defaults.matrix) + transform.push('matrix(' + o.matrix + ')') + + /* add rotation */ + if (o.rotation != 0) + transform.push('rotate(' + o.rotation + ' ' + (o.cx == null ? this.bbox().cx : o.cx) + ' ' + (o.cy == null ? this.bbox().cy : o.cy) + ')') + + /* add scale */ + if (o.scaleX != 1 || o.scaleY != 1) + transform.push('scale(' + o.scaleX + ' ' + o.scaleY + ')') + + /* add skew on x axis */ + if (o.skewX != 0) + transform.push('skewX(' + o.skewX + ')') + + /* add skew on y axis */ + if (o.skewY != 0) + transform.push('skewY(' + o.skewY + ')') + + /* add translation */ + if (o.x != 0 || o.y != 0) + transform.push('translate(' + new SVG.Number(o.x / o.scaleX) + ' ' + new SVG.Number(o.y / o.scaleY) + ')') + + /* update transformations, even if there are none */ + if (transform.length == 0) + this.node.removeAttribute('transform') + else + this.node.setAttribute('transform', transform.join(' ')) + + return this } - - /* ... otherwise continue as a setter */ - var transform = [] - - /* parse matrix */ - o = this._parseMatrix(o) - - /* merge values */ - for (v in o) - if (o[v] != null) - this.trans[v] = o[v] - - /* compile matrix */ - this.trans.matrix = this.trans.a - + ' ' + this.trans.b - + ' ' + this.trans.c - + ' ' + this.trans.d - + ' ' + this.trans.e - + ' ' + this.trans.f - - /* alias current transformations */ - o = this.trans - - /* add matrix */ - if (o.matrix != SVG.defaults.matrix) - transform.push('matrix(' + o.matrix + ')') - - /* add rotation */ - if (o.rotation != 0) - transform.push('rotate(' + o.rotation + ' ' + (o.cx == null ? this.bbox().cx : o.cx) + ' ' + (o.cy == null ? this.bbox().cy : o.cy) + ')') - - /* add scale */ - if (o.scaleX != 1 || o.scaleY != 1) - transform.push('scale(' + o.scaleX + ' ' + o.scaleY + ')') - - /* add skew on x axis */ - if (o.skewX != 0) - transform.push('skewX(' + o.skewX + ')') - - /* add skew on y axis */ - if (o.skewY != 0) - transform.push('skewY(' + o.skewY + ')') - - /* add translation */ - if (o.x != 0 || o.y != 0) - transform.push('translate(' + new SVG.Number(o.x / o.scaleX) + ' ' + new SVG.Number(o.y / o.scaleY) + ')') - - /* update transformations, even if there are none */ - if (transform.length == 0) - this.node.removeAttribute('transform') - else - this.node.setAttribute('transform', transform.join(' ')) - - return this - } - // 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]) + // Dynamic style generator + , style: function(s, v) { + if (arguments.length == 0) { + /* get full style */ + return this.attr('style') || '' - } else if (SVG.regex.isCss.test(s)) { - /* parse css string */ - s = s.split(';') + } 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(':') + /* 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+$/,'')) + 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 || SVG.regex.test(v, 'isBlank')) { + /* remove value */ + delete this.styles[s] + } else { - /* act as a getter if the first and only argument is not an object */ - return this.styles[s] + /* store value */ + this.styles[s] = v } - - } else if (v === null || SVG.regex.test(v, 'isBlank')) { - /* 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 */ - if (s == '') - this.node.removeAttribute('style') - else - this.node.setAttribute('style', s) - - return this - } - // Get bounding box -, bbox: function() { - return new SVG.BBox(this) - } - // Get rect box -, rbox: function() { - return new SVG.RBox(this) - } - // Checks whether the given point inside the bounding box of the element -, inside: function(x, y) { - var box = this.bbox() - - return x > box.x - && y > box.y - && x < box.x + box.width - && y < box.y + box.height - } - // Show element -, show: function() { - return this.style('display', '') - } - // Hide element -, hide: function() { - return this.style('display', 'none') - } - // Is element visible? -, visible: function() { - return this.style('display') != 'none' - } - // Return id on string conversion -, toString: function() { - return this.attr('id') - } - // Private: find svg parent by instance -, _parent: function(parent) { - var element = this - - while (element != null && !(element instanceof parent)) - element = element.parent - - return element - } - // Private: tester method for style detection -, _isStyle: function(a) { - return typeof a == 'string' ? SVG.regex.test(a, 'isStyle') : false - } - // Private: parse a matrix string -, _parseMatrix: function(o) { - if (o.matrix) { - /* split matrix string */ - var m = o.matrix.replace(/\s/g, '').split(',') + /* rebuild style string */ + s = '' + for (v in this.styles) + s += v + ':' + this.styles[v] + ';' - /* pasrse values */ - if (m.length == 6) { - o.a = parseFloat(m[0]) - o.b = parseFloat(m[1]) - o.c = parseFloat(m[2]) - o.d = parseFloat(m[3]) - o.e = parseFloat(m[4]) - o.f = parseFloat(m[5]) - } + /* apply style */ + if (s == '') + this.node.removeAttribute('style') + else + this.node.setAttribute('style', s) + + return this } - - return o - } - // Private: calculate proportional width and height values when necessary -, _proportionalSize: function(width, height) { - if (width == null || height == null) { + // Get bounding box + , bbox: function() { + return new SVG.BBox(this) + } + // Get rect box + , rbox: function() { + return new SVG.RBox(this) + } + // Checks whether the given point inside the bounding box of the element + , inside: function(x, y) { var box = this.bbox() + + return x > box.x + && y > box.y + && x < box.x + box.width + && y < box.y + box.height + } + // Show element + , show: function() { + return this.style('display', '') + } + // Hide element + , hide: function() { + return this.style('display', 'none') + } + // Is element visible? + , visible: function() { + return this.style('display') != 'none' + } + // Return id on string conversion + , toString: function() { + return this.attr('id') + } + // Private: find svg parent by instance + , _parent: function(parent) { + var element = this + + while (element != null && !(element instanceof parent)) + element = element.parent - if (height == null) - height = box.height / box.width * width - else if (width == null) - width = box.width / box.height * height + return element } - - return { - width: width - , height: height + // Private: tester method for style detection + , _isStyle: function(a) { + return typeof a == 'string' ? SVG.regex.test(a, 'isStyle') : false + } + // Private: parse a matrix string + , _parseMatrix: function(o) { + if (o.matrix) { + /* split matrix string */ + var m = o.matrix.replace(/\s/g, '').split(',') + + /* pasrse values */ + if (m.length == 6) { + o.a = parseFloat(m[0]) + o.b = parseFloat(m[1]) + o.c = parseFloat(m[2]) + o.d = parseFloat(m[3]) + o.e = parseFloat(m[4]) + o.f = parseFloat(m[5]) + } + } + + return o + } + // Private: calculate proportional width and height values when necessary + , _proportionalSize: function(width, height) { + if (width == null || height == null) { + var box = this.bbox() + + if (height == null) + height = box.height / box.width * width + else if (width == null) + width = box.width / box.height * height + } + + return { + width: width + , height: height + } } } diff --git a/src/ellipse.js b/src/ellipse.js index 237a5ca..d84f988 100755 --- a/src/ellipse.js +++ b/src/ellipse.js @@ -1,62 +1,59 @@ -// -SVG.Ellipse = function() { - this.constructor.call(this, SVG.create('ellipse')) -} +SVG.Ellipse = SVG.invent({ + // Initialize node + create: 'ellipse' -// Inherit from SVG.Shape -SVG.Ellipse.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape -// -SVG.extend(SVG.Ellipse, { - // Move over x-axis - x: function(x) { - return x == null ? this.cx() - this.attr('rx') : this.cx(x + this.attr('rx')) - } - // Move over y-axis -, y: function(y) { - return y == null ? this.cy() - this.attr('ry') : this.cy(y + this.attr('ry')) - } - // Move by center over x-axis -, cx: function(x) { - return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX)) - } - // Move by center over y-axis -, cy: function(y) { - return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY)) - } - // Set width of element -, width: function(width) { - return width == null ? this.attr('rx') * 2 : this.attr('rx', new SVG.Number(width).divide(2)) - } - // Set height of element -, height: function(height) { - return height == null ? this.attr('ry') * 2 : this.attr('ry', new SVG.Number(height).divide(2)) - } - // Custom size function -, size: function(width, height) { - var p = this._proportionalSize(width, height) + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + return x == null ? this.cx() - this.attr('rx') : this.cx(x + this.attr('rx')) + } + // Move over y-axis + , y: function(y) { + return y == null ? this.cy() - this.attr('ry') : this.cy(y + this.attr('ry')) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.trans.scaleX)) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.attr('cy') : this.attr('cy', new SVG.Number(y).divide(this.trans.scaleY)) + } + // Set width of element + , width: function(width) { + return width == null ? this.attr('rx') * 2 : this.attr('rx', new SVG.Number(width).divide(2)) + } + // Set height of element + , height: function(height) { + return height == null ? this.attr('ry') * 2 : this.attr('ry', new SVG.Number(height).divide(2)) + } + // Custom size function + , size: function(width, height) { + var p = this._proportionalSize(width, height) - return this.attr({ - rx: new SVG.Number(p.width).divide(2) - , ry: new SVG.Number(p.height).divide(2) - }) + return this.attr({ + rx: new SVG.Number(p.width).divide(2) + , ry: new SVG.Number(p.height).divide(2) + }) + } + } - -}) -// -SVG.extend(SVG.Container, { - // Create circle element, based on ellipse - circle: function(size) { - return this.ellipse(size, size) + // Add parent method +, construct: { + // Create circle element, based on ellipse + circle: function(size) { + return this.ellipse(size, size) + } + // Create an ellipse + , ellipse: function(width, height) { + return this.put(new SVG.Ellipse).size(width, height).move(0, 0) + } + } - // Create an ellipse -, ellipse: function(width, height) { - return this.put(new SVG.Ellipse).size(width, height).move(0, 0) - } - -}) - -// Usage: -// draw.ellipse(200, 100) \ No newline at end of file +}) \ No newline at end of file diff --git a/src/gradient.js b/src/gradient.js index c5c51d1..e95e18b 100755 --- a/src/gradient.js +++ b/src/gradient.js @@ -1,59 +1,68 @@ -SVG.Gradient = function(type) { - this.constructor.call(this, SVG.create(type + 'Gradient')) - - /* store type */ - this.type = type -} - -// Inherit from SVG.Container -SVG.Gradient.prototype = new SVG.Container - -// -SVG.extend(SVG.Gradient, { - // From position - from: function(x, y) { - return this.type == 'radial' ? - this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) : - this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) }) - } - // To position -, to: function(x, y) { - return this.type == 'radial' ? - this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) : - this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) }) - } - // Radius for radial gradient -, radius: function(r) { - return this.type == 'radial' ? - this.attr({ r: new SVG.Number(r) }) : - this - } - // Add a color stop -, at: function(stop) { - return this.put(new SVG.Stop(stop)) - } - // Update gradient -, update: function(block) { - /* remove all stops */ - this.clear() - - /* invoke passed block */ - block(this) +SVG.Gradient = SVG.invent({ + // Initialize node + create: function(type) { + this.constructor.call(this, SVG.create(type + 'Gradient')) - return this + /* store type */ + this.type = type } - // Return the fill id -, fill: function() { - return 'url(#' + this.attr('id') + ')' - } - // Alias string convertion to fill -, toString: function() { - return this.fill() + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // From position + from: function(x, y) { + return this.type == 'radial' ? + this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) : + this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) }) + } + // To position + , to: function(x, y) { + return this.type == 'radial' ? + this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) : + this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) }) + } + // Radius for radial gradient + , radius: function(r) { + return this.type == 'radial' ? + this.attr({ r: new SVG.Number(r) }) : + this + } + // Add a color stop + , at: function(stop) { + return this.put(new SVG.Stop().update(stop)) + } + // Update gradient + , update: function(block) { + /* remove all stops */ + this.clear() + + /* invoke passed block */ + block(this) + + return this + } + // Return the fill id + , fill: function() { + return 'url(#' + this.attr('id') + ')' + } + // Alias string convertion to fill + , toString: function() { + return this.fill() + } } + // Add parent method +, construct: { + // Create gradient element in defs + gradient: function(type, block) { + return this.defs().gradient(type, block) + } + } }) -// SVG.extend(SVG.Defs, { // define gradient gradient: function(type, block) { @@ -67,37 +76,24 @@ SVG.extend(SVG.Defs, { }) -// -SVG.extend(SVG.Container, { - // Create gradient element in defs - gradient: function(type, block) { - return this.defs().gradient(type, block) - } - -}) +SVG.Stop = SVG.invent({ + // Initialize node + create: 'stop' + // Inherit from +, inherit: SVG.Element -SVG.Stop = function(stop) { - this.constructor.call(this, SVG.create('stop')) - - /* immediatelly build stop */ - this.update(stop) -} + // Add class methods +, extend: { + // add color stops + update: function(o) { + /* set attributes */ + if (o.opacity != null) this.attr('stop-opacity', o.opacity) + if (o.color != null) this.attr('stop-color', o.color) + if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) -// Inherit from SVG.Element -SVG.Stop.prototype = new SVG.Element - -// -SVG.extend(SVG.Stop, { - // add color stops - update: function(o) { - /* set attributes */ - if (o.opacity != null) this.attr('stop-opacity', o.opacity) - if (o.color != null) this.attr('stop-color', o.color) - if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) - - return this + return this + } } - -}) +}) diff --git a/src/group.js b/src/group.js index 6f2d502..cfb99ac 100755 --- a/src/group.js +++ b/src/group.js @@ -1,36 +1,35 @@ -SVG.G = function() { - this.constructor.call(this, SVG.create('g')) -} +SVG.G = SVG.invent({ + // Initialize node + create: 'g' -// Inherit from SVG.Container -SVG.G.prototype = new SVG.Container - -// -SVG.extend(SVG.G, { - // Move over x-axis - x: function(x) { - return x == null ? this.trans.x : this.transform('x', x) - } - // Move over y-axis -, y: function(y) { - return y == null ? this.trans.y : this.transform('y', y) - } - // Move by center over x-axis -, cx: function(x) { - return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) - } - // Move by center over y-axis -, cy: function(y) { - return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) - } + // Inherit from +, inherit: SVG.Container -}) - -// -SVG.extend(SVG.Container, { - // Create a group element - group: function() { - return this.put(new SVG.G) + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + return x == null ? this.trans.x : this.transform('x', x) + } + // Move over y-axis + , y: function(y) { + return y == null ? this.trans.y : this.transform('y', y) + } + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) + } + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) + } } + // Add parent method +, construct: { + // Create a group element + group: function() { + return this.put(new SVG.G) + } + } }) \ No newline at end of file diff --git a/src/hyperlink.js b/src/hyperlink.js index 7b9d986..1981d52 100644 --- a/src/hyperlink.js +++ b/src/hyperlink.js @@ -1,37 +1,35 @@ -SVG.A = function() { - this.constructor.call(this, SVG.create('a')) -} +SVG.A = SVG.invent({ + // Initialize node + create: 'a' -// Inherit from SVG.Parent -SVG.A.prototype = new SVG.Container + // Inherit from +, inherit: SVG.Container -// -SVG.extend(SVG.A, { - // Link url - to: function(url) { - return this.attr('href', url, SVG.xlink) - } - // Link show attribute -, show: function(target) { - return this.attr('show', target, SVG.xlink) - } - // Link target attribute -, target: function(target) { - return this.attr('target', target) - } - -}) - -// -SVG.extend(SVG.Container, { - // Create a hyperlink element - link: function(url) { - return this.put(new SVG.A).to(url) + // Add class methods +, extend: { + // Link url + to: function(url) { + return this.attr('href', url, SVG.xlink) + } + // Link show attribute + , show: function(target) { + return this.attr('show', target, SVG.xlink) + } + // Link target attribute + , target: function(target) { + return this.attr('target', target) + } } + // Add parent method +, construct: { + // Create a hyperlink element + link: function(url) { + return this.put(new SVG.A).to(url) + } + } }) -// SVG.extend(SVG.Element, { // Create a hyperlink element linkTo: function(url) { diff --git a/src/image.js b/src/image.js index d0a1b40..24333c7 100755 --- a/src/image.js +++ b/src/image.js @@ -1,24 +1,24 @@ -SVG.Image = function() { - this.constructor.call(this, SVG.create('image')) -} +SVG.Image = SVG.invent({ + // Initialize node + create: 'image' -// Inherit from SVG.Element -SVG.Image.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape -// -SVG.extend(SVG.Image, { - // (re)load image - load: function(url) { - return (url ? this.attr('href', (this.src = url), SVG.xlink) : this) + // Add class methods +, extend: { + // (re)load image + load: function(url) { + return (url ? this.attr('href', (this.src = url), SVG.xlink) : this) + } } -}) - -// -SVG.extend(SVG.Container, { - // Create image element, load image and set its size - image: function(source, width, height) { - width = width != null ? width : 100 - return this.put(new SVG.Image().load(source).size(width, height != null ? height : width)) + + // Add parent method +, construct: { + // Create image element, load image and set its size + image: function(source, width, height) { + width = width != null ? width : 100 + return this.put(new SVG.Image().load(source).size(width, height != null ? height : width)) + } } - }) \ No newline at end of file diff --git a/src/inventor.js b/src/inventor.js new file mode 100644 index 0000000..f8244f5 --- /dev/null +++ b/src/inventor.js @@ -0,0 +1,23 @@ +// Invent new element +SVG.invent = function(config) { + /* create element initializer */ + var initializer = typeof config.create == 'function' ? + config.create : + function() { + this.constructor.call(this, SVG.create(config.create)) + } + + /* inherit prototype */ + if (config.inherit) + initializer.prototype = new config.inherit + + /* extend with methods */ + if (config.extend) + SVG.extend(initializer, config.extend) + + /* attach construct method to parent */ + if (config.construct) + SVG.extend(config.parent || SVG.Container, config.construct) + + return initializer +} \ No newline at end of file diff --git a/src/line.js b/src/line.js index e049ccb..bdbbcde 100755 --- a/src/line.js +++ b/src/line.js @@ -1,75 +1,74 @@ -SVG.Line = function() { - this.constructor.call(this, SVG.create('line')) -} +SVG.Line = SVG.invent({ + // Initialize node + create: 'line' -// Inherit from SVG.Shape -SVG.Line.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape -// Add required methods -SVG.extend(SVG.Line, { - // Move over x-axis - x: function(x) { - var b = this.bbox() - - return x == null ? b.x : this.attr({ - x1: this.attr('x1') - b.x + x - , x2: this.attr('x2') - b.x + x - }) - } - // Move over y-axis -, y: function(y) { - var b = this.bbox() - - return y == null ? b.y : this.attr({ - y1: this.attr('y1') - b.y + y - , y2: this.attr('y2') - b.y + y - }) - } - // Move by center over x-axis -, cx: function(x) { - var half = this.bbox().width / 2 - return x == null ? this.x() + half : this.x(x - half) - } - // Move by center over y-axis -, cy: function(y) { - var half = this.bbox().height / 2 - return y == null ? this.y() + half : this.y(y - half) - } - // Set width of element -, width: function(width) { - var b = this.bbox() + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + var b = this.bbox() + + return x == null ? b.x : this.attr({ + x1: this.attr('x1') - b.x + x + , x2: this.attr('x2') - b.x + x + }) + } + // Move over y-axis + , y: function(y) { + var b = this.bbox() + + return y == null ? b.y : this.attr({ + y1: this.attr('y1') - b.y + y + , y2: this.attr('y2') - b.y + y + }) + } + // Move by center over x-axis + , cx: function(x) { + var half = this.bbox().width / 2 + return x == null ? this.x() + half : this.x(x - half) + } + // Move by center over y-axis + , cy: function(y) { + var half = this.bbox().height / 2 + return y == null ? this.y() + half : this.y(y - half) + } + // Set width of element + , width: function(width) { + var b = this.bbox() - return width == null ? b.width : this.attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', b.x + width) - } - // Set height of element -, height: function(height) { - var b = this.bbox() + return width == null ? b.width : this.attr(this.attr('x1') < this.attr('x2') ? 'x2' : 'x1', b.x + width) + } + // Set height of element + , height: function(height) { + var b = this.bbox() - return height == null ? b.height : this.attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', b.y + height) - } - // Set line size by width and height -, size: function(width, height) { - var p = this._proportionalSize(width, height) + return height == null ? b.height : this.attr(this.attr('y1') < this.attr('y2') ? 'y2' : 'y1', b.y + height) + } + // Set line size by width and height + , size: function(width, height) { + var p = this._proportionalSize(width, height) - return this.width(p.width).height(p.height) - } - // Set path data -, plot: function(x1, y1, x2, y2) { - return this.attr({ - x1: x1 - , y1: y1 - , x2: x2 - , y2: y2 - }) + return this.width(p.width).height(p.height) + } + // Set path data + , plot: function(x1, y1, x2, y2) { + return this.attr({ + x1: x1 + , y1: y1 + , x2: x2 + , y2: y2 + }) + } } -}) - -// -SVG.extend(SVG.Container, { - // Create a line element - line: function(x1, y1, x2, y2) { - return this.put(new SVG.Line().plot(x1, y1, x2, y2)) + // Add parent method +, construct: { + // Create a line element + line: function(x1, y1, x2, y2) { + return this.put(new SVG.Line().plot(x1, y1, x2, y2)) + } } - }) diff --git a/src/mask.js b/src/mask.js index 2aa53d7..84d75b8 100755 --- a/src/mask.js +++ b/src/mask.js @@ -1,31 +1,42 @@ -SVG.Mask = function() { - this.constructor.call(this, SVG.create('mask')) +SVG.Mask = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('mask')) - /* keep references to masked elements */ - this.targets = [] -} + /* keep references to masked elements */ + this.targets = [] + } -// Inherit from SVG.Container -SVG.Mask.prototype = new SVG.Container + // Inherit from +, inherit: SVG.Container -// -SVG.extend(SVG.Mask, { - // Unmask all masked elements and remove itself - remove: function() { - /* unmask all targets */ - for (var i = this.targets.length - 1; i >= 0; i--) - if (this.targets[i]) - this.targets[i].unmask() - delete this.targets + // Add class methods +, extend: { + // Unmask all masked elements and remove itself + remove: function() { + /* unmask all targets */ + for (var i = this.targets.length - 1; i >= 0; i--) + if (this.targets[i]) + this.targets[i].unmask() + delete this.targets - /* remove mask from parent */ - this.parent.removeElement(this) - - return this + /* remove mask from parent */ + this.parent.removeElement(this) + + return this + } + } + + // Add parent method +, construct: { + // Create masking element + mask: function() { + return this.defs().put(new SVG.Mask) + } } }) -// + SVG.extend(SVG.Element, { // Distribute mask to svg element maskWith: function(element) { @@ -45,12 +56,3 @@ SVG.extend(SVG.Element, { } }) - -// -SVG.extend(SVG.Container, { - // Create masking element - mask: function() { - return this.defs().put(new SVG.Mask) - } - -}) \ No newline at end of file diff --git a/src/nested.js b/src/nested.js index d42abf9..cd3a0e0 100755 --- a/src/nested.js +++ b/src/nested.js @@ -1,17 +1,19 @@ -SVG.Nested = function() { - this.constructor.call(this, SVG.create('svg')) - - this.style('overflow', 'visible') -} - -// Inherit from SVG.Container -SVG.Nested.prototype = new SVG.Container +SVG.Nested = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('svg')) + + this.style('overflow', 'visible') + } -// -SVG.extend(SVG.Container, { - // Create nested svg document + // Inherit from +, inherit: SVG.Container + + // Add parent method +, construct: { + // Create nested svg document nested: function() { - return this.put(new SVG.Nested) + return this.put(new SVG.Nested) + } } - }) \ No newline at end of file diff --git a/src/parent.js b/src/parent.js index 4436d72..44514c2 100755 --- a/src/parent.js +++ b/src/parent.js @@ -1,102 +1,106 @@ -SVG.Parent = function(element) { - this.constructor.call(this, element) -} +SVG.Parent = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) + } -// Inherit from SVG.Element -SVG.Parent.prototype = new SVG.Element + // Inherit from +, inherit: SVG.Element -// -SVG.extend(SVG.Parent, { - // Returns all child elements - children: function() { - return this._children || (this._children = []) - } - // Add given element at a position -, add: function(element, i) { - if (!this.has(element)) { - /* define insertion index if none given */ - i = i == null ? this.children().length : i - - /* remove references from previous parent */ - if (element.parent) - element.parent.children().splice(element.parent.index(element), 1) - - /* add element references */ - this.children().splice(i, 0, element) - this.node.insertBefore(element.node, this.node.childNodes[i] || null) - element.parent = this + // Add class methods +, extend: { + // Returns all child elements + children: function() { + return this._children || (this._children = []) } + // Add given element at a position + , add: function(element, i) { + if (!this.has(element)) { + /* define insertion index if none given */ + i = i == null ? this.children().length : i + + /* remove references from previous parent */ + if (element.parent) + element.parent.children().splice(element.parent.index(element), 1) + + /* add element references */ + this.children().splice(i, 0, element) + this.node.insertBefore(element.node, this.node.childNodes[i] || null) + element.parent = this + } - /* reposition defs */ - if (this._defs) { - this.node.removeChild(this._defs.node) - this.node.appendChild(this._defs.node) + /* reposition defs */ + if (this._defs) { + this.node.removeChild(this._defs.node) + this.node.appendChild(this._defs.node) + } + + return this } - - return this - } - // Basically does the same as `add()` but returns the added element instead -, put: function(element, i) { - this.add(element, i) - return element - } - // Checks if the given element is a child -, has: function(element) { - return this.index(element) >= 0 - } - // Gets index of given element -, index: function(element) { - return this.children().indexOf(element) - } - // Get a element at the given index -, get: function(i) { - return this.children()[i] - } - // Get first child, skipping the defs node -, first: function() { - return this.children()[0] - } - // Get the last child -, last: function() { - return this.children()[this.children().length - 1] - } - // Iterates over all children and invokes a given block -, each: function(block, deep) { - var i, il - , children = this.children() - - for (i = 0, il = children.length; i < il; i++) { - if (children[i] instanceof SVG.Element) - block.apply(children[i], [i, children]) - - if (deep && (children[i] instanceof SVG.Container)) - children[i].each(block, deep) + // Basically does the same as `add()` but returns the added element instead + , put: function(element, i) { + this.add(element, i) + return element } - - return this - } - // Remove a child element at a position -, removeElement: function(element) { - this.children().splice(this.index(element), 1) - this.node.removeChild(element.node) - element.parent = null + // Checks if the given element is a child + , has: function(element) { + return this.index(element) >= 0 + } + // Gets index of given element + , index: function(element) { + return this.children().indexOf(element) + } + // Get a element at the given index + , get: function(i) { + return this.children()[i] + } + // Get first child, skipping the defs node + , first: function() { + return this.children()[0] + } + // Get the last child + , last: function() { + return this.children()[this.children().length - 1] + } + // Iterates over all children and invokes a given block + , each: function(block, deep) { + var i, il + , children = this.children() + + for (i = 0, il = children.length; i < il; i++) { + if (children[i] instanceof SVG.Element) + block.apply(children[i], [i, children]) + + if (deep && (children[i] instanceof SVG.Container)) + children[i].each(block, deep) + } - return this - } - // Remove all elements in this container -, clear: function() { - /* remove children */ - for (var i = this.children().length - 1; i >= 0; i--) - this.removeElement(this.children()[i]) + return this + } + // Remove a child element at a position + , removeElement: function(element) { + this.children().splice(this.index(element), 1) + this.node.removeChild(element.node) + element.parent = null + + return this + } + // Remove all elements in this container + , clear: function() { + /* remove children */ + for (var i = this.children().length - 1; i >= 0; i--) + this.removeElement(this.children()[i]) - /* remove defs node */ - if (this._defs) - this._defs.clear() + /* remove defs node */ + if (this._defs) + this._defs.clear() - return this - } - , // Get defs - defs: function() { - return this.doc().defs() + return this + } + , // Get defs + defs: function() { + return this.doc().defs() + } } -}) \ No newline at end of file + +}) diff --git a/src/path.js b/src/path.js index 60e6c73..4ce57f6 100755 --- a/src/path.js +++ b/src/path.js @@ -1,49 +1,49 @@ -SVG.Path = function() { - this.constructor.call(this, SVG.create('path')) -} +SVG.Path = SVG.invent({ + // Initialize node + create: 'path' -// Inherit from SVG.Shape -SVG.Path.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape -SVG.extend(SVG.Path, { - // Plot new poly points - plot: function(p) { - return this.attr('d', (this.array = new SVG.PathArray(p, [{ type:'M',x:0,y:0 }]))) + // Add class methods +, extend: { + // Plot new poly points + plot: function(p) { + return this.attr('d', (this.array = new SVG.PathArray(p, [{ type:'M',x:0,y:0 }]))) + } + // Move by left top corner + , move: function(x, y) { + return this.attr('d', this.array.move(x, y)) + } + // Move by left top corner over x-axis + , x: function(x) { + return x == null ? this.bbox().x : this.move(x, this.bbox().y) + } + // Move by left top corner over y-axis + , y: function(y) { + return y == null ? this.bbox().y : this.move(this.bbox().x, y) + } + // Set element size to given width and height + , size: function(width, height) { + var p = this._proportionalSize(width, height) + + return this.attr('d', this.array.size(p.width, p.height)) + } + // Set width of element + , width: function(width) { + return width == null ? this.bbox().width : this.size(width, this.bbox().height) + } + // Set height of element + , height: function(height) { + return height == null ? this.bbox().height : this.size(this.bbox().width, height) + } + } + + // Add parent method +, construct: { + // Create a wrapped path element + path: function(d) { + return this.put(new SVG.Path).plot(d) + } } - // Move by left top corner -, move: function(x, y) { - return this.attr('d', this.array.move(x, y)) - } - // Move by left top corner over x-axis -, x: function(x) { - return x == null ? this.bbox().x : this.move(x, this.bbox().y) - } - // Move by left top corner over y-axis -, y: function(y) { - return y == null ? this.bbox().y : this.move(this.bbox().x, y) - } - // Set element size to given width and height -, size: function(width, height) { - var p = this._proportionalSize(width, height) - - return this.attr('d', this.array.size(p.width, p.height)) - } - // Set width of element -, width: function(width) { - return width == null ? this.bbox().width : this.size(width, this.bbox().height) - } - // Set height of element -, height: function(height) { - return height == null ? this.bbox().height : this.size(this.bbox().width, height) - } - -}) - -// -SVG.extend(SVG.Container, { - // Create a wrapped path element - path: function(d) { - return this.put(new SVG.Path).plot(d) - } - }) \ No newline at end of file diff --git a/src/poly.js b/src/poly.js index 247ab8f..5a0f9b2 100755 --- a/src/poly.js +++ b/src/poly.js @@ -1,16 +1,34 @@ -SVG.Polyline = function() { - this.constructor.call(this, SVG.create('polyline')) -} +SVG.Polyline = SVG.invent({ + // Initialize node + create: 'polyline' -// Inherit from SVG.Shape -SVG.Polyline.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create a wrapped polyline element + polyline: function(p) { + return this.put(new SVG.Polyline).plot(p) + } + } +}) -SVG.Polygon = function() { - this.constructor.call(this, SVG.create('polygon')) -} +SVG.Polygon = SVG.invent({ + // Initialize node + create: 'polygon' -// Inherit from SVG.Shape -SVG.Polygon.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create a wrapped polygon element + polygon: function(p) { + return this.put(new SVG.Polygon).plot(p) + } + } +}) // Add polygon-specific functions SVG.extend(SVG.Polyline, SVG.Polygon, { @@ -51,17 +69,4 @@ SVG.extend(SVG.Polyline, SVG.Polygon, { return this.attr('points', this.array.size(p.width, p.height)) } -}) - -// -SVG.extend(SVG.Container, { - // Create a wrapped polyline element - polyline: function(p) { - return this.put(new SVG.Polyline).plot(p) - } - // Create a wrapped polygon element -, polygon: function(p) { - return this.put(new SVG.Polygon).plot(p) - } - }) \ No newline at end of file diff --git a/src/rect.js b/src/rect.js index 583556d..313c96d 100755 --- a/src/rect.js +++ b/src/rect.js @@ -1,15 +1,17 @@ -SVG.Rect = function() { - this.constructor.call(this, SVG.create('rect')) -} - -// Inherit from SVG.Shape -SVG.Rect.prototype = new SVG.Shape - -// -SVG.extend(SVG.Container, { - // Create a rect element - rect: function(width, height) { - return this.put(new SVG.Rect().size(width, height)) - } +SVG.Rect = SVG.invent({ + // Initialize node + create: 'rect' + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create a rect element + rect: function(width, height) { + return this.put(new SVG.Rect().size(width, height)) + } + + } + }) \ No newline at end of file diff --git a/src/set.js b/src/set.js index 9a6addc..38251f5 100755 --- a/src/set.js +++ b/src/set.js @@ -1,91 +1,102 @@ -SVG.Set = function() { - /* set initial state */ - this.clear() -} - -// Set FX class -SVG.SetFX = function(set) { - /* store reference to set */ - this.set = set -} - -// -SVG.extend(SVG.Set, { - // Add element to set - add: function() { - var i, il, elements = [].slice.call(arguments) - - for (i = 0, il = elements.length; i < il; i++) - this.members.push(elements[i]) - - return this +SVG.Set = SVG.invent({ + // Initialize + create: function() { + /* set initial state */ + this.clear() } - // Remove element from set -, remove: function(element) { - var i = this.index(element) - - /* remove given child */ - if (i > -1) - this.members.splice(i, 1) - - return this - } - // Iterate over all members -, each: function(block) { - for (var i = 0, il = this.members.length; i < il; i++) - block.apply(this.members[i], [i, this.members]) - return this - } - // Restore to defaults -, clear: function() { - /* initialize store */ - this.members = [] + // Add class methods +, extend: { + // Add element to set + add: function() { + var i, il, elements = [].slice.call(arguments) - return this - } - // Checks if a given element is present in set -, has: function(element) { - return this.index(element) >= 0 - } - // retuns index of given element in set -, index: function(element) { - return this.members.indexOf(element) - } - // Get member at given index -, get: function(i) { - return this.members[i] - } - // Default value -, valueOf: function() { - return this.members - } - // Get the bounding box of all members included or empty box if set has no items -, bbox: function(){ - var box = new SVG.BBox() + for (i = 0, il = elements.length; i < il; i++) + this.members.push(elements[i]) + + return this + } + // Remove element from set + , remove: function(element) { + var i = this.index(element) + + /* remove given child */ + if (i > -1) + this.members.splice(i, 1) - /* return an empty box of there are no members */ - if (this.members.length == 0) - return box + return this + } + // Iterate over all members + , each: function(block) { + for (var i = 0, il = this.members.length; i < il; i++) + block.apply(this.members[i], [i, this.members]) - /* get the first rbox and update the target bbox */ - var rbox = this.members[0].rbox() - box.x = rbox.x - box.y = rbox.y - box.width = rbox.width - box.height = rbox.height + return this + } + // Restore to defaults + , clear: function() { + /* initialize store */ + this.members = [] - this.each(function() { - /* user rbox for correct position and visual representation */ - box = box.merge(this.rbox()) - }) + return this + } + // Checks if a given element is present in set + , has: function(element) { + return this.index(element) >= 0 + } + // retuns index of given element in set + , index: function(element) { + return this.members.indexOf(element) + } + // Get member at given index + , get: function(i) { + return this.members[i] + } + // Default value + , valueOf: function() { + return this.members + } + // Get the bounding box of all members included or empty box if set has no items + , bbox: function(){ + var box = new SVG.BBox() + + /* return an empty box of there are no members */ + if (this.members.length == 0) + return box + + /* get the first rbox and update the target bbox */ + var rbox = this.members[0].rbox() + box.x = rbox.x + box.y = rbox.y + box.width = rbox.width + box.height = rbox.height + + this.each(function() { + /* user rbox for correct position and visual representation */ + box = box.merge(this.rbox()) + }) - return box + return box + } + } + + // Add parent method +, construct: { + // Create a new set + set: function() { + return new SVG.Set + } } - }) +SVG.SetFX = SVG.invent({ + // Initialize node + create: function(set) { + /* store reference to set */ + this.set = set + } +}) // Alias methods SVG.Set.inherit = function() { @@ -127,14 +138,4 @@ SVG.Set.inherit = function() { }) } -// -SVG.extend(SVG.Container, { - // Create a new set - set: function() { - return new SVG.Set - } - -}) - - diff --git a/src/shape.js b/src/shape.js index 0594ed7..15c1fa9 100755 --- a/src/shape.js +++ b/src/shape.js @@ -1,6 +1,10 @@ -SVG.Shape = function(element) { - this.constructor.call(this, element) -} +SVG.Shape = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) + } -// Inherit from SVG.Element -SVG.Shape.prototype = new SVG.Element \ No newline at end of file + // Inherit from +, inherit: SVG.Element + +}) \ No newline at end of file diff --git a/src/svg.js b/src/svg.js index b0f6ecd..364a786 100755 --- a/src/svg.js +++ b/src/svg.js @@ -1,18 +1,14 @@ -// 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('drawing').size(300, 300) -// var rect = draw.rect(100, 100).attr({ fill: '#f06' }) - - - // The main wrapping element this.SVG = function(element) { - if (!SVG.parser) - SVG.prepare() + if (SVG.supported) { + element = new SVG.Doc(element) - if (SVG.supported) - return new SVG.Doc(element) + if (!SVG.parser) + SVG.prepare(element) + + return element + } } // Default namespaces @@ -66,15 +62,15 @@ SVG.get = function(id) { } // Initialize parsing element -SVG.prepare = function() { - /* select document body and create svg element*/ - var body = document.getElementsByTagName('body')[0] || document.getElementsByTagName('svg')[0] - , draw = new SVG.Doc(body).size(2, 2).style('opacity:0;position:fixed;left:100%;top:100%;') +SVG.prepare = function(element) { + /* select document body and create invisible svg element */ + var body = document.getElementsByTagName('body')[0] + , draw = (body ? new SVG.Doc(body) : element.nested()).size(2, 2) /* create parser object */ SVG.parser = { - body: body - , draw: draw + body: body || element.parent + , draw: draw.style('opacity:0;position:fixed;left:100%;top:100%;overflow:hidden') , poly: draw.polygon().node , path: draw.path().node } diff --git a/src/text.js b/src/text.js index 5fc9ece..73ef290 100755 --- a/src/text.js +++ b/src/text.js @@ -2,209 +2,211 @@ // List font style attributes as they should be applied to style var _styleAttr = ('size family weight stretch variant style').split(' ') -SVG.Text = function() { - this.constructor.call(this, SVG.create('text')) - - /* define default style */ - this.styles = { - 'font-size': 16 - , 'font-family': 'Helvetica, Arial, sans-serif' - , 'text-anchor': 'start' - } - - this._leading = new SVG.Number('1.2em') - this._rebuild = true -} - -// Inherit from SVG.Element -SVG.Text.prototype = new SVG.Shape - -// -SVG.extend(SVG.Text, { - // Move over x-axis - x: function(x, a) { - /* act as getter */ - if (x == null) - return a ? this.attr('x') : this.bbox().x +SVG.Text = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('text')) - /* set x taking anchor in mind */ - if (!a) { - a = this.style('text-anchor') - x = a == 'start' ? x : a == 'end' ? x + this.bbox().width : x + this.bbox().width / 2 + /* define default style */ + this.styles = { + 'font-size': 16 + , 'font-family': 'Helvetica, Arial, sans-serif' + , 'text-anchor': 'start' } - - /* move lines as well if no textPath si present */ - if (!this.textPath) - this.lines.each(function() { if (this.newLined) this.x(x) }) - - return this.attr('x', x) - } - // Move center over x-axis -, cx: function(x, a) { - return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) - } - // Move center over y-axis -, cy: function(y, a) { - return y == null ? this.bbox().cy : this.y(a ? y : y - this.bbox().height / 2) - } - // Move element to given x and y values -, move: function(x, y, a) { - return this.x(x, a).y(y) - } - // Move element by its center -, center: function(x, y, a) { - return this.cx(x, a).cy(y, a) - } - // Set the text content -, text: function(text) { - /* act as getter */ - if (text == null) - return this.content - /* remove existing lines */ - this.clear() - - if (typeof text === 'function') { - this._rebuild = false + this._leading = new SVG.Number('1.2em') + this._rebuild = true + } - text.call(this, this) + // Inherit from +, inherit: SVG.Shape - } else { - this._rebuild = true + // Add class methods +, extend: { + // Move over x-axis + x: function(x, a) { + /* act as getter */ + if (x == null) + return a ? this.attr('x') : this.bbox().x + + /* set x taking anchor in mind */ + if (!a) { + a = this.style('text-anchor') + x = a == 'start' ? x : a == 'end' ? x + this.bbox().width : x + this.bbox().width / 2 + } + + /* move lines as well if no textPath si present */ + if (!this.textPath) + this.lines.each(function() { if (this.newLined) this.x(x) }) - /* make sure text is not blank */ - text = SVG.regex.isBlank.test(text) ? 'text' : text + return this.attr('x', x) + } + // Move center over x-axis + , cx: function(x, a) { + return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) + } + // Move center over y-axis + , cy: function(y, a) { + return y == null ? this.bbox().cy : this.y(a ? y : y - this.bbox().height / 2) + } + // Move element to given x and y values + , move: function(x, y, a) { + return this.x(x, a).y(y) + } + // Move element by its center + , center: function(x, y, a) { + return this.cx(x, a).cy(y, a) + } + // Set the text content + , text: function(text) { + /* act as getter */ + if (text == null) + return this.content - var i, il - , lines = text.split('\n') + /* remove existing lines */ + this.clear() - /* build new lines */ - for (i = 0, il = lines.length; i < il; i++) - this.tspan(lines[i]).newLine() - - this.rebuild() + if (typeof text === 'function') { + this._rebuild = false + + text.call(this, this) + + } else { + this._rebuild = true + + /* make sure text is not blank */ + text = SVG.regex.isBlank.test(text) ? 'text' : text + + var i, il + , lines = text.split('\n') + + /* build new lines */ + for (i = 0, il = lines.length; i < il; i++) + this.tspan(lines[i]).newLine() + + this.rebuild() + } + + return this } - - return this - } - // Create a tspan -, tspan: function(text) { - var node = this.textPath ? this.textPath.node : this.node - , tspan = new SVG.TSpan().text(text) - , style = this.style() - - /* add new tspan */ - node.appendChild(tspan.node) - this.lines.add(tspan) - - /* add style if any */ - if (!SVG.regex.isBlank.test(style)) - tspan.style(style) + // Create a tspan + , tspan: function(text) { + var node = this.textPath ? this.textPath.node : this.node + , tspan = new SVG.TSpan().text(text) + , style = this.style() + + /* add new tspan */ + node.appendChild(tspan.node) + this.lines.add(tspan) - /* store content */ - this.content += text + /* add style if any */ + if (!SVG.regex.isBlank.test(style)) + tspan.style(style) - /* store text parent */ - tspan.parent = this + /* store content */ + this.content += text - return tspan - } - // 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 */ - value = new SVG.Number(value) - this._leading = value - - /* apply leading */ - this.lines.each(function() { - if (this.newLined) - this.attr('dy', value) - }) + /* store text parent */ + tspan.parent = this - return this - } - // rebuild appearance type -, rebuild: function() { - var self = this - - /* define position of all lines */ - if (this._rebuild) { - this.lines.attr({ - x: this.attr('x') - , dy: this._leading - , style: this.style() - }) + return tspan } + // 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 */ + value = new SVG.Number(value) + this._leading = value + + /* apply leading */ + this.lines.each(function() { + if (this.newLined) + this.attr('dy', value) + }) - return this - } - // Clear all lines -, clear: function() { - var node = this.textPath ? this.textPath.node : this.node + return this + } + // rebuild appearance type + , rebuild: function() { + var self = this + + /* define position of all lines */ + if (this._rebuild) { + this.lines.attr({ + x: this.attr('x') + , dy: this._leading + , style: this.style() + }) + } + + return this + } + // Clear all lines + , clear: function() { + var node = this.textPath ? this.textPath.node : this.node - /* remove existing child nodes */ - while (node.hasChildNodes()) - node.removeChild(node.lastChild) - - /* refresh lines */ - delete this.lines - this.lines = new SVG.Set - - /* initialize content */ - this.content = '' + /* remove existing child nodes */ + while (node.hasChildNodes()) + node.removeChild(node.lastChild) + + /* refresh lines */ + delete this.lines + this.lines = new SVG.Set + + /* initialize content */ + this.content = '' - return this + return this + } } -}) - -// -SVG.extend(SVG.Container, { - // Create text element - text: function(text) { - return this.put(new SVG.Text).text(text) + // Add parent method +, construct: { + // Create text element + text: function(text) { + return this.put(new SVG.Text).text(text) + } } - }) -// tspan class -SVG.TSpan = function() { - this.constructor.call(this, SVG.create('tspan')) -} +SVG.TSpan = SVG.invent({ + // Initialize node + create: 'tspan' -// Inherit from SVG.Shape -SVG.TSpan.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape -// Include the container object -SVG.extend(SVG.TSpan, { - // Set text content - text: function(text) { - this.node.appendChild(document.createTextNode(text)) - - return this - } - // Shortcut dx -, dx: function(dx) { - return this.attr('dx', dx) - } - // Shortcut dy -, dy: function(dy) { - return this.attr('dy', dy) - } - // Create new line -, newLine: function() { - this.newLined = true - this.parent.content += '\n' - this.dy(this.parent._leading) - return this.attr('x', this.parent.x()) + // Add class methods +, extend: { + // Set text content + text: function(text) { + this.node.appendChild(document.createTextNode(text)) + + return this + } + // Shortcut dx + , dx: function(dx) { + return this.attr('dx', dx) + } + // Shortcut dy + , dy: function(dy) { + return this.attr('dy', dy) + } + // Create new line + , newLine: function() { + this.newLined = true + this.parent.content += '\n' + this.dy(this.parent._leading) + return this.attr('x', this.parent.x()) + } } - + }) + diff --git a/src/textpath.js b/src/textpath.js index 04d7bde..9f7b088 100755 --- a/src/textpath.js +++ b/src/textpath.js @@ -1,39 +1,42 @@ -SVG.TextPath = function() { - this.constructor.call(this, SVG.create('textPath')) -} - -// Inherit from SVG.Element -SVG.TextPath.prototype = new SVG.Element - -// -SVG.extend(SVG.Text, { - // Create path for text to run on - path: function(d) { - /* create textPath element */ - this.textPath = new SVG.TextPath - - /* move lines to textpath */ - while(this.node.hasChildNodes()) - this.textPath.node.appendChild(this.node.firstChild) - - /* add textPath element as child node */ - this.node.appendChild(this.textPath.node) - - /* create path in defs */ - this.track = this.doc().defs().path(d, true) - - /* create circular reference */ - this.textPath.parent = this - - /* link textPath to path and add content */ - this.textPath.attr('href', '#' + this.track, SVG.xlink) - - return this +SVG.TextPath = SVG.invent({ + // Initialize node + create: 'textPath' + + // Inherit from +, inherit: SVG.Element + + // Define parent class +, parent: SVG.Text + + // Add parent method +, construct: { + // Create path for text to run on + path: function(d) { + /* create textPath element */ + this.textPath = new SVG.TextPath + + /* move lines to textpath */ + while(this.node.hasChildNodes()) + this.textPath.node.appendChild(this.node.firstChild) + + /* add textPath element as child node */ + this.node.appendChild(this.textPath.node) + + /* create path in defs */ + this.track = this.doc().defs().path(d, true) + + /* create circular reference */ + this.textPath.parent = this + + /* link textPath to path and add content */ + this.textPath.attr('href', '#' + this.track, SVG.xlink) + + return this + } + // Plot path if any + , plot: function(d) { + if (this.track) this.track.plot(d) + return this + } } - // Plot path if any -, plot: function(d) { - if (this.track) this.track.plot(d) - return this - } - }) \ No newline at end of file diff --git a/src/use.js b/src/use.js index 0a8bb90..2690468 100755 --- a/src/use.js +++ b/src/use.js @@ -1,28 +1,27 @@ -SVG.Use = function() { - this.constructor.call(this, SVG.create('use')) -} +SVG.Use = SVG.invent({ + // Initialize node + create: 'use' -// Inherit from SVG.Shape -SVG.Use.prototype = new SVG.Shape + // Inherit from +, inherit: SVG.Shape -// -SVG.extend(SVG.Use, { - // Use element as a reference - element: function(element) { - /* store target element */ - this.target = element + // Add class methods +, extend: { + // Use element as a reference + element: function(element) { + /* store target element */ + this.target = element - /* set lined element */ - return this.attr('href', '#' + element, SVG.xlink) + /* set lined element */ + return this.attr('href', '#' + element, SVG.xlink) + } } -}) - -// -SVG.extend(SVG.Container, { - // Create a use element - use: function(element) { - return this.put(new SVG.Use).element(element) + // Add parent method +, construct: { + // Create a use element + use: function(element) { + return this.put(new SVG.Use).element(element) + } } - }) \ No newline at end of file -- 2.39.5