From e9a0d5f5d7074b1cb3d1bd5a00f6a602bd55f983 Mon Sep 17 00:00:00 2001 From: wout Date: Sun, 13 Jul 2014 10:32:20 +0200 Subject: Switched to gulp for building --- dist/svg.js | 7466 +++++++++++++++++++++++++++---------------------------- dist/svg.min.js | 5 +- 2 files changed, 3727 insertions(+), 3744 deletions(-) (limited to 'dist') diff --git a/dist/svg.js b/dist/svg.js index d3f9510..37617c2 100755 --- a/dist/svg.js +++ b/dist/svg.js @@ -1,3003 +1,3039 @@ -/* svg.js 1.0.0-rc.10-23-g03f358a - svg inventor adopter regex utilities default color array pointarray patharray number viewbox element boxes matrix attr transform style parent container transporter fx relative event defs group arrange mask clip gradient pattern doc spof shape symbol use rect ellipse line poly pointed path image text textpath nested hyperlink marker sugar set data memory selector loader helpers polyfill - svgjs.com/license */ +/*! +* SVG.js - A lightweight library for manipulating and animating SVG. +* @version 1.0.0-rc.10 +* http://www.svgjs.com +* +* @copyright Wout Fierens +* @license MIT +* +* BUILT: Sun Jul 13 2014 10:29:52 GMT+0200 (CEST) +*/ ;(function() { - var SVG = this.SVG = function(element) { - if (SVG.supported) { - element = new SVG.Doc(element) - - if (!SVG.parser) - SVG.prepare(element) - - return element - } +// The main wrapping element +var SVG = this.SVG = function(element) { + if (SVG.supported) { + element = new SVG.Doc(element) + + if (!SVG.parser) + SVG.prepare(element) + + return element } - - // Default namespaces - SVG.ns = 'http://www.w3.org/2000/svg' - SVG.xmlns = 'http://www.w3.org/2000/xmlns/' - SVG.xlink = 'http://www.w3.org/1999/xlink' - - // Element id sequence - SVG.did = 1000 - - // Get next named element id - SVG.eid = function(name) { - return 'Svgjs' + capitalize(name) + (SVG.did++) +} + +// Default namespaces +SVG.ns = 'http://www.w3.org/2000/svg' +SVG.xmlns = 'http://www.w3.org/2000/xmlns/' +SVG.xlink = 'http://www.w3.org/1999/xlink' + +// Element id sequence +SVG.did = 1000 + +// Get next named element id +SVG.eid = function(name) { + return 'Svgjs' + capitalize(name) + (SVG.did++) +} + +// Method for element creation +SVG.create = function(name) { + /* create element */ + var element = document.createElementNS(this.ns, name) + + /* apply unique id */ + element.setAttribute('id', this.eid(name)) + + return element +} + +// Method for extending objects +SVG.extend = function() { + var modules, methods, key, i + + /* get list of modules */ + modules = [].slice.call(arguments) + + /* get object with extensions */ + methods = modules.pop() + + for (i = modules.length - 1; i >= 0; i--) + if (modules[i]) + for (key in methods) + modules[i].prototype[key] = methods[key] + + /* make sure SVG.Set inherits any newly added methods */ + if (SVG.Set && SVG.Set.inherit) + SVG.Set.inherit() +} + +// Initialize parsing element +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, 0) + , path = SVG.create('path') + + /* insert parsers */ + draw.node.appendChild(path) + + /* create parser object */ + SVG.parser = { + body: body || element.parent() + , draw: draw.style('opacity:0;position:fixed;left:100%;top:100%;overflow:hidden') + , poly: draw.polyline().node + , path: path } - - // Method for element creation - SVG.create = function(name) { - /* create element */ - var element = document.createElementNS(this.ns, name) - - /* apply unique id */ - element.setAttribute('id', this.eid(name)) +} + +// svg support test +SVG.supported = (function() { + return !! document.createElementNS && + !! document.createElementNS(SVG.ns,'svg').createSVGRect +})() + +if (!SVG.supported) return false + +// 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 +} +// Adopt existing svg elements +SVG.adopt = function(node) { + // Make sure a node isn't already adopted + if (node.instance) return node.instance + + // Initialize variables + var element + + // Adopt with element-specific settings + if (node.nodeName == 'svg') + element = node.parentNode instanceof SVGElement ? new SVG.Nested : new SVG.Doc + else if (node.nodeName == 'lineairGradient') + element = new SVG.Gradient('lineair') + else if (node.nodeName == 'radialGradient') + element = new SVG.Gradient('radial') + else if (SVG[capitalize(node.nodeName)]) + element = new SVG[capitalize(node.nodeName)] + else + element = new SVG.Element(node) + + // Ensure references + element.type = node.nodeName + element.node = node + node.instance = element + + // SVG.Class specific preparations + if (element instanceof SVG.Doc) + element.namespace().defs() + + return element +} +// Storage for regular expressions +SVG.regex = { + /* parse unit value */ + unit: /^(-?[\d\.]+)([a-z%]{0,2})$/ + + /* parse hex value */ +, hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i + + /* parse rgb value */ +, rgb: /rgb\((\d+),(\d+),(\d+)\)/ + + /* parse reference id */ +, reference: /#([a-z0-9\-_]+)/i + + /* test hex value */ +, isHex: /^#[a-f0-9]{3,6}$/i + + /* test rgb value */ +, isRgb: /^rgb\(/ + + /* test css declaration */ +, isCss: /[^:]+:[^;]+;?/ + + /* test for blank string */ +, isBlank: /^(\s+)?$/ + + /* test for numeric string */ +, isNumber: /^-?[\d\.]+$/ + + /* test for percent value */ +, isPercent: /^-?[\d\.]+%$/ + + /* test for image url */ +, isImage: /\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i + + /* test for namespaced event */ +, isEvent: /^[\w]+:[\w]+$/ + +} +SVG.utils = { + // Map function + map: function(array, block) { + var i + , il = array.length + , result = [] + + for (i = 0; i < il; i++) + result.push(block(array[i])) - return element + return result + } + + // Degrees to radians +, radians: function(d) { + return d % 360 * Math.PI / 180 + } + // Radians to degrees +, degrees: function(r) { + return r * 180 / Math.PI % 360 + } + +} + +SVG.defaults = { + // Default attribute values + attrs: { + /* fill and stroke */ + 'fill-opacity': 1 + , 'stroke-opacity': 1 + , 'stroke-width': 0 + , 'stroke-linejoin': 'miter' + , 'stroke-linecap': 'butt' + , fill: '#000000' + , stroke: '#000000' + , opacity: 1 + /* position */ + , x: 0 + , y: 0 + , cx: 0 + , cy: 0 + /* size */ + , width: 0 + , height: 0 + /* radius */ + , r: 0 + , rx: 0 + , ry: 0 + /* gradient */ + , offset: 0 + , 'stop-opacity': 1 + , 'stop-color': '#000000' + /* text */ + , 'font-size': 16 + , 'font-family': 'Helvetica, Arial, sans-serif' + , 'text-anchor': 'start' + } + + // Transforms +, trans: { + /* translate */ + x: 0 + , y: 0 + /* scale */ + , scaleX: 1 + , scaleY: 1 + /* rotate */ + , rotation: 0 + /* skew */ + , skewX: 0 + , skewY: 0 + /* matrix */ + , matrix: this.matrix + , a: 1 + , b: 0 + , c: 0 + , d: 1 + , e: 0 + , f: 0 } - // Method for extending objects - SVG.extend = function() { - var modules, methods, key, i +} +// Module for color convertions +SVG.Color = function(color) { + var match + + /* initialize defaults */ + this.r = 0 + this.g = 0 + this.b = 0 + + /* parse color */ + if (typeof color === 'string') { + if (SVG.regex.isRgb.test(color)) { + /* get rgb values */ + match = SVG.regex.rgb.exec(color.replace(/\s/g,'')) + + /* parse numeric values */ + this.r = parseInt(match[1]) + this.g = parseInt(match[2]) + this.b = parseInt(match[3]) + + } else if (SVG.regex.isHex.test(color)) { + /* get hex values */ + match = SVG.regex.hex.exec(fullHex(color)) + + /* parse numeric values */ + this.r = parseInt(match[1], 16) + this.g = parseInt(match[2], 16) + this.b = parseInt(match[3], 16) + + } - /* get list of modules */ - modules = [].slice.call(arguments) + } else if (typeof color === 'object') { + this.r = color.r + this.g = color.g + this.b = color.b - /* get object with extensions */ - methods = modules.pop() + } - for (i = modules.length - 1; i >= 0; i--) - if (modules[i]) - for (key in methods) - modules[i].prototype[key] = methods[key] - - /* make sure SVG.Set inherits any newly added methods */ - if (SVG.Set && SVG.Set.inherit) - SVG.Set.inherit() +} + +SVG.extend(SVG.Color, { + // Default to hex conversion + toString: function() { + return this.toHex() } - - // Initialize parsing element - 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, 0) - , path = SVG.create('path') - - /* insert parsers */ - draw.node.appendChild(path) - - /* create parser object */ - SVG.parser = { - body: body || element.parent() - , draw: draw.style('opacity:0;position:fixed;left:100%;top:100%;overflow:hidden') - , poly: draw.polyline().node - , path: path - } + // Build hex value +, toHex: function() { + return '#' + + compToHex(this.r) + + compToHex(this.g) + + compToHex(this.b) + } + // Build rgb value +, toRgb: function() { + return 'rgb(' + [this.r, this.g, this.b].join() + ')' + } + // Calculate true brightness +, brightness: function() { + return (this.r / 255 * 0.30) + + (this.g / 255 * 0.59) + + (this.b / 255 * 0.11) + } + // Make color morphable +, morph: function(color) { + this.destination = new SVG.Color(color) + + return this + } + // Get morphed color at given position +, at: function(pos) { + /* make sure a destination is defined */ + if (!this.destination) return this + + /* normalise pos */ + pos = pos < 0 ? 0 : pos > 1 ? 1 : pos + + /* generate morphed color */ + return new SVG.Color({ + r: ~~(this.r + (this.destination.r - this.r) * pos) + , g: ~~(this.g + (this.destination.g - this.g) * pos) + , b: ~~(this.b + (this.destination.b - this.b) * pos) + }) } - // svg support test - SVG.supported = (function() { - return !! document.createElementNS && - !! document.createElementNS(SVG.ns,'svg').createSVGRect - })() - - if (!SVG.supported) return false +}) + +// Testers + +// Test if given value is a color string +SVG.Color.test = function(color) { + color += '' + return SVG.regex.isHex.test(color) + || SVG.regex.isRgb.test(color) +} + +// Test if given value is a rgb object +SVG.Color.isRgb = function(color) { + return color && typeof color.r == 'number' + && typeof color.g == 'number' + && typeof color.b == 'number' +} + +// Test if given value is a color +SVG.Color.isColor = function(color) { + return SVG.Color.isRgb(color) || SVG.Color.test(color) +} +// Module for array conversion +SVG.Array = function(array, fallback) { + array = (array || []).valueOf() + + /* if array is empty and fallback is provided, use fallback */ + if (array.length == 0 && fallback) + array = fallback.valueOf() + + /* parse array */ + this.value = this.parse(array) +} + +SVG.extend(SVG.Array, { + // Make array morphable + morph: function(array) { + this.destination = this.parse(array) + + /* normalize length of arrays */ + if (this.value.length != this.destination.length) { + var lastValue = this.value[this.value.length - 1] + , lastDestination = this.destination[this.destination.length - 1] + + while(this.value.length > this.destination.length) + this.destination.push(lastDestination) + while(this.value.length < this.destination.length) + this.value.push(lastValue) + } + + return this + } + // Clean up any duplicate points +, settle: function() { + /* find all unique values */ + for (var i = 0, il = this.value.length, seen = []; i < il; i++) + if (seen.indexOf(this.value[i]) == -1) + seen.push(this.value[i]) + + /* set new value */ + return this.value = seen + } + // Get morphed array at given position +, at: function(pos) { + /* make sure a destination is defined */ + if (!this.destination) return this + /* generate morphed array */ + for (var i = 0, il = this.value.length, array = []; i < il; i++) + array.push(this.value[i] + (this.destination[i] - this.value[i]) * pos) - 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 + return new SVG.Array(array) + } + // Convert array to string +, toString: function() { + return this.value.join(' ') + } + // Real value +, valueOf: function() { + return this.value } + // Parse whitespace separated string +, parse: function(array) { + array = array.valueOf() - SVG.adopt = function(node) { - // Make sure a node isn't already adopted - if (node.instance) return node.instance - - // Initialize variables - var element - - // Adopt with element-specific settings - if (node.nodeName == 'svg') - element = node.parentNode instanceof SVGElement ? new SVG.Nested : new SVG.Doc - else if (node.nodeName == 'lineairGradient') - element = new SVG.Gradient('lineair') - else if (node.nodeName == 'radialGradient') - element = new SVG.Gradient('radial') - else if (SVG[capitalize(node.nodeName)]) - element = new SVG[capitalize(node.nodeName)] - else - element = new SVG.Element(node) - - // Ensure references - element.type = node.nodeName - element.node = node - node.instance = element - - // SVG.Class specific preparations - if (element instanceof SVG.Doc) - element.namespace().defs() - - return element + /* if already is an array, no need to parse it */ + if (Array.isArray(array)) return array + + return this.split(array) } + // Strip unnecessary whitespace +, split: function(string) { + return string.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g,'').split(' ') + } + // Reverse array +, reverse: function() { + this.value.reverse() - SVG.regex = { - /* parse unit value */ - unit: /^(-?[\d\.]+)([a-z%]{0,2})$/ - - /* parse hex value */ - , hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i - - /* parse rgb value */ - , rgb: /rgb\((\d+),(\d+),(\d+)\)/ - - /* parse reference id */ - , reference: /#([a-z0-9\-_]+)/i - - /* test hex value */ - , isHex: /^#[a-f0-9]{3,6}$/i - - /* test rgb value */ - , isRgb: /^rgb\(/ - - /* test css declaration */ - , isCss: /[^:]+:[^;]+;?/ - - /* test for blank string */ - , isBlank: /^(\s+)?$/ - - /* test for numeric string */ - , isNumber: /^-?[\d\.]+$/ - - /* test for percent value */ - , isPercent: /^-?[\d\.]+%$/ - - /* test for image url */ - , isImage: /\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i - - /* test for namespaced event */ - , isEvent: /^[\w]+:[\w]+$/ - + return this } - SVG.utils = { - // Map function - map: function(array, block) { - var i - , il = array.length - , result = [] - - for (i = 0; i < il; i++) - result.push(block(array[i])) - - return result +}) +// Poly points array +SVG.PointArray = function(array, fallback) { + this.constructor.call(this, array, fallback || [[0,0]]) +} + +// Inherit from SVG.Array +SVG.PointArray.prototype = new SVG.Array + +SVG.extend(SVG.PointArray, { + // Convert array to string + toString: function() { + /* convert to a poly point string */ + for (var i = 0, il = this.value.length, array = []; i < il; i++) + array.push(this.value[i].join(',')) + + return array.join(' ') + } + // Convert array to line object +, toLine: function() { + return { + x1: this.value[0][0] + , y1: this.value[0][1] + , x2: this.value[1][0] + , y2: this.value[1][1] } - - // Degrees to radians - , radians: function(d) { - return d % 360 * Math.PI / 180 - } - // Radians to degrees - , degrees: function(r) { - return r * 180 / Math.PI % 360 - } - } + // Get morphed array at given position +, at: function(pos) { + /* make sure a destination is defined */ + if (!this.destination) return this + + /* generate morphed point string */ + for (var i = 0, il = this.value.length, array = []; i < il; i++) + array.push([ + this.value[i][0] + (this.destination[i][0] - this.value[i][0]) * pos + , this.value[i][1] + (this.destination[i][1] - this.value[i][1]) * pos + ]) + + return new SVG.PointArray(array) + } + // Parse point string +, parse: function(array) { + array = array.valueOf() - SVG.defaults = { - // Default attribute values - attrs: { - /* fill and stroke */ - 'fill-opacity': 1 - , 'stroke-opacity': 1 - , 'stroke-width': 0 - , 'stroke-linejoin': 'miter' - , 'stroke-linecap': 'butt' - , fill: '#000000' - , stroke: '#000000' - , opacity: 1 - /* position */ - , x: 0 - , y: 0 - , cx: 0 - , cy: 0 - /* size */ - , width: 0 - , height: 0 - /* radius */ - , r: 0 - , rx: 0 - , ry: 0 - /* gradient */ - , offset: 0 - , 'stop-opacity': 1 - , 'stop-color': '#000000' - /* text */ - , 'font-size': 16 - , 'font-family': 'Helvetica, Arial, sans-serif' - , 'text-anchor': 'start' + /* if already is an array, no need to parse it */ + if (Array.isArray(array)) return array + + /* split points */ + array = this.split(array) + + /* parse points */ + for (var i = 0, il = array.length, p, points = []; i < il; i++) { + p = array[i].split(',') + points.push([parseFloat(p[0]), parseFloat(p[1])]) } - - // Transforms - , trans: { - /* translate */ - x: 0 - , y: 0 - /* scale */ - , scaleX: 1 - , scaleY: 1 - /* rotate */ - , rotation: 0 - /* skew */ - , skewX: 0 - , skewY: 0 - /* matrix */ - , matrix: this.matrix - , a: 1 - , b: 0 - , c: 0 - , d: 1 - , e: 0 - , f: 0 + + return points + } + // Move point string +, move: function(x, y) { + var box = this.bbox() + + /* get relative offset */ + x -= box.x + y -= box.y + + /* move every point */ + if (!isNaN(x) && !isNaN(y)) + for (var i = this.value.length - 1; i >= 0; i--) + this.value[i] = [this.value[i][0] + x, this.value[i][1] + y] + + return this + } + // Resize poly string +, size: function(width, height) { + var i, box = this.bbox() + + /* recalculate position of all points according to new size */ + for (i = this.value.length - 1; i >= 0; i--) { + this.value[i][0] = ((this.value[i][0] - box.x) * width) / box.width + box.x + this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y } - + + return this + } + // Get bounding box of points +, bbox: function() { + SVG.parser.poly.setAttribute('points', this.toString()) + + return SVG.parser.poly.getBBox() + } + +}) +// Path points array +SVG.PathArray = function(array, fallback) { + this.constructor.call(this, array, fallback || [['M', 0, 0]]) +} + +// Inherit from SVG.Array +SVG.PathArray.prototype = new SVG.Array + +SVG.extend(SVG.PathArray, { + // Convert array to string + toString: function() { + return arrayToString(this.value) + } + // Move path string +, move: function(x, y) { + /* get bounding box of current situation */ + var box = this.bbox() + + /* get relative offset */ + x -= box.x + y -= box.y + + if (!isNaN(x) && !isNaN(y)) { + /* move every point */ + for (var l, i = this.value.length - 1; i >= 0; i--) { + l = this.value[i][0] + + if (l == 'M' || l == 'L' || l == 'T') { + this.value[i][1] += x + this.value[i][2] += y + + } else if (l == 'H') { + this.value[i][1] += x + + } else if (l == 'V') { + this.value[i][1] += y + + } else if (l == 'C' || l == 'S' || l == 'Q') { + this.value[i][1] += x + this.value[i][2] += y + this.value[i][3] += x + this.value[i][4] += y + + if (l == 'C') { + this.value[i][5] += x + this.value[i][6] += y + } + + } else if (l == 'A') { + this.value[i][6] += x + this.value[i][7] += y + } + + } + } + + return this } + // Resize path string +, size: function(width, height) { + /* get bounding box of current situation */ + var i, l, box = this.bbox() + + /* recalculate position of all points according to new size */ + for (i = this.value.length - 1; i >= 0; i--) { + l = this.value[i][0] + + if (l == 'M' || l == 'L' || l == 'T') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x + this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y + + } else if (l == 'H') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x + + } else if (l == 'V') { + this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y + + } else if (l == 'C' || l == 'S' || l == 'Q') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x + this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y + this.value[i][3] = ((this.value[i][3] - box.x) * width) / box.width + box.x + this.value[i][4] = ((this.value[i][4] - box.y) * height) / box.height + box.y + + if (l == 'C') { + this.value[i][5] = ((this.value[i][5] - box.x) * width) / box.width + box.x + this.value[i][6] = ((this.value[i][6] - box.y) * height) / box.height + box.y + } + + } else if (l == 'A') { + /* resize radii */ + this.value[i][1] = (this.value[i][1] * width) / box.width + this.value[i][2] = (this.value[i][2] * height) / box.height + + /* move position values */ + this.value[i][6] = ((this.value[i][6] - box.x) * width) / box.width + box.x + this.value[i][7] = ((this.value[i][7] - box.y) * height) / box.height + box.y + } + + } - SVG.Color = function(color) { - var match + return this + } + // Absolutize and parse path to array +, parse: function(array) { + /* if it's already is a patharray, no need to parse it */ + if (array instanceof SVG.PathArray) return array.valueOf() + + /* prepare for parsing */ + var i, il, x0, y0, x1, y1, x2, y2, s, seg, segs + , x = 0 + , y = 0 - /* initialize defaults */ - this.r = 0 - this.g = 0 - this.b = 0 + /* populate working path */ + SVG.parser.path.setAttribute('d', typeof array === 'string' ? array : arrayToString(array)) - /* parse color */ - if (typeof color === 'string') { - if (SVG.regex.isRgb.test(color)) { - /* get rgb values */ - match = SVG.regex.rgb.exec(color.replace(/\s/g,'')) - - /* parse numeric values */ - this.r = parseInt(match[1]) - this.g = parseInt(match[2]) - this.b = parseInt(match[3]) - - } else if (SVG.regex.isHex.test(color)) { - /* get hex values */ - match = SVG.regex.hex.exec(fullHex(color)) - - /* parse numeric values */ - this.r = parseInt(match[1], 16) - this.g = parseInt(match[2], 16) - this.b = parseInt(match[3], 16) - + /* get segments */ + segs = SVG.parser.path.pathSegList + + for (i = 0, il = segs.numberOfItems; i < il; ++i) { + seg = segs.getItem(i) + s = seg.pathSegTypeAsLetter + + /* yes, this IS quite verbose but also about 30 times faster than .test() with a precompiled regex */ + if (s == 'M' || s == 'L' || s == 'H' || s == 'V' || s == 'C' || s == 'S' || s == 'Q' || s == 'T' || s == 'A') { + if ('x' in seg) x = seg.x + if ('y' in seg) y = seg.y + + } else { + if ('x1' in seg) x1 = x + seg.x1 + if ('x2' in seg) x2 = x + seg.x2 + if ('y1' in seg) y1 = y + seg.y1 + if ('y2' in seg) y2 = y + seg.y2 + if ('x' in seg) x += seg.x + if ('y' in seg) y += seg.y + + if (s == 'm') + segs.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(x, y), i) + else if (s == 'l') + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(x, y), i) + else if (s == 'h') + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(x), i) + else if (s == 'v') + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(y), i) + else if (s == 'c') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i) + else if (s == 's') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i) + else if (s == 'q') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i) + else if (s == 't') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i) + else if (s == 'a') + segs.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i) + else if (s == 'z' || s == 'Z') { + x = x0 + y = y0 + } + } + + /* record the start of a subpath */ + if (s == 'M' || s == 'm') { + x0 = x + y0 = y } - - } else if (typeof color === 'object') { - this.r = color.r - this.g = color.g - this.b = color.b - } - + + /* build internal representation */ + array = [] + segs = SVG.parser.path.pathSegList + + for (i = 0, il = segs.numberOfItems; i < il; ++i) { + seg = segs.getItem(i) + s = seg.pathSegTypeAsLetter + x = [s] + + if (s == 'M' || s == 'L' || s == 'T') + x.push(seg.x, seg.y) + else if (s == 'H') + x.push(seg.x) + else if (s == 'V') + x.push(seg.y) + else if (s == 'C') + x.push(seg.x1, seg.y1, seg.x2, seg.y2, seg.x, seg.y) + else if (s == 'S') + x.push(seg.x2, seg.y2, seg.x, seg.y) + else if (s == 'Q') + x.push(seg.x1, seg.y1, seg.x, seg.y) + else if (s == 'A') + x.push(seg.r1, seg.r2, seg.angle, seg.largeArcFlag | 0, seg.sweepFlag | 0, seg.x, seg.y) + + /* store segment */ + array.push(x) + } + + return array } - - SVG.extend(SVG.Color, { - // Default to hex conversion - toString: function() { - return this.toHex() - } - // Build hex value - , toHex: function() { - return '#' - + compToHex(this.r) - + compToHex(this.g) - + compToHex(this.b) - } - // Build rgb value - , toRgb: function() { - return 'rgb(' + [this.r, this.g, this.b].join() + ')' - } - // Calculate true brightness - , brightness: function() { - return (this.r / 255 * 0.30) - + (this.g / 255 * 0.59) - + (this.b / 255 * 0.11) - } - // Make color morphable - , morph: function(color) { - this.destination = new SVG.Color(color) - - return this + // Get bounding box of path +, bbox: function() { + SVG.parser.path.setAttribute('d', this.toString()) + + return SVG.parser.path.getBBox() + } + +}) +// Module for unit convertions +SVG.Number = function(value) { + + /* initialize defaults */ + this.value = 0 + this.unit = '' + + /* parse value */ + if (typeof value === 'number') { + /* ensure a valid numeric value */ + this.value = isNaN(value) ? 0 : !isFinite(value) ? (value < 0 ? -3.4e+38 : +3.4e+38) : value + + } else if (typeof value === 'string') { + var match = value.match(SVG.regex.unit) + + if (match) { + /* make value numeric */ + this.value = parseFloat(match[1]) + + /* normalize percent value */ + if (match[2] == '%') + this.value /= 100 + else if (match[2] == 's') + this.value *= 1000 + + /* store unit */ + this.unit = match[2] } - // Get morphed color at given position - , at: function(pos) { - /* make sure a destination is defined */ - if (!this.destination) return this - - /* normalise pos */ - pos = pos < 0 ? 0 : pos > 1 ? 1 : pos - - /* generate morphed color */ - return new SVG.Color({ - r: ~~(this.r + (this.destination.r - this.r) * pos) - , g: ~~(this.g + (this.destination.g - this.g) * pos) - , b: ~~(this.b + (this.destination.b - this.b) * pos) - }) + + } else { + if (value instanceof SVG.Number) { + this.value = value.value + this.unit = value.unit } - - }) - - // Testers - - // Test if given value is a color string - SVG.Color.test = function(color) { - color += '' - return SVG.regex.isHex.test(color) - || SVG.regex.isRgb.test(color) } - - // Test if given value is a rgb object - SVG.Color.isRgb = function(color) { - return color && typeof color.r == 'number' - && typeof color.g == 'number' - && typeof color.b == 'number' + +} + +SVG.extend(SVG.Number, { + // Stringalize + toString: function() { + return ( + this.unit == '%' ? + ~~(this.value * 1e8) / 1e6: + this.unit == 's' ? + this.value / 1e3 : + this.value + ) + this.unit } - - // Test if given value is a color - SVG.Color.isColor = function(color) { - return SVG.Color.isRgb(color) || SVG.Color.test(color) +, // Convert to primitive + valueOf: function() { + return this.value } + // Add number +, plus: function(number) { + this.value = this + new SVG.Number(number) - SVG.Array = function(array, fallback) { - array = (array || []).valueOf() - - /* if array is empty and fallback is provided, use fallback */ - if (array.length == 0 && fallback) - array = fallback.valueOf() - - /* parse array */ - this.value = this.parse(array) + return this + } + // Subtract number +, minus: function(number) { + return this.plus(-new SVG.Number(number)) + } + // Multiply number +, times: function(number) { + this.value = this * new SVG.Number(number) + + return this + } + // Divide number +, divide: function(number) { + this.value = this / new SVG.Number(number) + + return this + } + // Convert to different unit +, to: function(unit) { + if (typeof unit === 'string') + this.unit = unit + + return this + } + // Make number morphable +, morph: function(number) { + this.destination = new SVG.Number(number) + + return this + } + // Get morphed number at given position +, at: function(pos) { + /* make sure a destination is defined */ + if (!this.destination) return this + + /* generate new morphed number */ + return new SVG.Number(this.destination) + .minus(this) + .times(pos) + .plus(this) + } + +}) + +SVG.ViewBox = function(element) { + var x, y, width, height + , wm = 1 /* width multiplier */ + , hm = 1 /* height multiplier */ + , box = element.bbox() + , view = (element.attr('viewBox') || '').match(/-?[\d\.]+/g) + , we = element + , he = element + + /* get dimensions of current node */ + width = new SVG.Number(element.width()) + height = new SVG.Number(element.height()) + + /* find nearest non-percentual dimensions */ + while (width.unit == '%') { + wm *= width.value + width = new SVG.Number(we instanceof SVG.Doc ? we.parent().offsetWidth : we.parent().width()) + we = we.parent() + } + while (height.unit == '%') { + hm *= height.value + height = new SVG.Number(he instanceof SVG.Doc ? he.parent().offsetHeight : he.parent().height()) + he = he.parent() } - SVG.extend(SVG.Array, { - // Make array morphable - morph: function(array) { - this.destination = this.parse(array) + /* ensure defaults */ + this.x = box.x + this.y = box.y + this.width = width * wm + this.height = height * hm + this.zoom = 1 + + if (view) { + /* get width and height from viewbox */ + x = parseFloat(view[0]) + y = parseFloat(view[1]) + width = parseFloat(view[2]) + height = parseFloat(view[3]) + + /* calculate zoom accoring to viewbox */ + this.zoom = ((this.width / this.height) > (width / height)) ? + this.height / height : + this.width / width + + /* calculate real pixel dimensions on parent SVG.Doc element */ + this.x = x + this.y = y + this.width = width + this.height = height + + } - /* normalize length of arrays */ - if (this.value.length != this.destination.length) { - var lastValue = this.value[this.value.length - 1] - , lastDestination = this.destination[this.destination.length - 1] +} + +// +SVG.extend(SVG.ViewBox, { + // Parse viewbox to string + toString: function() { + return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height + } - while(this.value.length > this.destination.length) - this.destination.push(lastDestination) - while(this.value.length < this.destination.length) - this.value.push(lastValue) +}) + +SVG.Element = SVG.invent({ + // Initialize node + create: function(node) { + // Make stroke value accessible dynamically + this._stroke = SVG.defaults.attrs.stroke + + // Create circular reference + if (this.node = node) { + this.type = node.nodeName + this.node.instance = this + + // Store current attribute value + this._stroke = node.getAttribute('stroke') || this._stroke + } + } + + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + if (x != null) { + x = new SVG.Number(x) + x.value /= this.transform('scaleX') } - - return this + return this.attr('x', x) } - // Clean up any duplicate points - , settle: function() { - /* find all unique values */ - for (var i = 0, il = this.value.length, seen = []; i < il; i++) - if (seen.indexOf(this.value[i]) == -1) - seen.push(this.value[i]) - - /* set new value */ - return this.value = seen + // Move over y-axis + , y: function(y) { + if (y != null) { + y = new SVG.Number(y) + y.value /= this.transform('scaleY') + } + return this.attr('y', y) } - // Get morphed array at given position - , at: function(pos) { - /* make sure a destination is defined */ - if (!this.destination) return this - - /* generate morphed array */ - for (var i = 0, il = this.value.length, array = []; i < il; i++) - array.push(this.value[i] + (this.destination[i] - this.value[i]) * pos) - - return new SVG.Array(array) + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2) } - // Convert array to string - , toString: function() { - return this.value.join(' ') + // Move by center over y-axis + , cy: function(y) { + return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2) } - // Real value - , valueOf: function() { - return this.value + // Move element to given x and y values + , move: function(x, y) { + return this.x(x).y(y) } - // Parse whitespace separated string - , parse: function(array) { - array = array.valueOf() - - /* if already is an array, no need to parse it */ - if (Array.isArray(array)) return array - - return this.split(array) + // Move element by its center + , center: function(x, y) { + return this.cx(x).cy(y) } - // Strip unnecessary whitespace - , split: function(string) { - return string.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g,'').split(' ') + // Set width of element + , width: function(width) { + return this.attr('width', width) } - // Reverse array - , reverse: function() { - this.value.reverse() - + // 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 = proportionalSize(this.bbox(), width, height) + return this + .width(new SVG.Number(p.width)) + .height(new SVG.Number(p.height)) } - - }) + // Clone element + , clone: function() { + return assignNewId(this.node.cloneNode(true)) + } + // Remove element + , remove: function() { + if (this.parent()) + this.parent().removeElement(this) + + return this + } + // Replace element + , replace: function(element) { + this.after(element).remove() - SVG.PointArray = function(array, fallback) { - this.constructor.call(this, array, fallback || [[0,0]]) - } - - // Inherit from SVG.Array - SVG.PointArray.prototype = new SVG.Array - - SVG.extend(SVG.PointArray, { - // Convert array to string - toString: function() { - /* convert to a poly point string */ - for (var i = 0, il = this.value.length, array = []; i < il; i++) - array.push(this.value[i].join(',')) - - return array.join(' ') - } - // Convert array to line object - , toLine: function() { - return { - x1: this.value[0][0] - , y1: this.value[0][1] - , x2: this.value[1][0] - , y2: this.value[1][1] - } + return element } - // Get morphed array at given position - , at: function(pos) { - /* make sure a destination is defined */ - if (!this.destination) return this - - /* generate morphed point string */ - for (var i = 0, il = this.value.length, array = []; i < il; i++) - array.push([ - this.value[i][0] + (this.destination[i][0] - this.value[i][0]) * pos - , this.value[i][1] + (this.destination[i][1] - this.value[i][1]) * pos - ]) - - return new SVG.PointArray(array) + // Add element to given container and return self + , addTo: function(parent) { + return parent.put(this) } - // Parse point string - , parse: function(array) { - array = array.valueOf() - - /* if already is an array, no need to parse it */ - if (Array.isArray(array)) return array - - /* split points */ - array = this.split(array) - - /* parse points */ - for (var i = 0, il = array.length, p, points = []; i < il; i++) { - p = array[i].split(',') - points.push([parseFloat(p[0]), parseFloat(p[1])]) - } - - return points + // Add element to given container and return container + , putIn: function(parent) { + return parent.add(this) } - // Move point string - , move: function(x, y) { + // Get / set id + , id: function(id) { + return this.attr('id', id) + } + // Checks whether the given point inside the bounding box of the element + , inside: function(x, y) { var box = this.bbox() - - /* get relative offset */ - x -= box.x - y -= box.y - - /* move every point */ - if (!isNaN(x) && !isNaN(y)) - for (var i = this.value.length - 1; i >= 0; i--) - this.value[i] = [this.value[i][0] + x, this.value[i][1] + y] - - return this + + return x > box.x + && y > box.y + && x < box.x + box.width + && y < box.y + box.height } - // Resize poly string - , size: function(width, height) { - var i, box = this.bbox() - - /* recalculate position of all points according to new size */ - for (i = this.value.length - 1; i >= 0; i--) { - this.value[i][0] = ((this.value[i][0] - box.x) * width) / box.width + box.x - this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y - } - - return this + // Show element + , show: function() { + return this.style('display', '') } - // Get bounding box of points - , bbox: function() { - SVG.parser.poly.setAttribute('points', this.toString()) - - return SVG.parser.poly.getBBox() + // Hide element + , hide: function() { + return this.style('display', 'none') } - - }) - - SVG.PathArray = function(array, fallback) { - this.constructor.call(this, array, fallback || [['M', 0, 0]]) - } - - // Inherit from SVG.Array - SVG.PathArray.prototype = new SVG.Array - - SVG.extend(SVG.PathArray, { - // Convert array to string - toString: function() { - return arrayToString(this.value) - } - // Move path string - , move: function(x, y) { - /* get bounding box of current situation */ - var box = this.bbox() - - /* get relative offset */ - x -= box.x - y -= box.y - - if (!isNaN(x) && !isNaN(y)) { - /* move every point */ - for (var l, i = this.value.length - 1; i >= 0; i--) { - l = this.value[i][0] - - if (l == 'M' || l == 'L' || l == 'T') { - this.value[i][1] += x - this.value[i][2] += y - - } else if (l == 'H') { - this.value[i][1] += x - - } else if (l == 'V') { - this.value[i][1] += y - - } else if (l == 'C' || l == 'S' || l == 'Q') { - this.value[i][1] += x - this.value[i][2] += y - this.value[i][3] += x - this.value[i][4] += y - - if (l == 'C') { - this.value[i][5] += x - this.value[i][6] += y - } - - } else if (l == 'A') { - this.value[i][6] += x - this.value[i][7] += y - } - - } - } - - return this - } - // Resize path string - , size: function(width, height) { - /* get bounding box of current situation */ - var i, l, box = this.bbox() - - /* recalculate position of all points according to new size */ - for (i = this.value.length - 1; i >= 0; i--) { - l = this.value[i][0] - - if (l == 'M' || l == 'L' || l == 'T') { - this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x - this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y - - } else if (l == 'H') { - this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x - - } else if (l == 'V') { - this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y - - } else if (l == 'C' || l == 'S' || l == 'Q') { - this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x - this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y - this.value[i][3] = ((this.value[i][3] - box.x) * width) / box.width + box.x - this.value[i][4] = ((this.value[i][4] - box.y) * height) / box.height + box.y - - if (l == 'C') { - this.value[i][5] = ((this.value[i][5] - box.x) * width) / box.width + box.x - this.value[i][6] = ((this.value[i][6] - box.y) * height) / box.height + box.y - } - - } else if (l == 'A') { - /* resize radii */ - this.value[i][1] = (this.value[i][1] * width) / box.width - this.value[i][2] = (this.value[i][2] * height) / box.height - - /* move position values */ - this.value[i][6] = ((this.value[i][6] - box.x) * width) / box.width + box.x - this.value[i][7] = ((this.value[i][7] - box.y) * height) / box.height + box.y - } - - } - - return this - } - // Absolutize and parse path to array - , parse: function(array) { - /* if it's already is a patharray, no need to parse it */ - if (array instanceof SVG.PathArray) return array.valueOf() - - /* prepare for parsing */ - var i, il, x0, y0, x1, y1, x2, y2, s, seg, segs - , x = 0 - , y = 0 - - /* populate working path */ - SVG.parser.path.setAttribute('d', typeof array === 'string' ? array : arrayToString(array)) - - /* get segments */ - segs = SVG.parser.path.pathSegList - - for (i = 0, il = segs.numberOfItems; i < il; ++i) { - seg = segs.getItem(i) - s = seg.pathSegTypeAsLetter - - /* yes, this IS quite verbose but also about 30 times faster than .test() with a precompiled regex */ - if (s == 'M' || s == 'L' || s == 'H' || s == 'V' || s == 'C' || s == 'S' || s == 'Q' || s == 'T' || s == 'A') { - if ('x' in seg) x = seg.x - if ('y' in seg) y = seg.y - - } else { - if ('x1' in seg) x1 = x + seg.x1 - if ('x2' in seg) x2 = x + seg.x2 - if ('y1' in seg) y1 = y + seg.y1 - if ('y2' in seg) y2 = y + seg.y2 - if ('x' in seg) x += seg.x - if ('y' in seg) y += seg.y - - if (s == 'm') - segs.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(x, y), i) - else if (s == 'l') - segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(x, y), i) - else if (s == 'h') - segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(x), i) - else if (s == 'v') - segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(y), i) - else if (s == 'c') - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i) - else if (s == 's') - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i) - else if (s == 'q') - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i) - else if (s == 't') - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i) - else if (s == 'a') - segs.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i) - else if (s == 'z' || s == 'Z') { - x = x0 - y = y0 - } - } - - /* record the start of a subpath */ - if (s == 'M' || s == 'm') { - x0 = x - y0 = y - } - } - - /* build internal representation */ - array = [] - segs = SVG.parser.path.pathSegList - - for (i = 0, il = segs.numberOfItems; i < il; ++i) { - seg = segs.getItem(i) - s = seg.pathSegTypeAsLetter - x = [s] - - if (s == 'M' || s == 'L' || s == 'T') - x.push(seg.x, seg.y) - else if (s == 'H') - x.push(seg.x) - else if (s == 'V') - x.push(seg.y) - else if (s == 'C') - x.push(seg.x1, seg.y1, seg.x2, seg.y2, seg.x, seg.y) - else if (s == 'S') - x.push(seg.x2, seg.y2, seg.x, seg.y) - else if (s == 'Q') - x.push(seg.x1, seg.y1, seg.x, seg.y) - else if (s == 'A') - x.push(seg.r1, seg.r2, seg.angle, seg.largeArcFlag | 0, seg.sweepFlag | 0, seg.x, seg.y) - - /* store segment */ - array.push(x) - } - - return array - } - // Get bounding box of path - , bbox: function() { - SVG.parser.path.setAttribute('d', this.toString()) - - return SVG.parser.path.getBBox() - } - - }) - - SVG.Number = function(value) { - - /* initialize defaults */ - this.value = 0 - this.unit = '' - - /* parse value */ - if (typeof value === 'number') { - /* ensure a valid numeric value */ - this.value = isNaN(value) ? 0 : !isFinite(value) ? (value < 0 ? -3.4e+38 : +3.4e+38) : value - - } else if (typeof value === 'string') { - var match = value.match(SVG.regex.unit) - - if (match) { - /* make value numeric */ - this.value = parseFloat(match[1]) - - /* normalize percent value */ - if (match[2] == '%') - this.value /= 100 - else if (match[2] == 's') - this.value *= 1000 - - /* store unit */ - this.unit = match[2] - } - - } else { - if (value instanceof SVG.Number) { - this.value = value.value - this.unit = value.unit - } - } - - } - - SVG.extend(SVG.Number, { - // Stringalize - toString: function() { - return ( - this.unit == '%' ? - ~~(this.value * 1e8) / 1e6: - this.unit == 's' ? - this.value / 1e3 : - this.value - ) + this.unit - } - , // Convert to primitive - valueOf: function() { - return this.value - } - // Add number - , plus: function(number) { - this.value = this + new SVG.Number(number) - - return this - } - // Subtract number - , minus: function(number) { - return this.plus(-new SVG.Number(number)) - } - // Multiply number - , times: function(number) { - this.value = this * new SVG.Number(number) - - return this - } - // Divide number - , divide: function(number) { - this.value = this / new SVG.Number(number) - - return this - } - // Convert to different unit - , to: function(unit) { - if (typeof unit === 'string') - this.unit = unit - - return this - } - // Make number morphable - , morph: function(number) { - this.destination = new SVG.Number(number) - - return this - } - // Get morphed number at given position - , at: function(pos) { - /* make sure a destination is defined */ - if (!this.destination) return this - - /* generate new morphed number */ - return new SVG.Number(this.destination) - .minus(this) - .times(pos) - .plus(this) - } - - }) - - SVG.ViewBox = function(element) { - var x, y, width, height - , wm = 1 /* width multiplier */ - , hm = 1 /* height multiplier */ - , box = element.bbox() - , view = (element.attr('viewBox') || '').match(/-?[\d\.]+/g) - , we = element - , he = element - - /* get dimensions of current node */ - width = new SVG.Number(element.width()) - height = new SVG.Number(element.height()) - - /* find nearest non-percentual dimensions */ - while (width.unit == '%') { - wm *= width.value - width = new SVG.Number(we instanceof SVG.Doc ? we.parent().offsetWidth : we.parent().width()) - we = we.parent() - } - while (height.unit == '%') { - hm *= height.value - height = new SVG.Number(he instanceof SVG.Doc ? he.parent().offsetHeight : he.parent().height()) - he = he.parent() - } - - /* ensure defaults */ - this.x = box.x - this.y = box.y - this.width = width * wm - this.height = height * hm - this.zoom = 1 - - if (view) { - /* get width and height from viewbox */ - x = parseFloat(view[0]) - y = parseFloat(view[1]) - width = parseFloat(view[2]) - height = parseFloat(view[3]) - - /* calculate zoom accoring to viewbox */ - this.zoom = ((this.width / this.height) > (width / height)) ? - this.height / height : - this.width / width - - /* calculate real pixel dimensions on parent SVG.Doc element */ - this.x = x - this.y = y - this.width = width - this.height = height - - } - - } - - // - SVG.extend(SVG.ViewBox, { - // Parse viewbox to string - toString: function() { - return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height - } - - }) - - SVG.Element = SVG.invent({ - // Initialize node - create: function(node) { - // Make stroke value accessible dynamically - this._stroke = SVG.defaults.attrs.stroke - - // Create circular reference - if (this.node = node) { - this.type = node.nodeName - this.node.instance = this - - // Store current attribute value - this._stroke = node.getAttribute('stroke') || this._stroke - } - } - - // Add class methods - , extend: { - // Move over x-axis - x: function(x) { - if (x != null) { - x = new SVG.Number(x) - x.value /= this.transform('scaleX') - } - return this.attr('x', x) - } - // Move over y-axis - , y: function(y) { - if (y != null) { - y = new SVG.Number(y) - y.value /= this.transform('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 = proportionalSize(this.bbox(), width, height) - - return this - .width(new SVG.Number(p.width)) - .height(new SVG.Number(p.height)) - } - // Clone element - , clone: function() { - return assignNewId(this.node.cloneNode(true)) - } - // 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 / set id - , id: function(id) { - return this.attr('id', id) - } - // 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') - } - // Return array of classes on the node - , classes: function() { - var attr = this.attr('class') - - return attr == null ? [] : attr.trim().split(/\s+/) - } - // Return true if class exists on the node, false otherwise - , hasClass: function(name) { - return this.classes().indexOf(name) != -1 - } - // Add class to the node - , addClass: function(name) { - if (!this.hasClass(name)) { - var array = this.classes() - array.push(name) - this.attr('class', array.join(' ')) - } - - return this - } - // Remove class from the node - , removeClass: function(name) { - if (this.hasClass(name)) { - this.attr('class', this.classes().filter(function(c) { - return c != name - }).join(' ')) - } - - return this - } - // Toggle the presence of a class on the node - , toggleClass: function(name) { - return this.hasClass(name) ? this.removeClass(name) : this.addClass(name) - } - // Get referenced element form attribute value - , reference: function(attr) { - return SVG.get(this.attr(attr)) - } - // Returns the parent element instance - , parent: function(type) { - // Get parent element - var parent = SVG.adopt(this.node.parentNode) - - // If a specific type is given, find a parent with that class - if (type) - while (!(parent instanceof type)) - parent = SVG.adopt(parent.node.parentNode) - - return parent - } - // Get parent document - , doc: function(type) { - return this.parent(type || SVG.Doc) - } - // Returns the svg node to call native svg methods on it - , native: function() { - return this.node - } + // Is element visible? + , visible: function() { + return this.style('display') != 'none' } - }) - - - SVG.BBox = SVG.invent({ - // Initialize - create: function(element) { - var box - - // Initialize zero box - this.x = 0 - this.y = 0 - this.width = 0 - this.height = 0 - - // Get values if element is given - if (element) { - // Get current extracted transformations - var t = new SVG.Matrix(element).extract() - - // Find native bbox - if (element.node.getBBox) - box = element.node.getBBox() - // Mimic bbox - else - box = { - x: element.node.clientLeft - , y: element.node.clientTop - , width: element.node.clientWidth - , height: element.node.clientHeight - } - - // Include translations on x an y - this.x = box.x + t.x - this.y = box.y + t.y - - // Plain width and height - this.width = box.width * t.scaleX - this.height = box.height * t.scaleY - } - - // Add center, right and bottom - fullBox(this) - } - - // define Parent - , parent: SVG.Element - - // Constructor - , construct: { - // Get bounding box - bbox: function() { - return new SVG.BBox(this) - } - } - - }) - - SVG.RBox = SVG.invent({ - // Initialize - create: function(element) { - var e, zoom - , box = {} - - // Initialize zero box - this.x = 0 - this.y = 0 - this.width = 0 - this.height = 0 - - if (element) { - e = element.doc().parent() - zoom = element.doc().viewbox().zoom - - // Actual, native bounding box - box = element.node.getBoundingClientRect() - - // Get screen offset - this.x = box.left - this.y = box.top - - // Subtract parent offset - this.x -= e.offsetLeft - this.y -= e.offsetTop - - while (e = e.offsetParent) { - this.x -= e.offsetLeft - this.y -= e.offsetTop - } - - // Calculate cumulative zoom from svg documents - e = element - while (e.parent && (e = e.parent())) { - if (e.viewbox) { - zoom *= e.viewbox().zoom - this.x -= e.x() || 0 - this.y -= e.y() || 0 - } - } - } - - // Recalculate viewbox distortion - this.x /= zoom - this.y /= zoom - this.width = box.width /= zoom - this.height = box.height /= zoom - - // Offset by window scroll position, because getBoundingClientRect changes when window is scrolled - this.x += window.scrollX - this.y += window.scrollY - - // Add center, right and bottom - fullBox(this) - } - - // define Parent - , parent: SVG.Element - - // Constructor - , construct: { - // Get rect box - rbox: function() { - return new SVG.RBox(this) - } - } - - }) - - // Add universal merge method - ;[SVG.BBox, SVG.RBox].forEach(function(c) { - - SVG.extend(c, { - // Merge rect box with another, return a new instance - merge: function(box) { - var b = new c() - - // Merge box - b.x = Math.min(this.x, box.x) - b.y = Math.min(this.y, box.y) - b.width = Math.max(this.x + this.width, box.x + box.width) - b.x - b.height = Math.max(this.y + this.height, box.y + box.height) - b.y - - return fullBox(b) - } - - }) - - }) - - - SVG.Matrix = SVG.invent({ - // Initialize - create: function(source) { - var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0]) - - // Ensure source as object - source = source && source.node && source.node.getCTM ? - source.node.getCTM() : - typeof source === 'string' ? - arrayToMatrix(source.replace(/\s/g, '').split(',')) : - arguments.length == 6 ? - arrayToMatrix([].slice.call(arguments)) : - typeof source === 'object' ? - source : base - - // Merge source - for (i = abcdef.length - 1; i >= 0; i--) - this[abcdef[i]] = typeof source[abcdef[i]] === 'number' ? - source[abcdef[i]] : base[abcdef[i]] - - } - - // Add methods - , extend: { - // Extract individual transformations - extract: function() { - // Find transform points - var px = deltaTransformPoint(this, 0, 1) - , py = deltaTransformPoint(this, 1, 0) - , skewX = 180 / Math.PI * Math.atan2(px.y, px.x) - 90 - - return { - // Translation - x: this.e - , y: this.f - // Skew - , skewX: skewX - , skewY: 180 / Math.PI * Math.atan2(py.y, py.x) - // Scale - , scaleX: Math.sqrt(this.a * this.a + this.b * this.b) - , scaleY: Math.sqrt(this.c * this.c + this.d * this.d) - // Rotation - , rotation: skewX - } - } - // Multiply - , multiply: function(matrix) { - return new SVG.Matrix(this.native().multiply(matrix.native())) - } - // Inverse - , inverse: function() { - return new SVG.Matrix(this.native().inverse()) - } - // Translate - , translate: function(x, y) { - return new SVG.Matrix(this.native().translate(x || 0, y || 0)) - } - // Scale - , scale: function(x, y, cx, cy) { - // Support universal scale - if (arguments.length == 1 || arguments.length == 3) - y = x - if (arguments.length == 3) { - cy = cx - cx = y - } - - return this - .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0)) - .multiply(new SVG.Matrix(x, 0, 0, y, 0, 0)) - .multiply(new SVG.Matrix(1, 0, 0, 1, -cx || 0, -cy || 0)) - } - // Rotate - , rotate: function(d, cx, cy) { - // Convert degrees to radians - d = SVG.utils.radians(d) - - return this - .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0)) - .multiply(new SVG.Matrix(Math.cos(d), Math.sin(d), -Math.sin(d), Math.cos(d), 0, 0)) - .multiply(new SVG.Matrix(1, 0, 0, 1, -cx || 0, -cy || 0)) - } - // Flip - , flip: function(a) { - return new SVG.Matrix(this.native()['flip' + a.toUpperCase()]()) - } - // Skew - , skew: function(x, y) { - return new SVG.Matrix(this.native().skewX(x || 0).skewY(y || 0)) - } - // Convert this to SVGMatrix - , native: function() { - // Create new matrix - var i, matrix = SVG.parser.draw.node.createSVGMatrix() - - // Update with current values - for (i = abcdef.length - 1; i >= 0; i--) - matrix[abcdef[i]] = this[abcdef[i]] - - return matrix - } - // Convert array to string - , toString: function() { - return 'matrix(' + [this.a, this.b, this.c, this.d, this.e, this.f].join() + ')' - } - } - - // Define parent - , parent: SVG.Element - - // Add parent method - , construct: { - // Get current matrix - ctm: function() { - return new SVG.Matrix(this) - } - - } - - }) - - SVG.extend(SVG.Element, { - // Set svg element attribute - attr: function(a, v, n) { - // Act as full getter - if (a == null) { - // Get an object of attributes - a = {} - v = this.node.attributes - for (n = v.length - 1; n >= 0; n--) - a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue - - return a - - } else if (typeof a == 'object') { - // Apply every attribute individually if an object is passed - for (v in a) this.attr(v, a[v]) - - } else if (v === null) { - // Remove value - this.node.removeAttribute(a) - - } else if (v == null) { - // Act as a getter if the first and only argument is not an object - v = this.node.getAttribute(a) - return v == null ? - SVG.defaults.attrs[a] : - SVG.regex.isNumber.test(v) ? - parseFloat(v) : v - - } else { - // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 - if (a == 'stroke-width') - this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null) - else if (a == 'stroke') - this._stroke = v - - // Convert image fill and stroke to patterns - if (a == 'fill' || a == 'stroke') { - if (SVG.regex.isImage.test(v)) - v = this.doc().defs().image(v, 0, 0) - - if (v instanceof SVG.Image) - v = this.doc().defs().pattern(0, 0, function() { - this.add(v) - }) - } - - // Ensure correct numeric values (also accepts NaN and Infinity) - if (typeof v === 'number') - v = new SVG.Number(v) - - // Ensure full hex color - else if (SVG.Color.isColor(v)) - v = new SVG.Color(v) - - // Parse array values - else if (Array.isArray(v)) - v = new SVG.Array(v) - - // If the passed attribute is leading... - if (a == 'leading') { - // ... call the leading method instead - if (this.leading) - this.leading(v) - } else { - // Set given attribute on node - typeof n === 'string' ? - this.node.setAttributeNS(n, a, v.toString()) : - this.node.setAttribute(a, v.toString()) - } - - // Rebuild if required - if (this.rebuild && (a == 'font-size' || a == 'x')) - this.rebuild(a, v) - } - - return this - } - }) - - SVG.extend(SVG.Element, { - // Add transformations - transform: function(o) { - // Full getter - if (o == null) - return this.ctm().extract() - - // Singular getter - else if (typeof o === 'string') - return this.ctm().extract()[o] - - // Get current matrix - var matrix = new SVG.Matrix(this) - - // Act on matrix - if (o.a != null) - matrix = matrix.multiply(new SVG.Matrix(o)) - - // Act on rotate - else if (o.rotation) - matrix = matrix.rotate( - o.rotation - , o.cx == null ? this.bbox().cx : o.cx - , o.cy == null ? this.bbox().cy : o.cy - ) - - // Act on scale - else if (o.scale != null || o.scaleX != null || o.scaleY != null) - matrix = matrix.scale( - o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1 - , o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1 - , o.cx != null ? o.cx : this.bbox().x - , o.cy != null ? o.cy : this.bbox().y - ) - - // Act on skew - else if (o.skewX || o.skewY) - matrix = matrix.skew(o.skewX, o.skewY) - - // Act on translate - else if (o.x || o.y) - matrix = matrix.translate(o.x, o.y) - - return this.attr('transform', matrix) - } - // Reset all transformations - , untransform: function() { - return this.attr('transform', null) - } - - }) - - SVG.extend(SVG.Element, { - // Dynamic style generator - style: function(s, v) { - if (arguments.length == 0) { - /* get full style */ - return this.node.style.cssText || '' - - } else if (arguments.length < 2) { - /* apply every style individually if an object is passed */ - if (typeof s == 'object') { - for (v in s) this.style(v, s[v]) - - } else if (SVG.regex.isCss.test(s)) { - /* parse css string */ - s = s.split(';') - - /* apply every definition individually */ - for (var i = 0; i < s.length; i++) { - v = s[i].split(':') - this.style(v[0].replace(/\s+/g, ''), v[1]) - } - } else { - /* act as a getter if the first and only argument is not an object */ - return this.node.style[camelCase(s)] - } - - } else { - this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v - } - - return this - } - }) - - SVG.Parent = SVG.invent({ - // Initialize node - create: function(element) { - this.constructor.call(this, element) - } - - // Inherit from - , inherit: SVG.Element - - // Add class methods - , extend: { - // Returns all child elements - children: function() { - return SVG.utils.map(this.node.childNodes, function(node) { - return SVG.adopt(node) - }) - } - // 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 - - // Add element references - this.node.insertBefore(element.node, this.node.childNodes[i] || null) - } - - 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) - } - - return this - } - // Remove a child element at a position - , removeElement: function(element) { - this.node.removeChild(element.node) - - return this - } - // Remove all elements in this container - , clear: function() { - // Remove children - while(this.node.hasChildNodes()) - this.node.removeChild(this.node.lastChild) - - // Remove defs reference - delete this._defs - - return this - } - , // Get defs - defs: function() { - return this.doc().defs() - } - } - - }) - - - 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) - } - } - - }) - - SVG.extend(SVG.Parent, SVG.Text, { - // Import svg SVG data - svg: function(svg) { - // create temporary div to receive svg content - var element = document.createElement('div') - - if (svg) { - // strip away newlines and properly close tags - svg = svg - .replace(/\n/, '') - .replace(/<(\w+)([^<]+?)\/>/g, '<$1$2>') - - // ensure SVG wrapper for correct element type casting - element.innerHTML = '' + svg + '' - - // transplant content from well to target - for (var i = element.firstChild.childNodes.length - 1; i >= 0; i--) - if (element.firstChild.childNodes[i].nodeType == 1) - this.node.appendChild(element.firstChild.childNodes[i]) - - return this - - } else { - // clone element and its contents - var clone = this.node.cloneNode(true) - - // add target to clone - element.appendChild(clone) - - return element.innerHTML - } - } - }) - - SVG.FX = SVG.invent({ - // Initialize FX object - create: function(element) { - /* store target element */ - this.target = element - } - - // Add class methods - , extend: { - // Add animation parameters and start animation - animate: function(d, ease, delay) { - var akeys, tkeys, skeys, key - , element = this.target - , fx = this - - /* dissect object if one is passed */ - if (typeof d == 'object') { - delay = d.delay - ease = d.ease - d = d.duration - } - - /* ensure default duration and easing */ - d = d == '=' ? d : d == null ? 1000 : new SVG.Number(d).valueOf() - ease = ease || '<>' - - /* process values */ - fx.to = function(pos) { - var i - - /* normalise pos */ - pos = pos < 0 ? 0 : pos > 1 ? 1 : pos - - /* collect attribute keys */ - if (akeys == null) { - akeys = [] - for (key in fx.attrs) - akeys.push(key) - - /* make sure morphable elements are scaled, translated and morphed all together */ - if (element.morphArray && (fx._plot || akeys.indexOf('points') > -1)) { - /* get destination */ - var box - , p = new element.morphArray(fx._plot || fx.attrs.points || element.array) - - /* add size */ - if (fx._size) p.size(fx._size.width.to, fx._size.height.to) - - /* add movement */ - box = p.bbox() - if (fx._x) p.move(fx._x.to, box.y) - else if (fx._cx) p.move(fx._cx.to - box.width / 2, box.y) - - box = p.bbox() - if (fx._y) p.move(box.x, fx._y.to) - else if (fx._cy) p.move(box.x, fx._cy.to - box.height / 2) - - /* delete element oriented changes */ - delete fx._x - delete fx._y - delete fx._cx - delete fx._cy - delete fx._size - - fx._plot = element.array.morph(p) - } - } - - /* collect transformation keys */ - if (tkeys == null) { - tkeys = [] - for (key in fx.trans) - tkeys.push(key) - } - - /* collect style keys */ - if (skeys == null) { - skeys = [] - for (key in fx.styles) - skeys.push(key) - } - - /* apply easing */ - pos = ease == '<>' ? - (-Math.cos(pos * Math.PI) / 2) + 0.5 : - ease == '>' ? - Math.sin(pos * Math.PI / 2) : - ease == '<' ? - -Math.cos(pos * Math.PI / 2) + 1 : - ease == '-' ? - pos : - typeof ease == 'function' ? - ease(pos) : - pos - - /* run plot function */ - if (fx._plot) { - element.plot(fx._plot.at(pos)) - - } else { - /* run all x-position properties */ - if (fx._x) - element.x(fx._x.at(pos)) - else if (fx._cx) - element.cx(fx._cx.at(pos)) - - /* run all y-position properties */ - if (fx._y) - element.y(fx._y.at(pos)) - else if (fx._cy) - element.cy(fx._cy.at(pos)) - - /* run all size properties */ - if (fx._size) - element.size(fx._size.width.at(pos), fx._size.height.at(pos)) - } - - /* run all viewbox properties */ - if (fx._viewbox) - element.viewbox( - fx._viewbox.x.at(pos) - , fx._viewbox.y.at(pos) - , fx._viewbox.width.at(pos) - , fx._viewbox.height.at(pos) - ) - - /* run leading property */ - if (fx._leading) - element.leading(fx._leading.at(pos)) - - /* animate attributes */ - for (i = akeys.length - 1; i >= 0; i--) - element.attr(akeys[i], at(fx.attrs[akeys[i]], pos)) - - /* animate transformations */ - for (i = tkeys.length - 1; i >= 0; i--) - element.transform(tkeys[i], at(fx.trans[tkeys[i]], pos)) - - /* animate styles */ - for (i = skeys.length - 1; i >= 0; i--) - element.style(skeys[i], at(fx.styles[skeys[i]], pos)) - - /* callback for each keyframe */ - if (fx._during) - fx._during.call(element, pos, function(from, to) { - return at({ from: from, to: to }, pos) - }) - } - - if (typeof d === 'number') { - /* delay animation */ - this.timeout = setTimeout(function() { - var start = new Date().getTime() - - /* initialize situation object */ - fx.situation = { - interval: 1000 / 60 - , start: start - , play: true - , finish: start + d - , duration: d - } - - /* render function */ - fx.render = function() { - - if (fx.situation.play === true) { - // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby. - var time = new Date().getTime() - , pos = time > fx.situation.finish ? 1 : (time - fx.situation.start) / d - - /* process values */ - fx.to(pos) - - /* finish off animation */ - if (time > fx.situation.finish) { - if (fx._plot) - element.plot(new SVG.PointArray(fx._plot.destination).settle()) - - if (fx._loop === true || (typeof fx._loop == 'number' && fx._loop > 1)) { - if (typeof fx._loop == 'number') - --fx._loop - fx.animate(d, ease, delay) - } else { - fx._after ? fx._after.apply(element, [fx]) : fx.stop() - } - - } else { - requestAnimFrame(fx.render) - } - } else { - requestAnimFrame(fx.render) - } - - } - - /* start animation */ - fx.render() - - }, new SVG.Number(delay).valueOf()) - } - - return this - } - // Get bounding box of target element - , bbox: function() { - return this.target.bbox() - } - // Add animatable attributes - , attr: function(a, v) { - if (typeof a == 'object') { - for (var key in a) - this.attr(key, a[key]) - - } else { - var from = this.target.attr(a) - - this.attrs[a] = SVG.Color.isColor(from) ? - new SVG.Color(from).morph(v) : - SVG.regex.unit.test(from) ? - new SVG.Number(from).morph(v) : - { from: from, to: v } - } - - return this - } - // Add animatable transformations - , transform: function(o, v) { - // if (arguments.length == 1) { - // /* parse matrix string */ - // o = parseMatrix(o) - - // /* dlete matrixstring from object */ - // delete o.matrix - - // /* store matrix values */ - // for (v in o) - // this.trans[v] = { from: this.target.trans[v], to: o[v] } - - // } else { - // /* apply transformations as object if key value arguments are given*/ - // var transform = {} - // transform[o] = v - - // this.transform(transform) - // } - - // return this - } - // Add animatable styles - , style: function(s, v) { - if (typeof s == 'object') - for (var key in s) - this.style(key, s[key]) - - else - this.styles[s] = { from: this.target.style(s), to: v } - - return this - } - // Animatable x-axis - , x: function(x) { - this._x = new SVG.Number(this.target.x()).morph(x) - - return this - } - // Animatable y-axis - , y: function(y) { - this._y = new SVG.Number(this.target.y()).morph(y) - - return this - } - // Animatable center x-axis - , cx: function(x) { - this._cx = new SVG.Number(this.target.cx()).morph(x) - - return this - } - // Animatable center y-axis - , cy: function(y) { - this._cy = new SVG.Number(this.target.cy()).morph(y) - - return this - } - // Add animatable move - , move: function(x, y) { - return this.x(x).y(y) - } - // Add animatable center - , center: function(x, y) { - return this.cx(x).cy(y) - } - // Add animatable size - , size: function(width, height) { - if (this.target instanceof SVG.Text) { - /* animate font size for Text elements */ - this.attr('font-size', width) - - } else { - /* animate bbox based size for all other elements */ - var box = this.target.bbox() - - this._size = { - width: new SVG.Number(box.width).morph(width) - , height: new SVG.Number(box.height).morph(height) - } - } - - return this - } - // Add animatable plot - , plot: function(p) { - this._plot = p - - return this - } - // Add leading method - , leading: function(value) { - if (this.target._leading) - this._leading = new SVG.Number(this.target._leading).morph(value) - - return this - } - // Add animatable viewbox - , viewbox: function(x, y, width, height) { - if (this.target instanceof SVG.Container) { - var box = this.target.viewbox() - - this._viewbox = { - x: new SVG.Number(box.x).morph(x) - , y: new SVG.Number(box.y).morph(y) - , width: new SVG.Number(box.width).morph(width) - , height: new SVG.Number(box.height).morph(height) - } - } - - return this - } - // Add animateable gradient update - , update: function(o) { - if (this.target instanceof SVG.Stop) { - if (o.opacity != null) this.attr('stop-opacity', o.opacity) - if (o.color != null) this.attr('stop-color', o.color) - if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) - } - - return this - } - // Add callback for each keyframe - , during: function(during) { - this._during = during - - return this - } - // Callback after animation - , after: function(after) { - this._after = after - - return this - } - // Make loopable - , loop: function(times) { - this._loop = times || true - - return this - } - // Stop running animation - , stop: function(fulfill) { - /* fulfill animation */ - if (fulfill === true) { - - this.animate(0) - - if (this._after) - this._after.apply(this.target, [this]) - - } else { - /* stop current animation */ - clearTimeout(this.timeout) - - /* reset storage for properties that need animation */ - this.attrs = {} - this.trans = {} - this.styles = {} - this.situation = {} - - /* delete destinations */ - delete this._x - delete this._y - delete this._cx - delete this._cy - delete this._size - delete this._plot - delete this._loop - delete this._after - delete this._during - delete this._leading - delete this._viewbox - } - - return this - } - // Pause running animation - , pause: function() { - if (this.situation.play === true) { - this.situation.play = false - this.situation.pause = new Date().getTime() - } - - return this - } - // Play running animation - , play: function() { - if (this.situation.play === false) { - var pause = new Date().getTime() - this.situation.pause - - this.situation.finish += pause - this.situation.start += pause - this.situation.play = true - } - - return this - } - - } - - // Define parent class - , parent: SVG.Element - - // Add method to parent elements - , construct: { - // Get fx module or create a new one, then animate with given duration and ease - animate: function(d, ease, delay) { - return (this.fx || (this.fx = new SVG.FX(this))).stop().animate(d, ease, delay) - } - // Stop current animation; this is an alias to the fx instance - , stop: function(fulfill) { - if (this.fx) - this.fx.stop(fulfill) - - return this - } - // Pause current animation - , pause: function() { - if (this.fx) - this.fx.pause() - - return this + // Return id on string conversion + , toString: function() { + return this.attr('id') + } + // Return array of classes on the node + , classes: function() { + var attr = this.attr('class') + + return attr == null ? [] : attr.trim().split(/\s+/) + } + // Return true if class exists on the node, false otherwise + , hasClass: function(name) { + return this.classes().indexOf(name) != -1 + } + // Add class to the node + , addClass: function(name) { + if (!this.hasClass(name)) { + var array = this.classes() + array.push(name) + this.attr('class', array.join(' ')) } - // Play paused current animation - , play: function() { - if (this.fx) - this.fx.play() - - return this + + return this + } + // Remove class from the node + , removeClass: function(name) { + if (this.hasClass(name)) { + this.attr('class', this.classes().filter(function(c) { + return c != name + }).join(' ')) } + + return this + } + // Toggle the presence of a class on the node + , toggleClass: function(name) { + return this.hasClass(name) ? this.removeClass(name) : this.addClass(name) + } + // Get referenced element form attribute value + , reference: function(attr) { + return SVG.get(this.attr(attr)) + } + // Returns the parent element instance + , parent: function(type) { + // Get parent element + var parent = SVG.adopt(this.node.parentNode) + + // If a specific type is given, find a parent with that class + if (type) + while (!(parent instanceof type)) + parent = SVG.adopt(parent.node.parentNode) + + return parent + } + // Get parent document + , doc: function(type) { + return this.parent(type || SVG.Doc) + } + // Returns the svg node to call native svg methods on it + , native: function() { + return this.node + } + } +}) + +SVG.BBox = SVG.invent({ + // Initialize + create: function(element) { + var box + + // Initialize zero box + this.x = 0 + this.y = 0 + this.width = 0 + this.height = 0 + + // Get values if element is given + if (element) { + // Get current extracted transformations + var t = new SVG.Matrix(element).extract() + + // Find native bbox + if (element.node.getBBox) + box = element.node.getBBox() + // Mimic bbox + else + box = { + x: element.node.clientLeft + , y: element.node.clientTop + , width: element.node.clientWidth + , height: element.node.clientHeight + } + // Include translations on x an y + this.x = box.x + t.x + this.y = box.y + t.y + + // Plain width and height + this.width = box.width * t.scaleX + this.height = box.height * t.scaleY } - }) + // Add center, right and bottom + fullBox(this) + } + + // define Parent +, parent: SVG.Element - SVG.extend(SVG.Element, SVG.FX, { - // Relative move over x axis - dx: function(x) { - return this.x((this.target || this).x() + x) + // Constructor +, construct: { + // Get bounding box + bbox: function() { + return new SVG.BBox(this) } - // Relative move over y axis - , dy: function(y) { - return this.y((this.target || this).y() + y) + } + +}) + +SVG.RBox = SVG.invent({ + // Initialize + create: function(element) { + var e, zoom + , box = {} + + // Initialize zero box + this.x = 0 + this.y = 0 + this.width = 0 + this.height = 0 + + if (element) { + e = element.doc().parent() + zoom = element.doc().viewbox().zoom + + // Actual, native bounding box + box = element.node.getBoundingClientRect() + + // Get screen offset + this.x = box.left + this.y = box.top + + // Subtract parent offset + this.x -= e.offsetLeft + this.y -= e.offsetTop + + while (e = e.offsetParent) { + this.x -= e.offsetLeft + this.y -= e.offsetTop + } + + // Calculate cumulative zoom from svg documents + e = element + while (e.parent && (e = e.parent())) { + if (e.viewbox) { + zoom *= e.viewbox().zoom + this.x -= e.x() || 0 + this.y -= e.y() || 0 + } + } } - // Relative move over x and y axes - , dmove: function(x, y) { - return this.dx(x).dy(y) + + // Recalculate viewbox distortion + this.x /= zoom + this.y /= zoom + this.width = box.width /= zoom + this.height = box.height /= zoom + + // Offset by window scroll position, because getBoundingClientRect changes when window is scrolled + this.x += window.scrollX + this.y += window.scrollY + + // Add center, right and bottom + fullBox(this) + } + + // define Parent +, parent: SVG.Element + + // Constructor +, construct: { + // Get rect box + rbox: function() { + return new SVG.RBox(this) } - - }) + } - ;[ 'click' - , 'dblclick' - , 'mousedown' - , 'mouseup' - , 'mouseover' - , 'mouseout' - , 'mousemove' - , 'mouseenter' - , 'mouseleave' - , 'touchstart' - , 'touchmove' - , 'touchleave' - , 'touchend' - , 'touchcancel' ].forEach(function(event) { +}) + +// Add universal merge method +;[SVG.BBox, SVG.RBox].forEach(function(c) { + + SVG.extend(c, { + // Merge rect box with another, return a new instance + merge: function(box) { + var b = new c() + + // Merge box + b.x = Math.min(this.x, box.x) + b.y = Math.min(this.y, box.y) + b.width = Math.max(this.x + this.width, box.x + box.width) - b.x + b.height = Math.max(this.y + this.height, box.y + box.height) - b.y + + return fullBox(b) + } + + }) + +}) + +SVG.Matrix = SVG.invent({ + // Initialize + create: function(source) { + var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0]) + + // Ensure source as object + source = source && source.node && source.node.getCTM ? + source.node.getCTM() : + typeof source === 'string' ? + arrayToMatrix(source.replace(/\s/g, '').split(',')) : + arguments.length == 6 ? + arrayToMatrix([].slice.call(arguments)) : + typeof source === 'object' ? + source : base + + // Merge source + for (i = abcdef.length - 1; i >= 0; i--) + this[abcdef[i]] = typeof source[abcdef[i]] === 'number' ? + source[abcdef[i]] : base[abcdef[i]] + + } + + // Add methods +, extend: { + // Extract individual transformations + extract: function() { + // Find transform points + var px = deltaTransformPoint(this, 0, 1) + , py = deltaTransformPoint(this, 1, 0) + , skewX = 180 / Math.PI * Math.atan2(px.y, px.x) - 90 + + return { + // Translation + x: this.e + , y: this.f + // Skew + , skewX: skewX + , skewY: 180 / Math.PI * Math.atan2(py.y, py.x) + // Scale + , scaleX: Math.sqrt(this.a * this.a + this.b * this.b) + , scaleY: Math.sqrt(this.c * this.c + this.d * this.d) + // Rotation + , rotation: skewX + } + } + // Multiply + , multiply: function(matrix) { + return new SVG.Matrix(this.native().multiply(matrix.native())) + } + // Inverse + , inverse: function() { + return new SVG.Matrix(this.native().inverse()) + } + // Translate + , translate: function(x, y) { + return new SVG.Matrix(this.native().translate(x || 0, y || 0)) + } + // Scale + , scale: function(x, y, cx, cy) { + // Support universal scale + if (arguments.length == 1 || arguments.length == 3) + y = x + if (arguments.length == 3) { + cy = cx + cx = y + } + + return this + .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0)) + .multiply(new SVG.Matrix(x, 0, 0, y, 0, 0)) + .multiply(new SVG.Matrix(1, 0, 0, 1, -cx || 0, -cy || 0)) + } + // Rotate + , rotate: function(d, cx, cy) { + // Convert degrees to radians + d = SVG.utils.radians(d) + + return this + .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0)) + .multiply(new SVG.Matrix(Math.cos(d), Math.sin(d), -Math.sin(d), Math.cos(d), 0, 0)) + .multiply(new SVG.Matrix(1, 0, 0, 1, -cx || 0, -cy || 0)) + } + // Flip + , flip: function(a) { + return new SVG.Matrix(this.native()['flip' + a.toUpperCase()]()) + } + // Skew + , skew: function(x, y, cx, cy) { + // IMPLEMENT SKEW CENTER POINT + return new SVG.Matrix(this.native().skewX(x || 0).skewY(y || 0)) + } + // Convert this to SVGMatrix + , native: function() { + // Create new matrix + var i, matrix = SVG.parser.draw.node.createSVGMatrix() + + // Update with current values + for (i = abcdef.length - 1; i >= 0; i--) + matrix[abcdef[i]] = this[abcdef[i]] + + return matrix + } + // Convert array to string + , toString: function() { + return 'matrix(' + [this.a, this.b, this.c, this.d, this.e, this.f].join() + ')' + } + } + + // Define parent +, parent: SVG.Element + + // Add parent method +, construct: { + // Get current matrix + ctm: function() { + return new SVG.Matrix(this) + } + + } + +}) +SVG.extend(SVG.Element, { + // Set svg element attribute + attr: function(a, v, n) { + // Act as full getter + if (a == null) { + // Get an object of attributes + a = {} + v = this.node.attributes + for (n = v.length - 1; n >= 0; n--) + a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue + + return a + + } else if (typeof a == 'object') { + // Apply every attribute individually if an object is passed + for (v in a) this.attr(v, a[v]) + + } else if (v === null) { + // Remove value + this.node.removeAttribute(a) + + } else if (v == null) { + // Act as a getter if the first and only argument is not an object + v = this.node.getAttribute(a) + return v == null ? + SVG.defaults.attrs[a] : + SVG.regex.isNumber.test(v) ? + parseFloat(v) : v - /* add event to SVG.Element */ - SVG.Element.prototype[event] = function(f) { - var self = this + } else { + // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 + if (a == 'stroke-width') + this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null) + else if (a == 'stroke') + this._stroke = v + + // Convert image fill and stroke to patterns + if (a == 'fill' || a == 'stroke') { + if (SVG.regex.isImage.test(v)) + v = this.doc().defs().image(v, 0, 0) + + if (v instanceof SVG.Image) + v = this.doc().defs().pattern(0, 0, function() { + this.add(v) + }) + } - /* bind event to element rather than element node */ - this.node['on' + event] = typeof f == 'function' ? - function() { return f.apply(self, arguments) } : null + // Ensure correct numeric values (also accepts NaN and Infinity) + if (typeof v === 'number') + v = new SVG.Number(v) + + // Ensure full hex color + else if (SVG.Color.isColor(v)) + v = new SVG.Color(v) - return this + // Parse array values + else if (Array.isArray(v)) + v = new SVG.Array(v) + + // If the passed attribute is leading... + if (a == 'leading') { + // ... call the leading method instead + if (this.leading) + this.leading(v) + } else { + // Set given attribute on node + typeof n === 'string' ? + this.node.setAttributeNS(n, a, v.toString()) : + this.node.setAttribute(a, v.toString()) + } + + // Rebuild if required + if (this.rebuild && (a == 'font-size' || a == 'x')) + this.rebuild(a, v) } - }) - - // Initialize events and listeners stack - SVG.events = {} - SVG.listeners = {} - - // Event constructor - SVG.registerEvent = function(event) { - if (!SVG.events[event]) - SVG.events[event] = new CustomEvent(event) + return this } - - // Add event binder in the SVG namespace - SVG.on = function(node, event, listener) { - var l = listener.bind(node.instance || node) - SVG.listeners[listener] = l - node.addEventListener(event, l, false) +}) +SVG.extend(SVG.Element, { + // Add transformations + transform: function(o) { + // Full getter + if (o == null) + return this.ctm().extract() + + // Singular getter + else if (typeof o === 'string') + return this.ctm().extract()[o] + + // Get current matrix + var matrix = new SVG.Matrix(this) + + // Act on matrix + if (o.a != null) + matrix = matrix.multiply(new SVG.Matrix(o)) + + // Act on rotate + else if (o.rotation) + matrix = matrix.rotate( + o.rotation + , o.cx == null ? this.bbox().cx : o.cx + , o.cy == null ? this.bbox().cy : o.cy + ) + + // Act on scale + else if (o.scale != null || o.scaleX != null || o.scaleY != null) + matrix = matrix.scale( + o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1 + , o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1 + , o.cx != null ? o.cx : this.bbox().x + , o.cy != null ? o.cy : this.bbox().y + ) + + // Act on skew + else if (o.skewX || o.skewY) + matrix = matrix.skew(o.skewX, o.skewY) + + // Act on translate + else if (o.x || o.y) + matrix = matrix.translate(o.x, o.y) + + return this.attr('transform', matrix) + } + // Reset all transformations +, untransform: function() { + return this.attr('transform', null) + } + +}) +SVG.extend(SVG.Element, { + // Dynamic style generator + style: function(s, v) { + if (arguments.length == 0) { + /* get full style */ + return this.node.style.cssText || '' + + } else if (arguments.length < 2) { + /* apply every style individually if an object is passed */ + if (typeof s == 'object') { + for (v in s) this.style(v, s[v]) + + } else if (SVG.regex.isCss.test(s)) { + /* parse css string */ + s = s.split(';') + + /* apply every definition individually */ + for (var i = 0; i < s.length; i++) { + v = s[i].split(':') + this.style(v[0].replace(/\s+/g, ''), v[1]) + } + } else { + /* act as a getter if the first and only argument is not an object */ + return this.node.style[camelCase(s)] + } + + } else { + this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v + } + + return this } - - // Add event unbinder in the SVG namespace - SVG.off = function(node, event, listener) { - node.removeEventListener(event, SVG.listeners[listener], false) - delete SVG.listeners[listener] +}) +SVG.Parent = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) } - - // - SVG.extend(SVG.Element, { - // Bind given event to listener - on: function(event, listener) { - SVG.on(this.node, event, listener) + + // Inherit from +, inherit: SVG.Element + + // Add class methods +, extend: { + // Returns all child elements + children: function() { + return SVG.utils.map(this.node.childNodes, function(node) { + return SVG.adopt(node) + }) + } + // 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 + + // Add element references + this.node.insertBefore(element.node, this.node.childNodes[i] || null) + } + + 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) + } + return this } - // Unbind event from listener - , off: function(event, listener) { - SVG.off(this.node, event, listener) + // Remove a child element at a position + , removeElement: function(element) { + this.node.removeChild(element.node) return this } - // Fire given event - , fire: function(event, data) { - // Add detail data to event - SVG.events[event].detail = data + // Remove all elements in this container + , clear: function() { + // Remove children + while(this.node.hasChildNodes()) + this.node.removeChild(this.node.lastChild) - // Dispatch event - this.node.dispatchEvent(SVG.events[event]) + // Remove defs reference + delete this._defs + + return this + } + , // Get defs + defs: function() { + return this.doc().defs() + } + } - // Remove detail - delete SVG.events[event].detail +}) + +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) + } + } +}) +SVG.extend(SVG.Parent, SVG.Text, { + // Import svg SVG data + svg: function(svg) { + // create temporary div to receive svg content + var element = document.createElement('div') + + if (svg) { + // strip away newlines and properly close tags + svg = svg + .replace(/\n/, '') + .replace(/<(\w+)([^<]+?)\/>/g, '<$1$2>') + + // ensure SVG wrapper for correct element type casting + element.innerHTML = '' + svg + '' + + // transplant content from well to target + for (var i = element.firstChild.childNodes.length - 1; i >= 0; i--) + if (element.firstChild.childNodes[i].nodeType == 1) + this.node.appendChild(element.firstChild.childNodes[i]) + return this + + } else { + // clone element and its contents + var clone = this.node.cloneNode(true) + + // add target to clone + element.appendChild(clone) + + return element.innerHTML } - }) + } +}) +SVG.FX = SVG.invent({ + // Initialize FX object + create: function(element) { + /* store target element */ + this.target = element + } - SVG.Defs = SVG.invent({ - // Initialize node - create: 'defs' - - // Inherit from - , inherit: SVG.Container - - }) + // Add class methods +, extend: { + // Add animation parameters and start animation + animate: function(d, ease, delay) { + var akeys, tkeys, skeys, key + , element = this.target + , fx = this + + /* dissect object if one is passed */ + if (typeof d == 'object') { + delay = d.delay + ease = d.ease + d = d.duration + } + + /* ensure default duration and easing */ + d = d == '=' ? d : d == null ? 1000 : new SVG.Number(d).valueOf() + ease = ease || '<>' + + /* process values */ + fx.to = function(pos) { + var i + + /* normalise pos */ + pos = pos < 0 ? 0 : pos > 1 ? 1 : pos + + /* collect attribute keys */ + if (akeys == null) { + akeys = [] + for (key in fx.attrs) + akeys.push(key) + + /* make sure morphable elements are scaled, translated and morphed all together */ + if (element.morphArray && (fx._plot || akeys.indexOf('points') > -1)) { + /* get destination */ + var box + , p = new element.morphArray(fx._plot || fx.attrs.points || element.array) + + /* add size */ + if (fx._size) p.size(fx._size.width.to, fx._size.height.to) + + /* add movement */ + box = p.bbox() + if (fx._x) p.move(fx._x.to, box.y) + else if (fx._cx) p.move(fx._cx.to - box.width / 2, box.y) + + box = p.bbox() + if (fx._y) p.move(box.x, fx._y.to) + else if (fx._cy) p.move(box.x, fx._cy.to - box.height / 2) + + /* delete element oriented changes */ + delete fx._x + delete fx._y + delete fx._cx + delete fx._cy + delete fx._size + + fx._plot = element.array.morph(p) + } + } - 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.transform('x') : this.transform({ x: x }) - } - // Move over y-axis - , y: function(y) { - return y == null ? this.transform('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) + /* collect transformation keys */ + if (tkeys == null) { + tkeys = [] + for (key in fx.trans) + tkeys.push(key) + } + + /* collect style keys */ + if (skeys == null) { + skeys = [] + for (key in fx.styles) + skeys.push(key) + } + + /* apply easing */ + pos = ease == '<>' ? + (-Math.cos(pos * Math.PI) / 2) + 0.5 : + ease == '>' ? + Math.sin(pos * Math.PI / 2) : + ease == '<' ? + -Math.cos(pos * Math.PI / 2) + 1 : + ease == '-' ? + pos : + typeof ease == 'function' ? + ease(pos) : + pos + + /* run plot function */ + if (fx._plot) { + element.plot(fx._plot.at(pos)) + + } else { + /* run all x-position properties */ + if (fx._x) + element.x(fx._x.at(pos)) + else if (fx._cx) + element.cx(fx._cx.at(pos)) + + /* run all y-position properties */ + if (fx._y) + element.y(fx._y.at(pos)) + else if (fx._cy) + element.cy(fx._cy.at(pos)) + + /* run all size properties */ + if (fx._size) + element.size(fx._size.width.at(pos), fx._size.height.at(pos)) + } + + /* run all viewbox properties */ + if (fx._viewbox) + element.viewbox( + fx._viewbox.x.at(pos) + , fx._viewbox.y.at(pos) + , fx._viewbox.width.at(pos) + , fx._viewbox.height.at(pos) + ) + + /* run leading property */ + if (fx._leading) + element.leading(fx._leading.at(pos)) + + /* animate attributes */ + for (i = akeys.length - 1; i >= 0; i--) + element.attr(akeys[i], at(fx.attrs[akeys[i]], pos)) + + /* animate transformations */ + for (i = tkeys.length - 1; i >= 0; i--) + element.transform(tkeys[i], at(fx.trans[tkeys[i]], pos)) + + /* animate styles */ + for (i = skeys.length - 1; i >= 0; i--) + element.style(skeys[i], at(fx.styles[skeys[i]], pos)) + + /* callback for each keyframe */ + if (fx._during) + fx._during.call(element, pos, function(from, to) { + return at({ from: from, to: to }, pos) + }) } - // Move by center over y-axis - , cy: function(y) { - return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) + + if (typeof d === 'number') { + /* delay animation */ + this.timeout = setTimeout(function() { + var start = new Date().getTime() + + /* initialize situation object */ + fx.situation = { + interval: 1000 / 60 + , start: start + , play: true + , finish: start + d + , duration: d + } + + /* render function */ + fx.render = function() { + + if (fx.situation.play === true) { + // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby. + var time = new Date().getTime() + , pos = time > fx.situation.finish ? 1 : (time - fx.situation.start) / d + + /* process values */ + fx.to(pos) + + /* finish off animation */ + if (time > fx.situation.finish) { + if (fx._plot) + element.plot(new SVG.PointArray(fx._plot.destination).settle()) + + if (fx._loop === true || (typeof fx._loop == 'number' && fx._loop > 1)) { + if (typeof fx._loop == 'number') + --fx._loop + fx.animate(d, ease, delay) + } else { + fx._after ? fx._after.apply(element, [fx]) : fx.stop() + } + + } else { + requestAnimFrame(fx.render) + } + } else { + requestAnimFrame(fx.render) + } + + } + + /* start animation */ + fx.render() + + }, new SVG.Number(delay).valueOf()) } + + return this } - - // Add parent method - , construct: { - // Create a group element - group: function() { - return this.put(new SVG.G) - } + // Get bounding box of target element + , bbox: function() { + return this.target.bbox() } - }) + // Add animatable attributes + , attr: function(a, v) { + if (typeof a == 'object') { + for (var key in a) + this.attr(key, a[key]) + + } else { + var from = this.target.attr(a) - SVG.extend(SVG.Element, { - // Get all siblings, including myself - siblings: function() { - return this.parent().children() + this.attrs[a] = SVG.Color.isColor(from) ? + new SVG.Color(from).morph(v) : + SVG.regex.unit.test(from) ? + new SVG.Number(from).morph(v) : + { from: from, to: v } + } + + return this } - // Get the curent position siblings - , position: function() { - return this.parent().index(this) + // Add animatable transformations + , transform: function(o, v) { + // if (arguments.length == 1) { + // /* parse matrix string */ + // o = parseMatrix(o) + + // /* dlete matrixstring from object */ + // delete o.matrix + + // /* store matrix values */ + // for (v in o) + // this.trans[v] = { from: this.target.trans[v], to: o[v] } + + // } else { + // /* apply transformations as object if key value arguments are given*/ + // var transform = {} + // transform[o] = v + + // this.transform(transform) + // } + + // return this } - // Get the next element (will return null if there is none) - , next: function() { - return this.siblings()[this.position() + 1] + // Add animatable styles + , style: function(s, v) { + if (typeof s == 'object') + for (var key in s) + this.style(key, s[key]) + + else + this.styles[s] = { from: this.target.style(s), to: v } + + return this } - // Get the next element (will return null if there is none) - , previous: function() { - return this.siblings()[this.position() - 1] + // Animatable x-axis + , x: function(x) { + this._x = new SVG.Number(this.target.x()).morph(x) + + return this } - // Send given element one step forward - , forward: function() { - var i = this.position() + 1 - , p = this.parent() - - // Move node one step forward - p.removeElement(this).add(this, i) - - // Make sure defs node is always at the top - if (p instanceof SVG.Doc) - p.node.appendChild(p.defs().node) - + // Animatable y-axis + , y: function(y) { + this._y = new SVG.Number(this.target.y()).morph(y) + return this } - // Send given element one step backward - , backward: function() { - var i = this.position() + // Animatable center x-axis + , cx: function(x) { + this._cx = new SVG.Number(this.target.cx()).morph(x) - if (i > 0) - this.parent().removeElement(this).add(this, i - 1) - return this } - // Send given element all the way to the front - , front: function() { - var p = this.parent() - - // Move node forward - p.node.appendChild(this.node) - - // Make sure defs node is always at the top - if (p instanceof SVG.Doc) - p.node.appendChild(p.defs().node) - + // Animatable center y-axis + , cy: function(y) { + this._cy = new SVG.Number(this.target.cy()).morph(y) + return this } - // Send given element all the way to the back - , back: function() { - if (this.position() > 0) - this.parent().removeElement(this).add(this, 0) + // Add animatable move + , move: function(x, y) { + return this.x(x).y(y) + } + // Add animatable center + , center: function(x, y) { + return this.cx(x).cy(y) + } + // Add animatable size + , size: function(width, height) { + if (this.target instanceof SVG.Text) { + /* animate font size for Text elements */ + this.attr('font-size', width) + + } else { + /* animate bbox based size for all other elements */ + var box = this.target.bbox() + + this._size = { + width: new SVG.Number(box.width).morph(width) + , height: new SVG.Number(box.height).morph(height) + } + } return this } - // Inserts a given element before the targeted element - , before: function(element) { - element.remove() - - var i = this.position() + // Add animatable plot + , plot: function(p) { + this._plot = p + + return this + } + // Add leading method + , leading: function(value) { + if (this.target._leading) + this._leading = new SVG.Number(this.target._leading).morph(value) + + return this + } + // Add animatable viewbox + , viewbox: function(x, y, width, height) { + if (this.target instanceof SVG.Container) { + var box = this.target.viewbox() + + this._viewbox = { + x: new SVG.Number(box.x).morph(x) + , y: new SVG.Number(box.y).morph(y) + , width: new SVG.Number(box.width).morph(width) + , height: new SVG.Number(box.height).morph(height) + } + } - this.parent().add(element, i) - return this } - // Insters a given element after the targeted element - , after: function(element) { - element.remove() + // Add animateable gradient update + , update: function(o) { + if (this.target instanceof SVG.Stop) { + if (o.opacity != null) this.attr('stop-opacity', o.opacity) + if (o.color != null) this.attr('stop-color', o.color) + if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) + } + + return this + } + // Add callback for each keyframe + , during: function(during) { + this._during = during - var i = this.position() + return this + } + // Callback after animation + , after: function(after) { + this._after = after - this.parent().add(element, i + 1) - return this } - - }) + // Make loopable + , loop: function(times) { + this._loop = times || true - SVG.Mask = SVG.invent({ - // Initialize node - create: function() { - this.constructor.call(this, SVG.create('mask')) - - /* keep references to masked elements */ - this.targets = [] + return this } - - // Inherit from - , inherit: SVG.Container - - // 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) + // Stop running animation + , stop: function(fulfill) { + /* fulfill animation */ + if (fulfill === true) { + + this.animate(0) + + if (this._after) + this._after.apply(this.target, [this]) + + } else { + /* stop current animation */ + clearTimeout(this.timeout) + + /* reset storage for properties that need animation */ + this.attrs = {} + this.trans = {} + this.styles = {} + this.situation = {} + + /* delete destinations */ + delete this._x + delete this._y + delete this._cx + delete this._cy + delete this._size + delete this._plot + delete this._loop + delete this._after + delete this._during + delete this._leading + delete this._viewbox + } + + return this + } + // Pause running animation + , pause: function() { + if (this.situation.play === true) { + this.situation.play = false + this.situation.pause = new Date().getTime() + } + + return this + } + // Play running animation + , play: function() { + if (this.situation.play === false) { + var pause = new Date().getTime() - this.situation.pause - return this + this.situation.finish += pause + this.situation.start += pause + this.situation.play = true } + + return this } - // Add parent method - , construct: { - // Create masking element - mask: function() { - return this.defs().put(new SVG.Mask) - } + } + + // Define parent class +, parent: SVG.Element + + // Add method to parent elements +, construct: { + // Get fx module or create a new one, then animate with given duration and ease + animate: function(d, ease, delay) { + return (this.fx || (this.fx = new SVG.FX(this))).stop().animate(d, ease, delay) } - }) - - - SVG.extend(SVG.Element, { - // Distribute mask to svg element - maskWith: function(element) { - /* use given mask or create a new one */ - this.masker = element instanceof SVG.Mask ? element : this.parent().mask().add(element) - - /* store reverence on self in mask */ - this.masker.targets.push(this) + // Stop current animation; this is an alias to the fx instance + , stop: function(fulfill) { + if (this.fx) + this.fx.stop(fulfill) - /* apply mask */ - return this.attr('mask', 'url("#' + this.masker.attr('id') + '")') + return this + } + // Pause current animation + , pause: function() { + if (this.fx) + this.fx.pause() + + return this } - // Unmask element - , unmask: function() { - delete this.masker - return this.attr('mask', null) + // Play paused current animation + , play: function() { + if (this.fx) + this.fx.play() + + return this } - }) + } +}) +// +SVG.extend(SVG.Element, SVG.FX, { + // Relative move over x axis + dx: function(x) { + return this.x((this.target || this).x() + x) + } + // Relative move over y axis +, dy: function(y) { + return this.y((this.target || this).y() + y) + } + // Relative move over x and y axes +, dmove: function(x, y) { + return this.dx(x).dy(y) + } - SVG.ClipPath = SVG.invent({ - // Initialize node - create: function() { - this.constructor.call(this, SVG.create('clipPath')) - - /* keep references to clipped elements */ - this.targets = [] - } +}) +// Add events to elements +;[ 'click' + , 'dblclick' + , 'mousedown' + , 'mouseup' + , 'mouseover' + , 'mouseout' + , 'mousemove' + , 'mouseenter' + , 'mouseleave' + , 'touchstart' + , 'touchmove' + , 'touchleave' + , 'touchend' + , 'touchcancel' ].forEach(function(event) { + + /* add event to SVG.Element */ + SVG.Element.prototype[event] = function(f) { + var self = this + + /* bind event to element rather than element node */ + this.node['on' + event] = typeof f == 'function' ? + function() { return f.apply(self, arguments) } : null + + return this + } - // Inherit from - , inherit: SVG.Container +}) + +// Initialize events and listeners stack +SVG.events = {} +SVG.listeners = {} + +// Event constructor +SVG.registerEvent = function(event) { + if (!SVG.events[event]) + SVG.events[event] = new CustomEvent(event) +} + +// Add event binder in the SVG namespace +SVG.on = function(node, event, listener) { + var l = listener.bind(node.instance || node) + SVG.listeners[listener] = l + node.addEventListener(event, l, false) +} + +// Add event unbinder in the SVG namespace +SVG.off = function(node, event, listener) { + node.removeEventListener(event, SVG.listeners[listener], false) + delete SVG.listeners[listener] +} + +// +SVG.extend(SVG.Element, { + // Bind given event to listener + on: function(event, listener) { + SVG.on(this.node, event, listener) + + return this + } + // Unbind event from listener +, off: function(event, listener) { + SVG.off(this.node, event, listener) + + return this + } + // Fire given event +, fire: function(event, data) { + // Add detail data to event + SVG.events[event].detail = data + + // Dispatch event + this.node.dispatchEvent(SVG.events[event]) + + // Remove detail + delete SVG.events[event].detail + + return this + } +}) + +SVG.Defs = SVG.invent({ + // Initialize node + create: 'defs' + + // 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 +}) +SVG.G = SVG.invent({ + // Initialize node + create: 'g' + + // Inherit from +, inherit: SVG.Container - /* remove clipPath from parent */ - this.parent().removeElement(this) - - return this - } + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + return x == null ? this.transform('x') : this.transform({ x: x }) } - - // Add parent method - , construct: { - // Create clipping element - clip: function() { - return this.defs().put(new SVG.ClipPath) - } + // Move over y-axis + , y: function(y) { + return y == null ? this.transform('y') : this.transform({ y: y }) } - }) - - // - SVG.extend(SVG.Element, { - // Distribute clipPath to svg element - clipWith: function(element) { - /* use given clip or create a new one */ - this.clipper = element instanceof SVG.ClipPath ? element : this.parent().clip().add(element) - - /* store reverence on self in mask */ - this.clipper.targets.push(this) - - /* apply mask */ - return this.attr('clip-path', 'url("#' + this.clipper.attr('id') + '")') + // 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) } - // Unclip element - , unclip: function() { - delete this.clipper - return this.attr('clip-path', null) + } + + // Add parent method +, construct: { + // Create a group element + group: function() { + return this.put(new SVG.G) } + } +}) +// ### This module adds backward / forward functionality to elements. + +// +SVG.extend(SVG.Element, { + // Get all siblings, including myself + siblings: function() { + return this.parent().children() + } + // Get the curent position siblings +, position: function() { + return this.parent().index(this) + } + // Get the next element (will return null if there is none) +, next: function() { + return this.siblings()[this.position() + 1] + } + // Get the next element (will return null if there is none) +, previous: function() { + return this.siblings()[this.position() - 1] + } + // Send given element one step forward +, forward: function() { + var i = this.position() + 1 + , p = this.parent() + + // Move node one step forward + p.removeElement(this).add(this, i) + + // Make sure defs node is always at the top + if (p instanceof SVG.Doc) + p.node.appendChild(p.defs().node) + + return this + } + // Send given element one step backward +, backward: function() { + var i = this.position() - }) + if (i > 0) + this.parent().removeElement(this).add(this, i - 1) + + return this + } + // Send given element all the way to the front +, front: function() { + var p = this.parent() + + // Move node forward + p.node.appendChild(this.node) + + // Make sure defs node is always at the top + if (p instanceof SVG.Doc) + p.node.appendChild(p.defs().node) + + return this + } + // Send given element all the way to the back +, back: function() { + if (this.position() > 0) + this.parent().removeElement(this).add(this, 0) + + return this + } + // Inserts a given element before the targeted element +, before: function(element) { + element.remove() + + var i = this.position() + + this.parent().add(element, i) - SVG.Gradient = SVG.invent({ - // Initialize node - create: function(type) { - this.constructor.call(this, SVG.create(type + 'Gradient')) + return this + } + // Insters a given element after the targeted element +, after: function(element) { + element.remove() + + var i = this.position() + + this.parent().add(element, i + 1) + + return this + } + +}) +SVG.Mask = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('mask')) + + /* keep references to masked elements */ + this.targets = [] + } + + // Inherit from +, inherit: SVG.Container + + // 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) - /* store type */ - this.type = type + return this } + } - // 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(offset, color, opacity) { - return this.put(new SVG.Stop).update(offset, color, opacity) - } - // Update gradient - , update: function(block) { - /* remove all stops */ - this.clear() - - /* invoke passed block */ - if (typeof block == 'function') - block.call(this, this) - - return this - } - // Return the fill id - , fill: function() { - return 'url(#' + this.id() + ')' - } - // Alias string convertion to fill - , toString: function() { - return this.fill() - } + // 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) { + /* use given mask or create a new one */ + this.masker = element instanceof SVG.Mask ? element : this.parent().mask().add(element) + + /* store reverence on self in mask */ + this.masker.targets.push(this) - // Add parent method - , construct: { - // Create gradient element in defs - gradient: function(type, block) { - return this.defs().gradient(type, block) - } - } - }) + /* apply mask */ + return this.attr('mask', 'url("#' + this.masker.attr('id') + '")') + } + // Unmask element +, unmask: function() { + delete this.masker + return this.attr('mask', null) + } - SVG.extend(SVG.Defs, { - // define gradient - gradient: function(type, block) { - return this.put(new SVG.Gradient(type)).update(block) +}) + +SVG.ClipPath = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('clipPath')) + + /* keep references to clipped elements */ + this.targets = [] + } + + // 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 } - - }) - - SVG.Stop = SVG.invent({ - // Initialize node - create: 'stop' - - // Inherit from - , inherit: SVG.Element - - // Add class methods - , extend: { - // add color stops - update: function(o) { - if (typeof o == 'number' || o instanceof SVG.Number) { - o = { - offset: arguments[0] - , color: arguments[1] - , opacity: arguments[2] - } - } - - /* 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 - } + // Add parent method +, construct: { + // Create clipping element + clip: function() { + return this.defs().put(new SVG.ClipPath) } - - }) + } +}) +// +SVG.extend(SVG.Element, { + // Distribute clipPath to svg element + clipWith: function(element) { + /* use given clip or create a new one */ + this.clipper = element instanceof SVG.ClipPath ? element : this.parent().clip().add(element) - SVG.Pattern = SVG.invent({ - // Initialize node - create: 'pattern' - - // Inherit from - , inherit: SVG.Container + /* store reverence on self in mask */ + this.clipper.targets.push(this) + + /* apply mask */ + return this.attr('clip-path', 'url("#' + this.clipper.attr('id') + '")') + } + // Unclip element +, unclip: function() { + delete this.clipper + return this.attr('clip-path', null) + } - // Add class methods - , extend: { - // Return the fill id - fill: function() { - return 'url(#' + this.id() + ')' - } - // Update pattern by rebuilding - , update: function(block) { - /* remove content */ - this.clear() - - /* invoke passed block */ - if (typeof block == 'function') - block.call(this, this) - - return this - } - // Alias string convertion to fill - , toString: function() { - return this.fill() - } - } +}) +SVG.Gradient = SVG.invent({ + // Initialize node + create: function(type) { + this.constructor.call(this, SVG.create(type + 'Gradient')) - // Add parent method - , construct: { - // Create pattern element in defs - pattern: function(width, height, block) { - return this.defs().pattern(width, height, block) - } + /* store type */ + this.type = type + } + + // 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(offset, color, opacity) { + return this.put(new SVG.Stop).update(offset, color, opacity) + } + // Update gradient + , update: function(block) { + /* remove all stops */ + this.clear() + + /* invoke passed block */ + if (typeof block == 'function') + block.call(this, this) + + return this } - }) + // Return the fill id + , fill: function() { + return 'url(#' + this.id() + ')' + } + // Alias string convertion to fill + , toString: function() { + return this.fill() + } + } - SVG.extend(SVG.Defs, { - // Define gradient - pattern: function(width, height, block) { - return this.put(new SVG.Pattern).update(block).attr({ - x: 0 - , y: 0 - , width: width - , height: height - , patternUnits: 'userSpaceOnUse' - }) + // 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) { + return this.put(new SVG.Gradient(type)).update(block) + } - }) +}) - SVG.Doc = SVG.invent({ - // Initialize node - create: function(element) { - if (element) { - /* ensure the presence of a dom element */ - element = 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. */ - if (element.nodeName == 'svg') { - this.constructor.call(this, element) - } else { - this.constructor.call(this, SVG.create('svg')) - element.appendChild(this.node) +SVG.Stop = SVG.invent({ + // Initialize node + create: 'stop' + + // Inherit from +, inherit: SVG.Element + + // Add class methods +, extend: { + // add color stops + update: function(o) { + if (typeof o == 'number' || o instanceof SVG.Number) { + o = { + offset: arguments[0] + , color: arguments[1] + , opacity: arguments[2] } - - /* set svg element attributes and ensure defs node */ - this.namespace().size('100%', '100%').defs() } + + /* 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 } + } + +}) + +SVG.Pattern = SVG.invent({ + // Initialize node + create: 'pattern' + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Return the fill id + fill: function() { + return 'url(#' + this.id() + ')' + } + // Update pattern by rebuilding + , update: function(block) { + /* remove content */ + this.clear() + + /* invoke passed block */ + if (typeof block == 'function') + block.call(this, this) + + return this + } + // Alias string convertion to fill + , toString: function() { + return this.fill() + } + } - // Inherit from - , inherit: SVG.Container - - // Add class methods - , extend: { - // Add namespaces - namespace: function() { - return this - .attr({ xmlns: SVG.ns, version: '1.1' }) - .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) - } - // Creates and returns defs element - , defs: function() { - if (!this._defs) { - var defs - - // Find or create a defs element in this instance - if (defs = this.node.getElementsByTagName('defs')[0]) - this._defs = SVG.adopt(defs) - else - this._defs = new SVG.Defs - - // Make sure the defs node is at the end of the stack - this.node.appendChild(this._defs.node) - } - - return this._defs - } - // custom parent method - , parent: function() { - return this.node.parentNode.nodeName == '#document' ? null : this.node.parentNode + // Add parent method +, construct: { + // Create pattern element in defs + pattern: function(width, height, block) { + return this.defs().pattern(width, height, block) + } + } +}) + +SVG.extend(SVG.Defs, { + // Define gradient + pattern: function(width, height, block) { + return this.put(new SVG.Pattern).update(block).attr({ + x: 0 + , y: 0 + , width: width + , height: height + , patternUnits: 'userSpaceOnUse' + }) + } + +}) +SVG.Doc = SVG.invent({ + // Initialize node + create: function(element) { + if (element) { + /* ensure the presence of a dom element */ + element = 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. */ + if (element.nodeName == 'svg') { + this.constructor.call(this, element) + } else { + this.constructor.call(this, SVG.create('svg')) + element.appendChild(this.node) } + + /* set svg element attributes and ensure defs node */ + this.namespace().size('100%', '100%').defs() } - - }) + } + // Inherit from +, inherit: SVG.Container - SVG.extend(SVG.Doc, { - // Callback - spof: function() { - if (this.doSpof) { - var pos = this.node.getScreenCTM() - - if (pos) - this - .style('left', (-pos.e % 1) + 'px') - .style('top', (-pos.f % 1) + 'px') - } - + // Add class methods +, extend: { + // Add namespaces + namespace: function() { return this + .attr({ xmlns: SVG.ns, version: '1.1' }) + .attr('xmlns:xlink', SVG.xlink, SVG.xmlns) } - - // Sub-pixel offset enabler - , fixSubPixelOffset: function() { - var self = this - - // Enable spof - this.doSpof = true - - // Make sure sub-pixel offset is fixed every time the window is resized - SVG.on(window, 'resize', function() { self.spof() }) - - return this.spof() - } - - }) + // Creates and returns defs element + , defs: function() { + if (!this._defs) { + var defs - SVG.Shape = SVG.invent({ - // Initialize node - create: function(element) { - this.constructor.call(this, element) - } - - // Inherit from - , inherit: SVG.Element - - }) + // Find or create a defs element in this instance + if (defs = this.node.getElementsByTagName('defs')[0]) + this._defs = SVG.adopt(defs) + else + this._defs = new SVG.Defs - SVG.Symbol = SVG.invent({ - // Initialize node - create: 'symbol' - - // Inherit from - , inherit: SVG.Container - - // Add parent method - , construct: { - // Create a new symbol - symbol: function() { - return this.defs().put(new SVG.Symbol) + // Make sure the defs node is at the end of the stack + this.node.appendChild(this._defs.node) } - } - - }) - 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) - } + return this._defs } - - // Add parent method - , construct: { - // Create a use element - use: function(element) { - return this.put(new SVG.Use).element(element) - } + // custom parent method + , parent: function() { + return this.node.parentNode.nodeName == '#document' ? null : this.node.parentNode } - }) - - 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.Circle = SVG.invent({ - // Initialize node - create: 'circle' - - // Inherit from - , inherit: SVG.Shape - - // Add parent method - , construct: { - // Create circle element, based on ellipse - circle: function(size) { - return this.put(new SVG.Circle).rx(new SVG.Number(size).divide(2)).move(0, 0) - } +// Fix for possible sub-pixel offset. See: +// https://bugzilla.mozilla.org/show_bug.cgi?id=608812 +SVG.extend(SVG.Doc, { + // Callback + spof: function() { + if (this.doSpof) { + var pos = this.node.getScreenCTM() + + if (pos) + this + .style('left', (-pos.e % 1) + 'px') + .style('top', (-pos.f % 1) + 'px') } - }) + + return this + } + + // Sub-pixel offset enabler +, fixSubPixelOffset: function() { + var self = this + + // Enable spof + this.doSpof = true + + // Make sure sub-pixel offset is fixed every time the window is resized + SVG.on(window, 'resize', function() { self.spof() }) + + return this.spof() + } - SVG.extend(SVG.Circle, SVG.FX, { - // Radius x value - rx: function(rx) { - return this.attr('r', rx) - } - // Alias radius x value - , ry: function(ry) { - return this.rx(ry) +}) +SVG.Shape = SVG.invent({ + // Initialize node + create: function(element) { + this.constructor.call(this, element) + } + + // Inherit from +, inherit: SVG.Element + +}) + +SVG.Symbol = SVG.invent({ + // Initialize node + create: 'symbol' + + // Inherit from +, inherit: SVG.Container + + // Add parent method +, construct: { + // Create a new symbol + symbol: function() { + return this.defs().put(new SVG.Symbol) } - }) - - SVG.Ellipse = SVG.invent({ - // Initialize node - create: 'ellipse' - - // Inherit from - , inherit: SVG.Shape + } - // Add parent method - , construct: { - // Create an ellipse - ellipse: function(width, height) { - return this.put(new SVG.Ellipse).size(width, height).move(0, 0) - } +}) +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.Ellipse, SVG.Rect, SVG.FX, { - // Radius x value - rx: function(rx) { - return this.attr('rx', rx) - } - // Radius y value - , ry: function(ry) { - return this.attr('ry', ry) + // Add parent method +, construct: { + // Create a use element + use: function(element) { + return this.put(new SVG.Use).element(element) } - }) - - // Add common method - SVG.extend(SVG.Circle, SVG.Ellipse, { - // Move over x-axis - x: function(x) { - return x == null ? this.cx() - this.rx() : this.cx(x + this.rx()) - } - // Move over y-axis - , y: function(y) { - return y == null ? this.cy() - this.ry() : this.cy(y + this.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.transform('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.transform('scaleY'))) - } - // Set width of element - , width: function(width) { - return width == null ? this.rx() * 2 : this.rx(new SVG.Number(width).divide(2)) - } - // Set height of element - , height: function(height) { - return height == null ? this.ry() * 2 : this.ry(new SVG.Number(height).divide(2)) - } - // Custom size function - , size: function(width, height) { - var p = proportionalSize(this.bbox(), width, height) - - return this - .rx(new SVG.Number(p.width).divide(2)) - .ry(new SVG.Number(p.height).divide(2)) - } - }) + } +}) +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.Circle = SVG.invent({ + // Initialize node + create: 'circle' - SVG.Line = SVG.invent({ - // Initialize node - create: 'line' - - // Inherit from - , inherit: SVG.Shape - - // Add class methods - , extend: { - // Get array - array: function() { - return new SVG.PointArray([ - [ this.attr('x1'), this.attr('y1') ] - , [ this.attr('x2'), this.attr('y2') ] - ]) - } - // Overwrite native plot() method - , plot: function(x1, y1, x2, y2) { - if (arguments.length == 4) - x1 = { x1: x1, y1: y1, x2: x2, y2: y2 } - else - x1 = new SVG.PointArray(x1).toLine() - - return this.attr(x1) - } - // Move by left top corner - , move: function(x, y) { - return this.attr(this.array().move(x, y).toLine()) - } - // Set element size to given width and height - , size: function(width, height) { - var p = proportionalSize(this.bbox(), width, height) - - return this.attr(this.array().size(p.width, p.height).toLine()) - } + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create circle element, based on ellipse + circle: function(size) { + return this.put(new SVG.Circle).rx(new SVG.Number(size).divide(2)).move(0, 0) } - - // 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.extend(SVG.Circle, SVG.FX, { + // Radius x value + rx: function(rx) { + return this.attr('r', rx) + } + // Alias radius x value +, ry: function(ry) { + return this.rx(ry) + } +}) + +SVG.Ellipse = SVG.invent({ + // Initialize node + create: 'ellipse' + + // Inherit from +, inherit: SVG.Shape + + // Add parent method +, construct: { + // Create an ellipse + ellipse: function(width, height) { + return this.put(new SVG.Ellipse).size(width, height).move(0, 0) } - }) + } +}) +SVG.extend(SVG.Ellipse, SVG.Rect, SVG.FX, { + // Radius x value + rx: function(rx) { + return this.attr('rx', rx) + } + // Radius y value +, ry: function(ry) { + return this.attr('ry', ry) + } +}) - 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) - } +// Add common method +SVG.extend(SVG.Circle, SVG.Ellipse, { + // Move over x-axis + x: function(x) { + return x == null ? this.cx() - this.rx() : this.cx(x + this.rx()) } - }) - - SVG.Polygon = SVG.invent({ - // Initialize node - create: 'polygon' - - // 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) - } + // Move over y-axis + , y: function(y) { + return y == null ? this.cy() - this.ry() : this.cy(y + this.ry()) } - }) - - // Add polygon-specific functions - SVG.extend(SVG.Polyline, SVG.Polygon, { + // Move by center over x-axis + , cx: function(x) { + return x == null ? this.attr('cx') : this.attr('cx', new SVG.Number(x).divide(this.transform('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.transform('scaleY'))) + } + // Set width of element + , width: function(width) { + return width == null ? this.rx() * 2 : this.rx(new SVG.Number(width).divide(2)) + } + // Set height of element + , height: function(height) { + return height == null ? this.ry() * 2 : this.ry(new SVG.Number(height).divide(2)) + } + // Custom size function + , size: function(width, height) { + var p = proportionalSize(this.bbox(), width, height) + + return this + .rx(new SVG.Number(p.width).divide(2)) + .ry(new SVG.Number(p.height).divide(2)) + } +}) +SVG.Line = SVG.invent({ + // Initialize node + create: 'line' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { // Get array array: function() { - return this._array || (this._array = new SVG.PointArray(this.attr('points'))) + return new SVG.PointArray([ + [ this.attr('x1'), this.attr('y1') ] + , [ this.attr('x2'), this.attr('y2') ] + ]) } - // Plot new path - , plot: function(p) { - return this.attr('points', (this._array = new SVG.PointArray(p))) + // Overwrite native plot() method + , plot: function(x1, y1, x2, y2) { + if (arguments.length == 4) + x1 = { x1: x1, y1: y1, x2: x2, y2: y2 } + else + x1 = new SVG.PointArray(x1).toLine() + + return this.attr(x1) } // Move by left top corner , move: function(x, y) { - return this.attr('points', this.array().move(x, y)) + return this.attr(this.array().move(x, y).toLine()) } // Set element size to given width and height , size: function(width, height) { var p = proportionalSize(this.bbox(), width, height) + + return this.attr(this.array().size(p.width, p.height).toLine()) + } + } - return this.attr('points', this.array().size(p.width, p.height)) + // 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 = 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 = SVG.invent({ + // Initialize node + create: 'polygon' + + // 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, { + // Get array + array: function() { + return this._array || (this._array = new SVG.PointArray(this.attr('points'))) + } + // Plot new path +, plot: function(p) { + return this.attr('points', (this._array = new SVG.PointArray(p))) + } + // Move by left top corner +, move: function(x, y) { + return this.attr('points', this.array().move(x, y)) + } + // Set element size to given width and height +, size: function(width, height) { + var p = proportionalSize(this.bbox(), width, height) + + return this.attr('points', this.array().size(p.width, p.height)) + } + +}) +// unify all point to point elements +SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, { + // Define morphable array + morphArray: SVG.PointArray + // 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 width of element +, width: function(width) { + var b = this.bbox() - SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, { + return width == null ? b.width : this.size(width, b.height) + } + // Set height of element +, height: function(height) { + var b = this.bbox() + + return height == null ? b.height : this.size(b.width, height) + } +}) +SVG.Path = SVG.invent({ + // Initialize node + create: 'path' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { // Define morphable array - morphArray: SVG.PointArray + morphArray: SVG.PathArray + // Get array + , array: function() { + return this._array || (this._array = new SVG.PathArray(this.attr('d'))) + } + // Plot new poly points + , plot: function(p) { + return this.attr('d', (this._array = new SVG.PathArray(p))) + } + // 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) @@ -3006,1046 +3042,994 @@ , 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 = proportionalSize(this.bbox(), width, height) + + return this.attr('d', this.array().size(p.width, p.height)) + } // Set width of element , width: function(width) { - var b = this.bbox() - - return width == null ? b.width : this.size(width, b.height) + return width == null ? this.bbox().width : this.size(width, this.bbox().height) } // Set height of element , height: function(height) { - var b = this.bbox() + return height == null ? this.bbox().height : this.size(this.bbox().width, height) + } + + } - return height == null ? b.height : this.size(b.width, height) + // Add parent method +, construct: { + // Create a wrapped path element + path: function(d) { + return this.put(new SVG.Path).plot(d) } - }) + } +}) +SVG.Image = SVG.invent({ + // Initialize node + create: 'image' - SVG.Path = SVG.invent({ - // Initialize node - create: 'path' - - // Inherit from - , inherit: SVG.Shape - - // Add class methods - , extend: { - // Define morphable array - morphArray: SVG.PathArray - // Get array - , array: function() { - return this._array || (this._array = new SVG.PathArray(this.attr('d'))) - } - // Plot new poly points - , plot: function(p) { - return this.attr('d', (this._array = new SVG.PathArray(p))) - } - // 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 = proportionalSize(this.bbox(), 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) - } + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // (re)load image + load: function(url) { + if (!url) return this + + var self = this + , img = document.createElement('img') - } - - // Add parent method - , construct: { - // Create a wrapped path element - path: function(d) { - return this.put(new SVG.Path).plot(d) + /* preload image */ + img.onload = function() { + var p = self.doc(SVG.Pattern) + + /* ensure image size */ + if (self.width() == 0 && self.height() == 0) + self.size(img.width, img.height) + + /* ensure pattern size if not set */ + if (p && p.width() == 0 && p.height() == 0) + p.size(self.width(), self.height()) + + /* callback */ + if (typeof self._loaded === 'function') + self._loaded.call(self, { + width: img.width + , height: img.height + , ratio: img.width / img.height + , url: url + }) } - } - }) - SVG.Image = SVG.invent({ - // Initialize node - create: 'image' - - // Inherit from - , inherit: SVG.Shape - - // Add class methods - , extend: { - // (re)load image - load: function(url) { - if (!url) return this - - var self = this - , img = document.createElement('img') - - /* preload image */ - img.onload = function() { - var p = self.doc(SVG.Pattern) - - /* ensure image size */ - if (self.width() == 0 && self.height() == 0) - self.size(img.width, img.height) - - /* ensure pattern size if not set */ - if (p && p.width() == 0 && p.height() == 0) - p.size(self.width(), self.height()) - - /* callback */ - if (typeof self._loaded === 'function') - self._loaded.call(self, { - width: img.width - , height: img.height - , ratio: img.width / img.height - , url: url - }) - } - - return this.attr('href', (img.src = this.src = url), SVG.xlink) - } - // Add loade callback - , loaded: function(loaded) { - this._loaded = loaded - return this - } + return this.attr('href', (img.src = this.src = url), SVG.xlink) } - - // Add parent method - , construct: { - // Create image element, load image and set its size - image: function(source, width, height) { - return this.put(new SVG.Image).load(source).size(width || 0, height || width || 0) - } + // Add loade callback + , loaded: function(loaded) { + this._loaded = loaded + return this } + } - }) + // Add parent method +, construct: { + // Create image element, load image and set its size + image: function(source, width, height) { + return this.put(new SVG.Image).load(source).size(width || 0, height || width || 0) + } + } + +}) + +SVG.Text = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('text')) + + this._leading = new SVG.Number(1.3) /* store leading value for rebuilding */ + this._rebuild = true /* enable automatic updating of dy values */ + this._build = false /* disable build mode for adding multiple lines */ - SVG.Text = SVG.invent({ - // Initialize node - create: function() { - this.constructor.call(this, SVG.create('text')) + /* set default font */ + this.attr('font-family', SVG.defaults.attrs['font-family']) + } + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // Move over x-axis + x: function(x) { + /* act as getter */ + if (x == null) + return this.attr('x') - this._leading = new SVG.Number(1.3) /* store leading value for rebuilding */ - this._rebuild = true /* enable automatic updating of dy values */ - this._build = false /* disable build mode for adding multiple lines */ - - /* set default font */ - this.attr('font-family', SVG.defaults.attrs['font-family']) + /* move lines as well if no textPath is present */ + if (!this.textPath) + this.lines.each(function() { if (this.newLined) this.x(x) }) + + return this.attr('x', x) } - - // Inherit from - , inherit: SVG.Shape - - // Add class methods - , extend: { - // Move over x-axis - x: function(x) { - /* act as getter */ - if (x == null) - return this.attr('x') - - /* move lines as well if no textPath is present */ - if (!this.textPath) - this.lines.each(function() { if (this.newLined) this.x(x) }) - - return this.attr('x', x) - } - // Move over y-axis - , y: function(y) { - var oy = this.attr('y') - , o = typeof oy === 'number' ? oy - this.bbox().y : 0 - - /* act as getter */ - if (y == null) - return typeof oy === 'number' ? oy - o : oy - - return this.attr('y', typeof y === 'number' ? y + o : y) - } - // Move center over x-axis - , cx: function(x) { - return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) - } - // Move center over y-axis - , cy: function(y) { - return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) - } - // Set the text content - , text: function(text) { - /* act as getter */ - if (typeof text === 'undefined') return this.content - - /* remove existing content */ - this.clear().build(true) - - if (typeof text === 'function') { - /* call block */ - text.call(this, this) - - } else { - /* store text and make sure text is not blank */ - text = (this.content = text).split('\n') - - /* build new lines */ - for (var i = 0, il = text.length; i < il; i++) - this.tspan(text[i]).newLine() - } - - /* disable build mode and rebuild lines */ - return this.build(false).rebuild() - } - // Set font size - , size: function(size) { - return this.attr('font-size', size).rebuild() - } - // Set / get leading - , leading: function(value) { - /* act as getter */ - if (value == null) - return this._leading - - /* act as setter */ - this._leading = new SVG.Number(value) - - return this.rebuild() - } - // Rebuild appearance type - , rebuild: function(rebuild) { - /* store new rebuild flag if given */ - if (typeof rebuild == 'boolean') - this._rebuild = rebuild - - /* define position of all lines */ - if (this._rebuild) { - var self = this - - this.lines.each(function() { - if (this.newLined) { - if (!this.textPath) - this.attr('x', self.attr('x')) - this.attr('dy', self._leading * new SVG.Number(self.attr('font-size'))) - } - }) - - this.fire('rebuild') - } - - return this - } - // Enable / disable build mode - , build: function(build) { - this._build = !!build - return this - } + // Move over y-axis + , y: function(y) { + var oy = this.attr('y') + , o = typeof oy === 'number' ? oy - this.bbox().y : 0 + + /* act as getter */ + if (y == null) + return typeof oy === 'number' ? oy - o : oy + + return this.attr('y', typeof y === 'number' ? y + o : y) } - - // Add parent method - , construct: { - // Create text element - text: function(text) { - return this.put(new SVG.Text).text(text) - } - // Create plain text element - , plain: function(text) { - return this.put(new SVG.Text).plain(text) - } + // Move center over x-axis + , cx: function(x) { + return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) } - - }) - - SVG.Tspan = SVG.invent({ - // Initialize node - create: 'tspan' - - // Inherit from - , inherit: SVG.Shape - - // Add class methods - , extend: { - // Set text content - text: function(text) { - typeof text === 'function' ? text.call(this, this) : this.plain(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() { - /* fetch text parent */ - var t = this.doc(SVG.Text) - - /* mark new line */ - this.newLined = true - - /* apply new hy¡n */ - return this.dy(t._leading * t.attr('font-size')).attr('x', t.x()) - } + // Move center over y-axis + , cy: function(y) { + return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) } - - }) - - SVG.extend(SVG.Text, SVG.Tspan, { - // Create plain text node - plain: function(text) { - /* clear if build mode is disabled */ - if (this._build === false) - this.clear() - - /* create text node */ - this.node.appendChild(document.createTextNode((this.content = text))) + // Set the text content + , text: function(text) { + /* act as getter */ + if (typeof text === 'undefined') return this.content - return this - } - // Create a tspan - , tspan: function(text) { - var node = (this.textPath || this).node - , tspan = new SVG.Tspan - - /* clear if build mode is disabled */ - if (this._build === false) - this.clear() + /* remove existing content */ + this.clear().build(true) - /* add new tspan and reference */ - node.appendChild(tspan.node) - - /* only first level tspans are considered to be "lines" */ - if (this instanceof SVG.Text) - this.lines.add(tspan) - - return tspan.text(text) - } - // Clear all lines - , clear: function() { - var node = (this.textPath || this).node - - /* remove existing child nodes */ - while (node.hasChildNodes()) - node.removeChild(node.lastChild) - - /* reset content references */ - if (this instanceof SVG.Text) { - delete this.lines - this.lines = new SVG.Set - this.content = '' + if (typeof text === 'function') { + /* call block */ + text.call(this, this) + + } else { + /* store text and make sure text is not blank */ + text = (this.content = text).split('\n') + + /* build new lines */ + for (var i = 0, il = text.length; i < il; i++) + this.tspan(text[i]).newLine() } - return this - } - // Get length of text element - , length: function() { - return this.node.getComputedTextLength() + /* disable build mode and rebuild lines */ + return this.build(false).rebuild() + } + // Set font size + , size: function(size) { + return this.attr('font-size', size).rebuild() + } + // Set / get leading + , leading: function(value) { + /* act as getter */ + if (value == null) + return this._leading + + /* act as setter */ + this._leading = new SVG.Number(value) + + return this.rebuild() } - }) - - // Register rebuild event - SVG.registerEvent('rebuild') + // Rebuild appearance type + , rebuild: function(rebuild) { + /* store new rebuild flag if given */ + if (typeof rebuild == 'boolean') + this._rebuild = rebuild + /* define position of all lines */ + if (this._rebuild) { + var self = this + + this.lines.each(function() { + if (this.newLined) { + if (!this.textPath) + this.attr('x', self.attr('x')) + this.attr('dy', self._leading * new SVG.Number(self.attr('font-size'))) + } + }) - 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) - - /* create circular reference */ - this.textPath.parent = this - - /* link textPath to path and add content */ - this.textPath.attr('href', '#' + this.track, SVG.xlink) - - return this + this.fire('rebuild') } - // Plot path if any - , plot: function(d) { - if (this.track) this.track.plot(d) - return this - } - } - }) - SVG.Nested = SVG.invent({ - // Initialize node - create: function() { - this.constructor.call(this, SVG.create('svg')) - - this.style('overflow', 'visible') + return this } - - // Inherit from - , inherit: SVG.Container - - // Add parent method - , construct: { - // Create nested svg document - nested: function() { - return this.put(new SVG.Nested) - } + // Enable / disable build mode + , build: function(build) { + this._build = !!build + return this } - }) - - 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 text element + text: function(text) { + return this.put(new SVG.Text).text(text) } - - // 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) { - var link = new SVG.A - - if (typeof url == 'function') - url.call(link, link) - else - link.to(url) - - return this.parent().put(link).put(this) + // Create plain text element + , plain: function(text) { + return this.put(new SVG.Text).plain(text) } - - }) + } - SVG.Marker = SVG.invent({ - // Initialize node - create: 'marker' - - // Inherit from - , inherit: SVG.Container - - // Add class methods - , extend: { - // Set width of element - width: function(width) { - return this.attr('markerWidth', width) - } - // Set height of element - , height: function(height) { - return this.attr('markerHeight', height) - } - // Set marker refX and refY - , ref: function(x, y) { - return this.attr('refX', x).attr('refY', y) - } - // Update marker - , update: function(block) { - /* remove all content */ - this.clear() - - /* invoke passed block */ - if (typeof block == 'function') - block.call(this, this) - - return this - } - // Return the fill id - , toString: function() { - return 'url(#' + this.id() + ')' - } - } - - // Add parent method - , construct: { - marker: function(width, height, block) { - // Create marker element in defs - return this.defs().marker(width, height, block) - } +}) + +SVG.Tspan = SVG.invent({ + // Initialize node + create: 'tspan' + + // Inherit from +, inherit: SVG.Shape + + // Add class methods +, extend: { + // Set text content + text: function(text) { + typeof text === 'function' ? text.call(this, this) : this.plain(text) + + return this } - - }) - - SVG.extend(SVG.Defs, { - // Create marker - marker: function(width, height, block) { - // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto - return this.put(new SVG.Marker) - .size(width, height) - .ref(width / 2, height / 2) - .viewbox(0, 0, width, height) - .attr('orient', 'auto') - .update(block) + // Shortcut dx + , dx: function(dx) { + return this.attr('dx', dx) } - - }) - - SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, SVG.Path, { - // Create and attach markers - marker: function(marker, width, height, block) { - var attr = ['marker'] - - // Build attribute name - if (marker != 'all') attr.push(marker) - attr = attr.join('-') - - // Set marker attribute - marker = arguments[1] instanceof SVG.Marker ? - arguments[1] : - this.doc().marker(width, height, block) - - return this.attr(attr, marker) + // Shortcut dy + , dy: function(dy) { + return this.attr('dy', dy) } - - }) + // Create new line + , newLine: function() { + /* fetch text parent */ + var t = this.doc(SVG.Text) + + /* mark new line */ + this.newLined = true - var sugar = { - stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'] - , fill: ['color', 'opacity', 'rule'] - , prefix: function(t, a) { - return a == 'color' ? t : t + '-' + a + /* apply new hy¡n */ + return this.dy(t._leading * t.attr('font-size')).attr('x', t.x()) } } - /* Add sugar for fill and stroke */ - ;['fill', 'stroke'].forEach(function(m) { - var i, extension = {} +}) + +SVG.extend(SVG.Text, SVG.Tspan, { + // Create plain text node + plain: function(text) { + /* clear if build mode is disabled */ + if (this._build === false) + this.clear() + + /* create text node */ + this.node.appendChild(document.createTextNode((this.content = text))) - extension[m] = function(o) { - if (typeof o == 'string' || SVG.Color.isRgb(o) || (o && typeof o.fill === 'function')) - this.attr(m, o) - - else - /* set all attributes from sugar.fill and sugar.stroke list */ - for (i = sugar[m].length - 1; i >= 0; i--) - if (o[sugar[m][i]] != null) - this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]) - - return this - } + return this + } + // Create a tspan +, tspan: function(text) { + var node = (this.textPath || this).node + , tspan = new SVG.Tspan + + /* clear if build mode is disabled */ + if (this._build === false) + this.clear() - SVG.extend(SVG.Element, SVG.FX, extension) + /* add new tspan and reference */ + node.appendChild(tspan.node) + + /* only first level tspans are considered to be "lines" */ + if (this instanceof SVG.Text) + this.lines.add(tspan) + + return tspan.text(text) + } + // Clear all lines +, clear: function() { + var node = (this.textPath || this).node + + /* remove existing child nodes */ + while (node.hasChildNodes()) + node.removeChild(node.lastChild) - }) - - SVG.extend(SVG.Element, SVG.FX, { - // Rotation - rotate: function(d, cx, cy) { - return this.transform({ rotation: d, cx: cx, cy: cy }) + /* reset content references */ + if (this instanceof SVG.Text) { + delete this.lines + this.lines = new SVG.Set + this.content = '' } - // Skew - , skew: function(x, y) { - return this.transform({ skewX: x, skewY: y }) + + return this + } + // Get length of text element +, length: function() { + return this.node.getComputedTextLength() + } +}) + +// Register rebuild event +SVG.registerEvent('rebuild') + +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) + + /* 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 } - // Scale - , scale: function(x, y, cx, cy) { - return arguments.length == 1 || arguments.length == 3 ? - this.transform({ scale: x, cx: y, cy: cx }) : - this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy }) + } +}) +SVG.Nested = SVG.invent({ + // Initialize node + create: function() { + this.constructor.call(this, SVG.create('svg')) + + this.style('overflow', 'visible') + } + + // Inherit from +, inherit: SVG.Container + + // Add parent method +, construct: { + // Create nested svg document + nested: function() { + return this.put(new SVG.Nested) } - // Translate - , translate: function(x, y) { - return this.transform({ x: x, y: y }) + } +}) +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) } - // Matrix - , matrix: function(m) { - return this.attr('transform', new SVG.Matrix(m)) + // Link show attribute + , show: function(target) { + return this.attr('show', target, SVG.xlink) } - // Opacity - , opacity: function(value) { - return this.attr('opacity', value) + // Link target attribute + , target: function(target) { + return this.attr('target', target) } - }) + } - SVG.extend(SVG.Rect, SVG.Ellipse, SVG.Circle, SVG.FX, { - // Add x and y radius - radius: function(x, y) { - return this.rx(x).ry(y == null ? x : y) + // 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) { + var link = new SVG.A + + if (typeof url == 'function') + url.call(link, link) + else + link.to(url) + + return this.parent().put(link).put(this) + } - SVG.extend(SVG.Path, { - // Get path length - length: function() { - return this.node.getTotalLength() +}) +SVG.Marker = SVG.invent({ + // Initialize node + create: 'marker' + + // Inherit from +, inherit: SVG.Container + + // Add class methods +, extend: { + // Set width of element + width: function(width) { + return this.attr('markerWidth', width) + } + // Set height of element + , height: function(height) { + return this.attr('markerHeight', height) } - // Get point at length - , pointAt: function(length) { - return this.node.getPointAtLength(length) + // Set marker refX and refY + , ref: function(x, y) { + return this.attr('refX', x).attr('refY', y) } - }) - - SVG.extend(SVG.Parent, SVG.Text, SVG.FX, { - // Set font - font: function(o) { - for (var k in o) - k == 'leading' ? - this.leading(o[k]) : - k == 'anchor' ? - this.attr('text-anchor', o[k]) : - k == 'size' || k == 'family' || k == 'weight' || k == 'stretch' || k == 'variant' || k == 'style' ? - this.attr('font-'+ k, o[k]) : - this.attr(k, o[k]) + // Update marker + , update: function(block) { + /* remove all content */ + this.clear() + + /* invoke passed block */ + if (typeof block == 'function') + block.call(this, this) return this } - }) - - + // Return the fill id + , toString: function() { + return 'url(#' + this.id() + ')' + } + } - SVG.Set = SVG.invent({ - // Initialize - create: function(members) { - // Set initial state - Array.isArray(members) ?this.members = members : this.clear() + // Add parent method +, construct: { + marker: function(width, height, block) { + // Create marker element in defs + return this.defs().marker(width, height, block) } + } + +}) + +SVG.extend(SVG.Defs, { + // Create marker + marker: function(width, height, block) { + // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto + return this.put(new SVG.Marker) + .size(width, height) + .ref(width / 2, height / 2) + .viewbox(0, 0, width, height) + .attr('orient', 'auto') + .update(block) + } - // Add class methods - , extend: { - // 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) - - 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 - } - // 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] - } - // Get first member - , first: function() { - return this.get(0) - } - // Get last member - , last: function() { - return this.get(this.members.length - 1) - } - // 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 - } - } +}) + +SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, SVG.Path, { + // Create and attach markers + marker: function(marker, width, height, block) { + var attr = ['marker'] + + // Build attribute name + if (marker != 'all') attr.push(marker) + attr = attr.join('-') + + // Set marker attribute + marker = arguments[1] instanceof SVG.Marker ? + arguments[1] : + this.doc().marker(width, height, block) - // Add parent method - , construct: { - // Create a new set - set: function(members) { - return new SVG.Set(members) - } - } - }) - - SVG.SetFX = SVG.invent({ - // Initialize node - create: function(set) { - /* store reference to set */ - this.set = set - } + return this.attr(attr, marker) + } - }) +}) +// Define list of available attributes for stroke and fill +var sugar = { + stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'] +, fill: ['color', 'opacity', 'rule'] +, prefix: function(t, a) { + return a == 'color' ? t : t + '-' + a + } +} + +/* Add sugar for fill and stroke */ +;['fill', 'stroke'].forEach(function(m) { + var i, extension = {} - // Alias methods - SVG.Set.inherit = function() { - var m - , methods = [] + extension[m] = function(o) { + if (typeof o == 'string' || SVG.Color.isRgb(o) || (o && typeof o.fill === 'function')) + this.attr(m, o) + + else + /* set all attributes from sugar.fill and sugar.stroke list */ + for (i = sugar[m].length - 1; i >= 0; i--) + if (o[sugar[m][i]] != null) + this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]) - /* gather shape methods */ - for(var m in SVG.Shape.prototype) - if (typeof SVG.Shape.prototype[m] == 'function' && typeof SVG.Set.prototype[m] != 'function') - methods.push(m) - - /* apply shape aliasses */ - methods.forEach(function(method) { - SVG.Set.prototype[method] = function() { - for (var i = 0, il = this.members.length; i < il; i++) - if (this.members[i] && typeof this.members[i][method] == 'function') - this.members[i][method].apply(this.members[i], arguments) - - return method == 'animate' ? (this.fx || (this.fx = new SVG.SetFX(this))) : this - } - }) - - /* clear methods for the next round */ - methods = [] - - /* gather fx methods */ - for(var m in SVG.FX.prototype) - if (typeof SVG.FX.prototype[m] == 'function' && typeof SVG.SetFX.prototype[m] != 'function') - methods.push(m) - - /* apply fx aliasses */ - methods.forEach(function(method) { - SVG.SetFX.prototype[method] = function() { - for (var i = 0, il = this.set.members.length; i < il; i++) - this.set.members[i].fx[method].apply(this.set.members[i].fx, arguments) - - return this - } - }) + return this } + SVG.extend(SVG.Element, SVG.FX, extension) +}) +SVG.extend(SVG.Element, SVG.FX, { + // Rotation + rotate: function(d, cx, cy) { + return this.transform({ rotation: d, cx: cx, cy: cy }) + } + // Skew +, skew: function(x, y) { + return this.transform({ skewX: x, skewY: y }) + } + // Scale +, scale: function(x, y, cx, cy) { + return arguments.length == 1 || arguments.length == 3 ? + this.transform({ scale: x, cx: y, cy: cx }) : + this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy }) + } + // Translate +, translate: function(x, y) { + return this.transform({ x: x, y: y }) + } + // Matrix +, matrix: function(m) { + return this.attr('transform', new SVG.Matrix(m)) + } + // Opacity +, opacity: function(value) { + return this.attr('opacity', value) + } +}) - SVG.extend(SVG.Element, { - // Store data values on svg nodes - data: function(a, v, r) { - if (typeof a == 'object') { - for (v in a) - this.data(v, a[v]) - - } else if (arguments.length < 2) { - try { - return JSON.parse(this.attr('data-' + a)) - } catch(e) { - return this.attr('data-' + a) - } - - } else { - this.attr( - 'data-' + a - , v === null ? - null : - r === true || typeof v === 'string' || typeof v === 'number' ? - v : - JSON.stringify(v) - ) - } +SVG.extend(SVG.Rect, SVG.Ellipse, SVG.Circle, SVG.FX, { + // Add x and y radius + radius: function(x, y) { + return this.rx(x).ry(y == null ? x : y) + } +}) + +SVG.extend(SVG.Path, { + // Get path length + length: function() { + return this.node.getTotalLength() + } + // Get point at length +, pointAt: function(length) { + return this.node.getPointAtLength(length) + } +}) + +SVG.extend(SVG.Parent, SVG.Text, SVG.FX, { + // Set font + font: function(o) { + for (var k in o) + k == 'leading' ? + this.leading(o[k]) : + k == 'anchor' ? + this.attr('text-anchor', o[k]) : + k == 'size' || k == 'family' || k == 'weight' || k == 'stretch' || k == 'variant' || k == 'style' ? + this.attr('font-'+ k, o[k]) : + this.attr(k, o[k]) + + return this + } +}) + + +SVG.Set = SVG.invent({ + // Initialize + create: function(members) { + // Set initial state + Array.isArray(members) ?this.members = members : this.clear() + } + + // Add class methods +, extend: { + // 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) - SVG.extend(SVG.Element, { - // Remember arbitrary data - remember: function(k, v) { - /* remember every item in an object individually */ - if (typeof arguments[0] == 'object') - for (var v in k) - this.remember(v, k[v]) - - /* retrieve memory */ - else if (arguments.length == 1) - return this.memory()[k] - - /* store memory */ - else - this.memory()[k] = v - return this } - - // Erase a given memory - , forget: function() { - if (arguments.length == 0) - this._memory = {} - else - for (var i = arguments.length - 1; i >= 0; i--) - delete this.memory()[arguments[i]] - + // 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 } - - // Initialize or return local memory object - , memory: function() { - return this._memory || (this._memory = {}) + // 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] + } + // Get first member + , first: function() { + return this.get(0) + } + // Get last member + , last: function() { + return this.get(this.members.length - 1) + } + // 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() - SVG.get = function(id) { - var node = document.getElementById(idFromReference(id) || id) - if (node) return SVG.adopt(node) - } - - // Select elements by query string - SVG.select = function(query, parent) { - return new SVG.Set( - SVG.utils.map((parent || document).querySelectorAll(query), function(node) { - return SVG.adopt(node) + /* 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.extend(SVG.Parent, { - // Scoped select method - select: function(query) { - return SVG.select(query, this.node) + // Add parent method +, construct: { + // Create a new set + set: function(members) { + return new SVG.Set(members) } + } +}) + +SVG.SetFX = SVG.invent({ + // Initialize node + create: function(set) { + /* store reference to set */ + this.set = set + } + +}) + +// Alias methods +SVG.Set.inherit = function() { + var m + , methods = [] + /* gather shape methods */ + for(var m in SVG.Shape.prototype) + if (typeof SVG.Shape.prototype[m] == 'function' && typeof SVG.Set.prototype[m] != 'function') + methods.push(m) + + /* apply shape aliasses */ + methods.forEach(function(method) { + SVG.Set.prototype[method] = function() { + for (var i = 0, il = this.members.length; i < il; i++) + if (this.members[i] && typeof this.members[i][method] == 'function') + this.members[i][method].apply(this.members[i], arguments) + + return method == 'animate' ? (this.fx || (this.fx = new SVG.SetFX(this))) : this + } }) - if (typeof define === 'function' && define.amd) - define(function() { return SVG }) - else if (typeof exports !== 'undefined') - exports.SVG = SVG + /* clear methods for the next round */ + methods = [] - function camelCase(s) { - return s.toLowerCase().replace(/-(.)/g, function(m, g) { - return g.toUpperCase() - }) - } - - // Capitalize first letter of a string - function capitalize(s) { - return s.charAt(0).toUpperCase() + s.slice(1) - } - - // Ensure to six-based hex - function fullHex(hex) { - return hex.length == 4 ? - [ '#', - hex.substring(1, 2), hex.substring(1, 2) - , hex.substring(2, 3), hex.substring(2, 3) - , hex.substring(3, 4), hex.substring(3, 4) - ].join('') : hex - } - - // Component to hex value - function compToHex(comp) { - var hex = comp.toString(16) - return hex.length == 1 ? '0' + hex : hex - } - - // Calculate proportional width and height values when necessary - function proportionalSize(box, width, height) { - if (width == null || height == null) { - if (height == null) - height = box.height / box.width * width - else if (width == null) - width = box.width / box.height * height - } - - return { - width: width - , height: height - } - } - - // Delta transform point - function deltaTransformPoint(matrix, x, y) { - return { - x: x * matrix.a + y * matrix.c + 0 - , y: x * matrix.b + y * matrix.d + 0 - } - } - - // Map matrix array to object - function arrayToMatrix(a) { - return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] } - } - - // Calculate position according to from and to - function at(o, pos) { - /* number recalculation (don't bother converting to SVG.Number for performance reasons) */ - return typeof o.from == 'number' ? - o.from + (o.to - o.from) * pos : - - /* instance recalculation */ - o instanceof SVG.Color || o instanceof SVG.Number ? o.at(pos) : - - /* for all other values wait until pos has reached 1 to return the final value */ - pos < 1 ? o.from : o.to + /* gather fx methods */ + for(var m in SVG.FX.prototype) + if (typeof SVG.FX.prototype[m] == 'function' && typeof SVG.SetFX.prototype[m] != 'function') + methods.push(m) + + /* apply fx aliasses */ + methods.forEach(function(method) { + SVG.SetFX.prototype[method] = function() { + for (var i = 0, il = this.set.members.length; i < il; i++) + this.set.members[i].fx[method].apply(this.set.members[i].fx, arguments) + + return this + } + }) +} + + + +// +SVG.extend(SVG.Element, { + // Store data values on svg nodes + data: function(a, v, r) { + if (typeof a == 'object') { + for (v in a) + this.data(v, a[v]) + + } else if (arguments.length < 2) { + try { + return JSON.parse(this.attr('data-' + a)) + } catch(e) { + return this.attr('data-' + a) + } + + } else { + this.attr( + 'data-' + a + , v === null ? + null : + r === true || typeof v === 'string' || typeof v === 'number' ? + v : + JSON.stringify(v) + ) + } + + return this } - - // PathArray Helpers - function arrayToString(a) { - for (var i = 0, il = a.length, s = ''; i < il; i++) { - s += a[i][0] - - if (a[i][1] != null) { - s += a[i][1] - - if (a[i][2] != null) { - s += ' ' - s += a[i][2] - - if (a[i][3] != null) { - s += ' ' - s += a[i][3] - s += ' ' - s += a[i][4] - - if (a[i][5] != null) { - s += ' ' - s += a[i][5] - s += ' ' - s += a[i][6] - - if (a[i][7] != null) { - s += ' ' - s += a[i][7] - } - } - } - } - } - } - - return s + ' ' +}) +SVG.extend(SVG.Element, { + // Remember arbitrary data + remember: function(k, v) { + /* remember every item in an object individually */ + if (typeof arguments[0] == 'object') + for (var v in k) + this.remember(v, k[v]) + + /* retrieve memory */ + else if (arguments.length == 1) + return this.memory()[k] + + /* store memory */ + else + this.memory()[k] = v + + return this } - - // Deep new id assignment - function assignNewId(node) { - // Do the same for SVG child nodes as well - for (var i = node.childNodes.length - 1; i >= 0; i--) - if (node.childNodes[i] instanceof SVGElement) - assignNewId(node.childNodes[i]) - - return SVG.adopt(node).id(SVG.eid(node.nodeName)) + + // Erase a given memory +, forget: function() { + if (arguments.length == 0) + this._memory = {} + else + for (var i = arguments.length - 1; i >= 0; i--) + delete this.memory()[arguments[i]] + + return this } - - // Add more bounding box properties - function fullBox(b) { - b.x2 = b.x + b.width - b.y2 = b.y + b.height - b.cx = b.x + b.width / 2 - b.cy = b.y + b.height / 2 - - return b + + // Initialize or return local memory object +, memory: function() { + return this._memory || (this._memory = {}) } - - // Get id from reference string - function idFromReference(url) { - var m = url.toString().match(SVG.regex.reference) - - if (m) return m[1] + +}) +// Method for getting an element by id +SVG.get = function(id) { + var node = document.getElementById(idFromReference(id) || id) + if (node) return SVG.adopt(node) +} + +// Select elements by query string +SVG.select = function(query, parent) { + return new SVG.Set( + SVG.utils.map((parent || document).querySelectorAll(query), function(node) { + return SVG.adopt(node) + }) + ) +} + +SVG.extend(SVG.Parent, { + // Scoped select method + select: function(query) { + return SVG.select(query, this.node) } - - // Create matrix array for looping - var abcdef = 'abcdef'.split('') - - // Shim layer with setTimeout fallback by Paul Irish - window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.msRequestAnimationFrame || - function (c) { window.setTimeout(c, 1000 / 60) } - })() - - if (typeof CustomEvent !== 'function') { - // Code from: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent - function CustomEvent (event, options) { - options = options || { bubbles: false, cancelable: false, detail: undefined } - var e = document.createEvent('CustomEvent') - e.initCustomEvent(event, options.bubbles, options.cancelable, options.detail) - return e - } - - CustomEvent.prototype = window.Event.prototype - - window.CustomEvent = CustomEvent + +}) + +// Use AMD or CommonJS if either is present +if (typeof define === 'function' && define.amd) + define(function() { return SVG }) +else if (typeof exports !== 'undefined') + exports.SVG = SVG +// Convert dash-separated-string to camelCase +function camelCase(s) { + return s.toLowerCase().replace(/-(.)/g, function(m, g) { + return g.toUpperCase() + }) +} + +// Capitalize first letter of a string +function capitalize(s) { + return s.charAt(0).toUpperCase() + s.slice(1) +} + +// Ensure to six-based hex +function fullHex(hex) { + return hex.length == 4 ? + [ '#', + hex.substring(1, 2), hex.substring(1, 2) + , hex.substring(2, 3), hex.substring(2, 3) + , hex.substring(3, 4), hex.substring(3, 4) + ].join('') : hex +} + +// Component to hex value +function compToHex(comp) { + var hex = comp.toString(16) + return hex.length == 1 ? '0' + hex : hex +} + +// Calculate proportional width and height values when necessary +function proportionalSize(box, width, height) { + if (width == null || height == null) { + if (height == null) + height = box.height / box.width * width + else if (width == null) + width = box.width / box.height * height + } + + return { + width: width + , height: height + } +} + +// Delta transform point +function deltaTransformPoint(matrix, x, y) { + return { + x: x * matrix.a + y * matrix.c + 0 + , y: x * matrix.b + y * matrix.d + 0 + } +} + +// Map matrix array to object +function arrayToMatrix(a) { + return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] } +} + +// Calculate position according to from and to +function at(o, pos) { + /* number recalculation (don't bother converting to SVG.Number for performance reasons) */ + return typeof o.from == 'number' ? + o.from + (o.to - o.from) * pos : + + /* instance recalculation */ + o instanceof SVG.Color || o instanceof SVG.Number ? o.at(pos) : + + /* for all other values wait until pos has reached 1 to return the final value */ + pos < 1 ? o.from : o.to +} + +// PathArray Helpers +function arrayToString(a) { + for (var i = 0, il = a.length, s = ''; i < il; i++) { + s += a[i][0] + + if (a[i][1] != null) { + s += a[i][1] + + if (a[i][2] != null) { + s += ' ' + s += a[i][2] + + if (a[i][3] != null) { + s += ' ' + s += a[i][3] + s += ' ' + s += a[i][4] + + if (a[i][5] != null) { + s += ' ' + s += a[i][5] + s += ' ' + s += a[i][6] + + if (a[i][7] != null) { + s += ' ' + s += a[i][7] + } + } + } + } + } + } + + return s + ' ' +} + +// Deep new id assignment +function assignNewId(node) { + // Do the same for SVG child nodes as well + for (var i = node.childNodes.length - 1; i >= 0; i--) + if (node.childNodes[i] instanceof SVGElement) + assignNewId(node.childNodes[i]) + + return SVG.adopt(node).id(SVG.eid(node.nodeName)) +} + +// Add more bounding box properties +function fullBox(b) { + b.x2 = b.x + b.width + b.y2 = b.y + b.height + b.cx = b.x + b.width / 2 + b.cy = b.y + b.height / 2 + + return b +} + +// Get id from reference string +function idFromReference(url) { + var m = url.toString().match(SVG.regex.reference) + + if (m) return m[1] +} + +// Create matrix array for looping +var abcdef = 'abcdef'.split('') + +// Shim layer with setTimeout fallback by Paul Irish +window.requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.msRequestAnimationFrame || + function (c) { window.setTimeout(c, 1000 / 60) } +})() +// Add CustomEvent to IE9 and IE10 +if (typeof CustomEvent !== 'function') { + // Code from: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent + function CustomEvent (event, options) { + options = options || { bubbles: false, cancelable: false, detail: undefined } + var e = document.createEvent('CustomEvent') + e.initCustomEvent(event, options.bubbles, options.cancelable, options.detail) + return e } -}).call(this); + CustomEvent.prototype = window.Event.prototype + + window.CustomEvent = CustomEvent +} +}).call(this); \ No newline at end of file diff --git a/dist/svg.min.js b/dist/svg.min.js index b9c49f0..956fac4 100755 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,3 +1,2 @@ -/* svg.js 1.0.0-rc.10-23-g03f358a - svg inventor adopter regex utilities default color array pointarray patharray number viewbox element boxes matrix attr transform style parent container transporter fx relative event defs group arrange mask clip gradient pattern doc spof shape symbol use rect ellipse line poly pointed path image text textpath nested hyperlink marker sugar set data memory selector loader helpers polyfill - svgjs.com/license */ -(function(){function n(e){return e.toLowerCase().replace(/-(.)/g,function(e,t){return t.toUpperCase()})}function r(e){return e.charAt(0).toUpperCase()+e.slice(1)}function i(e){return e.length==4?["#",e.substring(1,2),e.substring(1,2),e.substring(2,3),e.substring(2,3),e.substring(3,4),e.substring(3,4)].join(""):e}function s(e){var t=e.toString(16);return t.length==1?"0"+t:t}function o(e,t,n){if(t==null||n==null)n==null?n=e.height/e.width*t:t==null&&(t=e.width/e.height*n);return{width:t,height:n}}function u(e,t,n){return{x:t*e.a+n*e.c+0,y:t*e.b+n*e.d+0}}function a(e){return{a:e[0],b:e[1],c:e[2],d:e[3],e:e[4],f:e[5]}}function f(t,n){return typeof t.from=="number"?t.from+(t.to-t.from)*n:t instanceof e.Color||t instanceof e.Number?t.at(n):n<1?t.from:t.to}function l(e){for(var t=0,n=e.length,r="";t=0;n--)t.childNodes[n]instanceof SVGElement&&c(t.childNodes[n]);return e.adopt(t).id(e.eid(t.nodeName))}function h(e){return e.x2=e.x+e.width,e.y2=e.y+e.height,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}function p(t){var n=t.toString().match(e.regex.reference);if(n)return n[1]}var e=this.SVG=function(t){if(e.supported)return t=new e.Doc(t),e.parser||e.prepare(t),t};e.ns="http://www.w3.org/2000/svg",e.xmlns="http://www.w3.org/2000/xmlns/",e.xlink="http://www.w3.org/1999/xlink",e.did=1e3,e.eid=function(t){return"Svgjs"+r(t)+e.did++},e.create=function(e){var t=document.createElementNS(this.ns,e);return t.setAttribute("id",this.eid(e)),t},e.extend=function(){var t,n,r,i;t=[].slice.call(arguments),n=t.pop();for(i=t.length-1;i>=0;i--)if(t[i])for(r in n)t[i].prototype[r]=n[r];e.Set&&e.Set.inherit&&e.Set.inherit()},e.prepare=function(t){var n=document.getElementsByTagName("body")[0],r=(n?new e.Doc(n):t.nested()).size(2,0),i=e.create("path");r.node.appendChild(i),e.parser={body:n||t.parent(),draw:r.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:r.polyline().node,path:i}},e.supported=function(){return!!document.createElementNS&&!!document.createElementNS(e.ns,"svg").createSVGRect}();if(!e.supported)return!1;e.invent=function(t){var n=typeof t.create=="function"?t.create:function(){this.constructor.call(this,e.create(t.create))};return t.inherit&&(n.prototype=new t.inherit),t.extend&&e.extend(n,t.extend),t.construct&&e.extend(t.parent||e.Container,t.construct),n},e.adopt=function(t){if(t.instance)return t.instance;var n;return t.nodeName=="svg"?n=t.parentNode instanceof SVGElement?new e.Nested:new e.Doc:t.nodeName=="lineairGradient"?n=new e.Gradient("lineair"):t.nodeName=="radialGradient"?n=new e.Gradient("radial"):e[r(t.nodeName)]?n=new(e[r(t.nodeName)]):n=new e.Element(t),n.type=t.nodeName,n.node=t,t.instance=n,n instanceof e.Doc&&n.namespace().defs(),n},e.regex={unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i,isEvent:/^[\w]+:[\w]+$/},e.utils={map:function(e,t){var n,r=e.length,i=[];for(n=0;n1?1:t,new e.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),e.Color.test=function(t){return t+="",e.regex.isHex.test(t)||e.regex.isRgb.test(t)},e.Color.isRgb=function(e){return e&&typeof e.r=="number"&&typeof e.g=="number"&&typeof e.b=="number"},e.Color.isColor=function(t){return e.Color.isRgb(t)||e.Color.test(t)},e.Array=function(e,t){e=(e||[]).valueOf(),e.length==0&&t&&(e=t.valueOf()),this.value=this.parse(e)},e.extend(e.Array,{morph:function(e){this.destination=this.parse(e);if(this.value.length!=this.destination.length){var t=this.value[this.value.length-1],n=this.destination[this.destination.length-1];while(this.value.length>this.destination.length)this.destination.push(n);while(this.value.length=0;r--)this.value[r]=[this.value[r][0]+e,this.value[r][1]+t];return this},size:function(e,t){var n,r=this.bbox();for(n=this.value.length-1;n>=0;n--)this.value[n][0]=(this.value[n][0]-r.x)*e/r.width+r.x,this.value[n][1]=(this.value[n][1]-r.y)*t/r.height+r.y;return this},bbox:function(){return e.parser.poly.setAttribute("points",this.toString()),e.parser.poly.getBBox()}}),e.PathArray=function(e,t){this.constructor.call(this,e,t||[["M",0,0]])},e.PathArray.prototype=new e.Array,e.extend(e.PathArray,{toString:function(){return l(this.value)},move:function(e,t){var n=this.bbox();e-=n.x,t-=n.y;if(!isNaN(e)&&!isNaN(t))for(var r,i=this.value.length-1;i>=0;i--)r=this.value[i][0],r=="M"||r=="L"||r=="T"?(this.value[i][1]+=e,this.value[i][2]+=t):r=="H"?this.value[i][1]+=e:r=="V"?this.value[i][1]+=t:r=="C"||r=="S"||r=="Q"?(this.value[i][1]+=e,this.value[i][2]+=t,this.value[i][3]+=e,this.value[i][4]+=t,r=="C"&&(this.value[i][5]+=e,this.value[i][6]+=t)):r=="A"&&(this.value[i][6]+=e,this.value[i][7]+=t);return this},size:function(e,t){var n,r,i=this.bbox();for(n=this.value.length-1;n>=0;n--)r=this.value[n][0],r=="M"||r=="L"||r=="T"?(this.value[n][1]=(this.value[n][1]-i.x)*e/i.width+i.x,this.value[n][2]=(this.value[n][2]-i.y)*t/i.height+i.y):r=="H"?this.value[n][1]=(this.value[n][1]-i.x)*e/i.width+i.x:r=="V"?this.value[n][1]=(this.value[n][1]-i.y)*t/i.height+i.y:r=="C"||r=="S"||r=="Q"?(this.value[n][1]=(this.value[n][1]-i.x)*e/i.width+i.x,this.value[n][2]=(this.value[n][2]-i.y)*t/i.height+i.y,this.value[n][3]=(this.value[n][3]-i.x)*e/i.width+i.x,this.value[n][4]=(this.value[n][4]-i.y)*t/i.height+i.y,r=="C"&&(this.value[n][5]=(this.value[n][5]-i.x)*e/i.width+i.x,this.value[n][6]=(this.value[n][6]-i.y)*t/i.height+i.y)):r=="A"&&(this.value[n][1]=this.value[n][1]*e/i.width,this.value[n][2]=this.value[n][2]*t/i.height,this.value[n][6]=(this.value[n][6]-i.x)*e/i.width+i.x,this.value[n][7]=(this.value[n][7]-i.y)*t/i.height+i.y);return this},parse:function(t){if(t instanceof e.PathArray)return t.valueOf();var n,r,i,s,o,u,a,f,c,h,p,d=0,v=0;e.parser.path.setAttribute("d",typeof t=="string"?t:l(t)),p=e.parser.path.pathSegList;for(n=0,r=p.numberOfItems;ni/s?this.height/s:this.width/i,this.x=n,this.y=r,this.width=i,this.height=s)},e.extend(e.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),e.Element=e.invent({create:function(t){this._stroke=e.defaults.attrs.stroke;if(this.node=t)this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute("stroke")||this._stroke},extend:{x:function(t){return t!=null&&(t=new e.Number(t),t.value/=this.transform("scaleX")),this.attr("x",t)},y:function(t){return t!=null&&(t=new e.Number(t),t.value/=this.transform("scaleY")),this.attr("y",t)},cx:function(e){return e==null?this.x()+this.width()/2:this.x(e-this.width()/2)},cy:function(e){return e==null?this.y()+this.height()/2:this.y(e-this.height()/2)},move:function(e,t){return this.x(e).y(t)},center:function(e,t){return this.cx(e).cy(t)},width:function(e){return this.attr("width",e)},height:function(e){return this.attr("height",e)},size:function(t,n){var r=o(this.bbox(),t,n);return this.width(new e.Number(r.width)).height(new e.Number(r.height))},clone:function(){return c(this.node.cloneNode(!0))},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(e){return this.after(e).remove(),e},addTo:function(e){return e.put(this)},putIn:function(e){return e.add(this)},id:function(e){return this.attr("id",e)},inside:function(e,t){var n=this.bbox();return e>n.x&&t>n.y&&e=0;t--)this[d[t]]=typeof e[d[t]]=="number"?e[d[t]]:n[d[t]]},extend:{extract:function(){var e=u(this,0,1),t=u(this,1,0),n=180/Math.PI*Math.atan2(e.y,e.x)-90;return{x:this.e,y:this.f,skewX:n,skewY:180/Math.PI*Math.atan2(t.y,t.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:n}},multiply:function(t){return new e.Matrix(this.native().multiply(t.native()))},inverse:function(){return new e.Matrix(this.native().inverse())},translate:function(t,n){return new e.Matrix(this.native().translate(t||0,n||0))},scale:function(t,n,r,i){if(arguments.length==1||arguments.length==3)n=t;return arguments.length==3&&(i=r,r=n),this.multiply(new e.Matrix(1,0,0,1,r||0,i||0)).multiply(new e.Matrix(t,0,0,n,0,0)).multiply(new e.Matrix(1,0,0,1,-r||0,-i||0))},rotate:function(t,n,r){return t=e.utils.radians(t),this.multiply(new e.Matrix(1,0,0,1,n||0,r||0)).multiply(new e.Matrix(Math.cos(t),Math.sin(t),-Math.sin(t),Math.cos(t),0,0)).multiply(new e.Matrix(1,0,0,1,-n||0,-r||0))},flip:function(t){return new e.Matrix(this.native()["flip"+t.toUpperCase()]())},skew:function(t,n){return new e.Matrix(this.native().skewX(t||0).skewY(n||0))},"native":function(){var t,n=e.parser.draw.node.createSVGMatrix();for(t=d.length-1;t>=0;t--)n[d[t]]=this[d[t]];return n},toString:function(){return"matrix("+[this.a,this.b,this.c,this.d,this.e,this.f].join()+")"}},parent:e.Element,construct:{ctm:function(){return new e.Matrix(this)}}}),e.extend(e.Element,{attr:function(t,n,r){if(t==null){t={},n=this.node.attributes;for(r=n.length-1;r>=0;r--)t[n[r].nodeName]=e.regex.isNumber.test(n[r].nodeValue)?parseFloat(n[r].nodeValue):n[r].nodeValue;return t}if(typeof t=="object")for(n in t)this.attr(n,t[n]);else if(n===null)this.node.removeAttribute(t);else{if(n==null)return n=this.node.getAttribute(t),n==null?e.defaults.attrs[t]:e.regex.isNumber.test(n)?parseFloat(n):n;t=="stroke-width"?this.attr("stroke",parseFloat(n)>0?this._stroke:null):t=="stroke"&&(this._stroke=n);if(t=="fill"||t=="stroke")e.regex.isImage.test(n)&&(n=this.doc().defs().image(n,0,0)),n instanceof e.Image&&(n=this.doc().defs().pattern(0,0,function(){this.add(n)}));typeof n=="number"?n=new e.Number(n):e.Color.isColor(n)?n=new e.Color(n):Array.isArray(n)&&(n=new e.Array(n)),t=="leading"?this.leading&&this.leading(n):typeof r=="string"?this.node.setAttributeNS(r,t,n.toString()):this.node.setAttribute(t,n.toString()),this.rebuild&&(t=="font-size"||t=="x")&&this.rebuild(t,n)}return this}}),e.extend(e.Element,{transform:function(t){if(t==null)return this.ctm().extract();if(typeof t=="string")return this.ctm().extract()[t];var n=new e.Matrix(this);if(t.a!=null)n=n.multiply(new e.Matrix(t));else if(t.rotation)n=n.rotate(t.rotation,t.cx==null?this.bbox().cx:t.cx,t.cy==null?this.bbox().cy:t.cy);else if(t.scale!=null||t.scaleX!=null||t.scaleY!=null)n=n.scale(t.scale!=null?t.scale:t.scaleX!=null?t.scaleX:1,t.scale!=null?t.scale:t.scaleY!=null?t.scaleY:1,t.cx!=null?t.cx:this.bbox().x,t.cy!=null?t.cy:this.bbox().y);else if(t.skewX||t.skewY)n=n.skew(t.skewX,t.skewY);else if(t.x||t.y)n=n.translate(t.x,t.y);return this.attr("transform",n)},untransform:function(){return this.attr("transform",null)}}),e.extend(e.Element,{style:function(t,r){if(arguments.length==0)return this.node.style.cssText||"";if(arguments.length<2)if(typeof t=="object")for(r in t)this.style(r,t[r]);else{if(!e.regex.isCss.test(t))return this.node.style[n(t)];t=t.split(";");for(var i=0;i=0},index:function(e){return this.children().indexOf(e)},get:function(e){return this.children()[e]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,n){var r,i,s=this.children();for(r=0,i=s.length;r/g,"<$1$2>"),t.innerHTML=""+e+"";for(var n=t.firstChild.childNodes.length-1;n>=0;n--)t.firstChild.childNodes[n].nodeType==1&&this.node.appendChild(t.firstChild.childNodes[n]);return this}var r=this.node.cloneNode(!0);return t.appendChild(r),t.innerHTML}}),e.FX=e.invent({create:function(e){this.target=e},extend:{animate:function(t,n,r){var i,s,o,u,a=this.target,l=this;return typeof t=="object"&&(r=t.delay,n=t.ease,t=t.duration),t=t=="="?t:t==null?1e3:(new e.Number(t)).valueOf(),n=n||"<>",l.to=function(e){var t;e=e<0?0:e>1?1:e;if(i==null){i=[];for(u in l.attrs)i.push(u);if(a.morphArray&&(l._plot||i.indexOf("points")>-1)){var r,c=new a.morphArray(l._plot||l.attrs.points||a.array);l._size&&c.size(l._size.width.to,l._size.height.to),r=c.bbox(),l._x?c.move(l._x.to,r.y):l._cx&&c.move(l._cx.to-r.width/2,r.y),r=c.bbox(),l._y?c.move(r.x,l._y.to):l._cy&&c.move(r.x,l._cy.to-r.height/2),delete l._x,delete l._y,delete l._cx,delete l._cy,delete l._size,l._plot=a.array.morph(c)}}if(s==null){s=[];for(u in l.trans)s.push(u)}if(o==null){o=[];for(u in l.styles)o.push(u)}e=n=="<>"?-Math.cos(e*Math.PI)/2+.5:n==">"?Math.sin(e*Math.PI/2):n=="<"?-Math.cos(e*Math.PI/2)+1:n=="-"?e:typeof n=="function"?n(e):e,l._plot?a.plot(l._plot.at(e)):(l._x?a.x(l._x.at(e)):l._cx&&a.cx(l._cx.at(e)),l._y?a.y(l._y.at(e)):l._cy&&a.cy(l._cy.at(e)),l._size&&a.size(l._size.width.at(e),l._size.height.at(e))),l._viewbox&&a.viewbox(l._viewbox.x.at(e),l._viewbox.y.at(e),l._viewbox.width.at(e),l._viewbox.height.at(e)),l._leading&&a.leading(l._leading.at(e));for(t=i.length-1;t>=0;t--)a.attr(i[t],f(l.attrs[i[t]],e));for(t=s.length-1;t>=0;t--)a.transform(s[t],f(l.trans[s[t]],e));for(t=o.length-1;t>=0;t--)a.style(o[t],f(l.styles[o[t]],e));l._during&&l._during.call(a,e,function(t,n){return f({from:t,to:n},e)})},typeof t=="number"&&(this.timeout=setTimeout(function(){var i=(new Date).getTime();l.situation={interval:1e3/60,start:i,play:!0,finish:i+t,duration:t},l.render=function(){if(l.situation.play===!0){var i=(new Date).getTime(),s=i>l.situation.finish?1:(i-l.situation.start)/t;l.to(s),i>l.situation.finish?(l._plot&&a.plot((new e.PointArray(l._plot.destination)).settle()),l._loop===!0||typeof l._loop=="number"&&l._loop>1?(typeof l._loop=="number"&&--l._loop,l.animate(t,n,r)):l._after?l._after.apply(a,[l]):l.stop()):requestAnimFrame(l.render)}else requestAnimFrame(l.render)},l.render()},(new e.Number(r)).valueOf())),this},bbox:function(){return this.target.bbox()},attr:function(t,n){if(typeof t=="object")for(var r in t)this.attr(r,t[r]);else{var i=this.target.attr(t);this.attrs[t]=e.Color.isColor(i)?(new e.Color(i)).morph(n):e.regex.unit.test(i)?(new e.Number(i)).morph(n):{from:i,to:n}}return this},transform:function(e,t){},style:function(e,t){if(typeof e=="object")for(var n in e)this.style(n,e[n]);else this.styles[e]={from:this.target.style(e),to:t};return this},x:function(t){return this._x=(new e.Number(this.target.x())).morph(t),this},y:function(t){return this._y=(new e.Number(this.target.y())).morph(t),this},cx:function(t){return this._cx=(new e.Number(this.target.cx())).morph(t),this},cy:function(t){return this._cy=(new e.Number(this.target.cy())).morph(t),this},move:function(e,t){return this.x(e).y(t)},center:function(e,t){return this.cx(e).cy(t)},size:function(t,n){if(this.target instanceof e.Text)this.attr("font-size",t);else{var r=this.target.bbox();this._size={width:(new e.Number(r.width)).morph(t),height:(new e.Number(r.height)).morph(n)}}return this},plot:function(e){return this._plot=e,this},leading:function(t){return this.target._leading&&(this._leading=(new e.Number(this.target._leading)).morph(t)),this},viewbox:function(t,n,r,i){if(this.target instanceof e.Container){var s=this.target.viewbox();this._viewbox={x:(new e.Number(s.x)).morph(t),y:(new e.Number(s.y)).morph(n),width:(new e.Number(s.width)).morph(r),height:(new e.Number(s.height)).morph(i)}}return this},update:function(t){return this.target instanceof e.Stop&&(t.opacity!=null&&this.attr("stop-opacity",t.opacity),t.color!=null&&this.attr("stop-color",t.color),t.offset!=null&&this.attr("offset",new e.Number(t.offset))),this},during:function(e){return this._during=e,this},after:function(e){return this._after=e,this},loop:function(e){return this._loop=e||!0,this},stop:function(e){return e===!0?(this.animate(0),this._after&&this._after.apply(this.target,[this])):(clearTimeout(this.timeout),this.attrs={},this.trans={},this.styles={},this.situation={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._plot,delete this._loop,delete this._after,delete this._during,delete this._leading,delete this._viewbox),this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var e=(new Date).getTime()-this.situation.pause;this.situation.finish+=e,this.situation.start+=e,this.situation.play=!0}return this}},parent:e.Element,construct:{animate:function(t,n,r){return(this.fx||(this.fx=new e.FX(this))).stop().animate(t,n,r)},stop:function(e){return this.fx&&this.fx.stop(e),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}}),e.extend(e.Element,e.FX,{dx:function(e){return this.x((this.target||this).x()+e)},dy:function(e){return this.y((this.target||this).y()+e)},dmove:function(e,t){return this.dx(e).dy(t)}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){e.Element.prototype[t]=function(e){var n=this;return this.node["on"+t]=typeof e=="function"?function(){return e.apply(n,arguments)}:null,this}}),e.events={},e.listeners={},e.registerEvent=function(t){e.events[t]||(e.events[t]=new v(t))},e.on=function(t,n,r){var i=r.bind(t.instance||t);e.listeners[r]=i,t.addEventListener(n,i,!1)},e.off=function(t,n,r){t.removeEventListener(n,e.listeners[r],!1),delete e.listeners[r]},e.extend(e.Element,{on:function(t,n){return e.on(this.node,t,n),this},off:function(t,n){return e.off(this.node,t,n),this},fire:function(t,n){return e.events[t].detail=n,this.node.dispatchEvent(e.events[t]),delete e.events[t].detail,this}}),e.Defs=e.invent({create:"defs",inherit:e.Container}),e.G=e.invent({create:"g",inherit:e.Container,extend:{x:function(e){return e==null?this.transform("x"):this.transform({x:e})},y:function(e){return e==null?this.transform("y"):this.transform({y:e})},cx:function(e){return e==null?this.bbox().cx:this.x(e-this.bbox().width/2)},cy:function(e){return e==null?this.bbox().cy:this.y(e-this.bbox().height/2)}},construct:{group:function(){return this.put(new e.G)}}}),e.extend(e.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,n=this.parent();return n.removeElement(this).add(this,t),n instanceof e.Doc&&n.node.appendChild(n.defs().node),this},backward:function(){var e=this.position();return e>0&&this.parent().removeElement(this).add(this,e-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof e.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(e){e.remove();var t=this.position();return this.parent().add(e,t),this},after:function(e){e.remove();var t=this.position();return this.parent().add(e,t+1),this}}),e.Mask=e.invent({create:function(){this.constructor.call(this,e.create("mask")),this.targets=[]},inherit:e.Container,extend:{remove:function(){for(var e=this.targets.length-1;e>=0;e--)this.targets[e]&&this.targets[e].unmask();return delete this.targets,this.parent().removeElement(this),this}},construct:{mask:function(){return this.defs().put(new e.Mask)}}}),e.extend(e.Element,{maskWith:function(t){return this.masker=t instanceof e.Mask?t:this.parent().mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),e.ClipPath=e.invent({create:function(){this.constructor.call(this,e.create("clipPath")),this.targets=[]},inherit:e.Container,extend:{remove:function(){for(var e=this.targets.length-1;e>=0;e--)this.targets[e]&&this.targets[e].unclip();return delete this.targets,this.parent().removeElement(this),this}},construct:{clip:function(){return this.defs().put(new e.ClipPath)}}}),e.extend(e.Element,{clipWith:function(t){return this.clipper=t instanceof e.ClipPath?t:this.parent().clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),e.Gradient=e.invent({create:function(t){this.constructor.call(this,e.create(t+"Gradient")),this.type=t},inherit:e.Container,extend:{from:function(t,n){return this.type=="radial"?this.attr({fx:new e.Number(t),fy:new e.Number(n)}):this.attr({x1:new e.Number(t),y1:new e.Number(n)})},to:function(t,n){return this.type=="radial"?this.attr({cx:new e.Number(t),cy:new e.Number(n)}):this.attr({x2:new e.Number(t),y2:new e.Number(n)})},radius:function(t){return this.type=="radial"?this.attr({r:new e.Number(t)}):this},at:function(t,n,r){return this.put(new e.Stop).update(t,n,r)},update:function(e){return this.clear(),typeof e=="function"&&e.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()}},construct:{gradient:function(e,t){return this.defs().gradient(e,t)}}}),e.extend(e.Defs,{gradient:function(t,n){return this.put(new e.Gradient(t)).update(n)}}),e.Stop=e.invent({create:"stop",inherit:e.Element,extend:{update:function(t){if(typeof t=="number"||t instanceof e.Number)t={offset:arguments[0],color:arguments[1],opacity:arguments[2]};return t.opacity!=null&&this.attr("stop-opacity",t.opacity),t.color!=null&&this.attr("stop-color",t.color),t.offset!=null&&this.attr("offset",new e.Number(t.offset)),this}}}),e.Pattern=e.invent({create:"pattern",inherit:e.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(e){return this.clear(),typeof e=="function"&&e.call(this,this),this},toString:function(){return this.fill()}},construct:{pattern:function(e,t,n){return this.defs().pattern(e,t,n)}}}),e.extend(e.Defs,{pattern:function(t,n,r){return this.put(new e.Pattern).update(r).attr({x:0,y:0,width:t,height:n,patternUnits:"userSpaceOnUse"})}}),e.Doc=e.invent({create:function(t){t&&(t=typeof t=="string"?document.getElementById(t):t,t.nodeName=="svg"?this.constructor.call(this,t):(this.constructor.call(this,e.create("svg")),t.appendChild(this.node)),this.namespace() -.size("100%","100%").defs())},inherit:e.Container,extend:{namespace:function(){return this.attr({xmlns:e.ns,version:"1.1"}).attr("xmlns:xlink",e.xlink,e.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=e.adopt(t):this._defs=new e.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return this.node.parentNode.nodeName=="#document"?null:this.node.parentNode}}}),e.extend(e.Doc,{spof:function(){if(this.doSpof){var e=this.node.getScreenCTM();e&&this.style("left",-e.e%1+"px").style("top",-e.f%1+"px")}return this},fixSubPixelOffset:function(){var t=this;return this.doSpof=!0,e.on(window,"resize",function(){t.spof()}),this.spof()}}),e.Shape=e.invent({create:function(e){this.constructor.call(this,e)},inherit:e.Element}),e.Symbol=e.invent({create:"symbol",inherit:e.Container,construct:{symbol:function(){return this.defs().put(new e.Symbol)}}}),e.Use=e.invent({create:"use",inherit:e.Shape,extend:{element:function(t){return this.target=t,this.attr("href","#"+t,e.xlink)}},construct:{use:function(t){return this.put(new e.Use).element(t)}}}),e.Rect=e.invent({create:"rect",inherit:e.Shape,construct:{rect:function(t,n){return this.put((new e.Rect).size(t,n))}}}),e.Circle=e.invent({create:"circle",inherit:e.Shape,construct:{circle:function(t){return this.put(new e.Circle).rx((new e.Number(t)).divide(2)).move(0,0)}}}),e.extend(e.Circle,e.FX,{rx:function(e){return this.attr("r",e)},ry:function(e){return this.rx(e)}}),e.Ellipse=e.invent({create:"ellipse",inherit:e.Shape,construct:{ellipse:function(t,n){return this.put(new e.Ellipse).size(t,n).move(0,0)}}}),e.extend(e.Ellipse,e.Rect,e.FX,{rx:function(e){return this.attr("rx",e)},ry:function(e){return this.attr("ry",e)}}),e.extend(e.Circle,e.Ellipse,{x:function(e){return e==null?this.cx()-this.rx():this.cx(e+this.rx())},y:function(e){return e==null?this.cy()-this.ry():this.cy(e+this.ry())},cx:function(t){return t==null?this.attr("cx"):this.attr("cx",(new e.Number(t)).divide(this.transform("scaleX")))},cy:function(t){return t==null?this.attr("cy"):this.attr("cy",(new e.Number(t)).divide(this.transform("scaleY")))},width:function(t){return t==null?this.rx()*2:this.rx((new e.Number(t)).divide(2))},height:function(t){return t==null?this.ry()*2:this.ry((new e.Number(t)).divide(2))},size:function(t,n){var r=o(this.bbox(),t,n);return this.rx((new e.Number(r.width)).divide(2)).ry((new e.Number(r.height)).divide(2))}}),e.Line=e.invent({create:"line",inherit:e.Shape,extend:{array:function(){return new e.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,n,r,i){return arguments.length==4?t={x1:t,y1:n,x2:r,y2:i}:t=(new e.PointArray(t)).toLine(),this.attr(t)},move:function(e,t){return this.attr(this.array().move(e,t).toLine())},size:function(e,t){var n=o(this.bbox(),e,t);return this.attr(this.array().size(n.width,n.height).toLine())}},construct:{line:function(t,n,r,i){return this.put(new e.Line).plot(t,n,r,i)}}}),e.Polyline=e.invent({create:"polyline",inherit:e.Shape,construct:{polyline:function(t){return this.put(new e.Polyline).plot(t)}}}),e.Polygon=e.invent({create:"polygon",inherit:e.Shape,construct:{polygon:function(t){return this.put(new e.Polygon).plot(t)}}}),e.extend(e.Polyline,e.Polygon,{array:function(){return this._array||(this._array=new e.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new e.PointArray(t))},move:function(e,t){return this.attr("points",this.array().move(e,t))},size:function(e,t){var n=o(this.bbox(),e,t);return this.attr("points",this.array().size(n.width,n.height))}}),e.extend(e.Line,e.Polyline,e.Polygon,{morphArray:e.PointArray,x:function(e){return e==null?this.bbox().x:this.move(e,this.bbox().y)},y:function(e){return e==null?this.bbox().y:this.move(this.bbox().x,e)},width:function(e){var t=this.bbox();return e==null?t.width:this.size(e,t.height)},height:function(e){var t=this.bbox();return e==null?t.height:this.size(t.width,e)}}),e.Path=e.invent({create:"path",inherit:e.Shape,extend:{morphArray:e.PathArray,array:function(){return this._array||(this._array=new e.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new e.PathArray(t))},move:function(e,t){return this.attr("d",this.array().move(e,t))},x:function(e){return e==null?this.bbox().x:this.move(e,this.bbox().y)},y:function(e){return e==null?this.bbox().y:this.move(this.bbox().x,e)},size:function(e,t){var n=o(this.bbox(),e,t);return this.attr("d",this.array().size(n.width,n.height))},width:function(e){return e==null?this.bbox().width:this.size(e,this.bbox().height)},height:function(e){return e==null?this.bbox().height:this.size(this.bbox().width,e)}},construct:{path:function(t){return this.put(new e.Path).plot(t)}}}),e.Image=e.invent({create:"image",inherit:e.Shape,extend:{load:function(t){if(!t)return this;var n=this,r=document.createElement("img");return r.onload=function(){var i=n.doc(e.Pattern);n.width()==0&&n.height()==0&&n.size(r.width,r.height),i&&i.width()==0&&i.height()==0&&i.size(n.width(),n.height()),typeof n._loaded=="function"&&n._loaded.call(n,{width:r.width,height:r.height,ratio:r.width/r.height,url:t})},this.attr("href",r.src=this.src=t,e.xlink)},loaded:function(e){return this._loaded=e,this}},construct:{image:function(t,n,r){return this.put(new e.Image).load(t).size(n||0,r||n||0)}}}),e.Text=e.invent({create:function(){this.constructor.call(this,e.create("text")),this._leading=new e.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",e.defaults.attrs["font-family"])},inherit:e.Shape,extend:{x:function(e){return e==null?this.attr("x"):(this.textPath||this.lines.each(function(){this.newLined&&this.x(e)}),this.attr("x",e))},y:function(e){var t=this.attr("y"),n=typeof t=="number"?t-this.bbox().y:0;return e==null?typeof t=="number"?t-n:t:this.attr("y",typeof e=="number"?e+n:e)},cx:function(e){return e==null?this.bbox().cx:this.x(e-this.bbox().width/2)},cy:function(e){return e==null?this.bbox().cy:this.y(e-this.bbox().height/2)},text:function(e){if(typeof e=="undefined")return this.content;this.clear().build(!0);if(typeof e=="function")e.call(this,this);else{e=(this.content=e).split("\n");for(var t=0,n=e.length;t=0;r--)i[t[n][r]]!=null&&this.attr(t.prefix(n,t[n][r]),i[t[n][r]]);return this},e.extend(e.Element,e.FX,i)}),e.extend(e.Element,e.FX,{rotate:function(e,t,n){return this.transform({rotation:e,cx:t,cy:n})},skew:function(e,t){return this.transform({skewX:e,skewY:t})},scale:function(e,t,n,r){return arguments.length==1||arguments.length==3?this.transform({scale:e,cx:t,cy:n}):this.transform({scaleX:e,scaleY:t,cx:n,cy:r})},translate:function(e,t){return this.transform({x:e,y:t})},matrix:function(t){return this.attr("transform",new e.Matrix(t))},opacity:function(e){return this.attr("opacity",e)}}),e.extend(e.Rect,e.Ellipse,e.Circle,e.FX,{radius:function(e,t){return this.rx(e).ry(t==null?e:t)}}),e.extend(e.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(e){return this.node.getPointAtLength(e)}}),e.extend(e.Parent,e.Text,e.FX,{font:function(e){for(var t in e)t=="leading"?this.leading(e[t]):t=="anchor"?this.attr("text-anchor",e[t]):t=="size"||t=="family"||t=="weight"||t=="stretch"||t=="variant"||t=="style"?this.attr("font-"+t,e[t]):this.attr(t,e[t]);return this}}),e.Set=e.invent({create:function(e){Array.isArray(e)?this.members=e:this.clear()},extend:{add:function(){var e,t,n=[].slice.call(arguments);for(e=0,t=n.length;e-1&&this.members.splice(t,1),this},each:function(e){for(var t=0,n=this.members.length;t=0},index:function(e){return this.members.indexOf(e)},get:function(e){return this.members[e]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new e.BBox;if(this.members.length==0)return t;var n=this.members[0].rbox();return t.x=n.x,t.y=n.y,t.width=n.width,t.height=n.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new e.Set(t)}}}),e.SetFX=e.invent({create:function(e){this.set=e}}),e.Set.inherit=function(){var t,n=[];for(var t in e.Shape.prototype)typeof e.Shape.prototype[t]=="function"&&typeof e.Set.prototype[t]!="function"&&n.push(t);n.forEach(function(t){e.Set.prototype[t]=function(){for(var n=0,r=this.members.length;n=0;e--)delete this.memory()[arguments[e]];return this},memory:function(){return this._memory||(this._memory={})}}),e.get=function(t){var n=document.getElementById(p(t)||t);if(n)return e.adopt(n)},e.select=function(t,n){return new e.Set(e.utils.map((n||document).querySelectorAll(t),function(t){return e.adopt(t)}))},e.extend(e.Parent,{select:function(t){return e.select(t,this.node)}}),typeof define=="function"&&define.amd?define(function(){return e}):typeof exports!="undefined"&&(exports.SVG=e);var d="abcdef".split("");window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1e3/60)}}();if(typeof v!="function"){function v(e,t){t=t||{bubbles:!1,cancelable:!1,detail:undefined};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),n}v.prototype=window.Event.prototype,window.CustomEvent=v}}).call(this); \ No newline at end of file +/*! SVG.js v1.0.0-rc.10 MIT*/(function(){function t(t){return t.toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()})}function e(t){return t.charAt(0).toUpperCase()+t.slice(1)}function n(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t}function i(t){var e=t.toString(16);return 1==e.length?"0"+e:e}function r(t,e,n){return(null==e||null==n)&&(null==n?n=t.height/t.width*e:null==e&&(e=t.width/t.height*n)),{width:e,height:n}}function s(t,e,n){return{x:e*t.a+n*t.c+0,y:e*t.b+n*t.d+0}}function h(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function o(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:t instanceof d.Color||t instanceof d.Number?t.at(e):1>e?t.from:t.to}function a(t){for(var e=0,n=t.length,i="";n>e;e++)i+=t[e][0],null!=t[e][1]&&(i+=t[e][1],null!=t[e][2]&&(i+=" ",i+=t[e][2],null!=t[e][3]&&(i+=" ",i+=t[e][3],i+=" ",i+=t[e][4],null!=t[e][5]&&(i+=" ",i+=t[e][5],i+=" ",i+=t[e][6],null!=t[e][7]&&(i+=" ",i+=t[e][7])))));return i+" "}function u(t){for(var e=t.childNodes.length-1;e>=0;e--)t.childNodes[e]instanceof SVGElement&&u(t.childNodes[e]);return d.adopt(t).id(d.eid(t.nodeName))}function l(t){return t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function c(t){var e=t.toString().match(d.regex.reference);return e?e[1]:void 0}function f(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var n=document.createEvent("CustomEvent");return n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n}var d=this.SVG=function(t){return d.supported?(t=new d.Doc(t),d.parser||d.prepare(t),t):void 0};if(d.ns="http://www.w3.org/2000/svg",d.xmlns="http://www.w3.org/2000/xmlns/",d.xlink="http://www.w3.org/1999/xlink",d.did=1e3,d.eid=function(t){return"Svgjs"+e(t)+d.did++},d.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},d.extend=function(){var t,e,n,i;for(t=[].slice.call(arguments),e=t.pop(),i=t.length-1;i>=0;i--)if(t[i])for(n in e)t[i].prototype[n]=e[n];d.Set&&d.Set.inherit&&d.Set.inherit()},d.prepare=function(t){var e=document.getElementsByTagName("body")[0],n=(e?new d.Doc(e):t.nested()).size(2,0),i=d.create("path");n.node.appendChild(i),d.parser={body:e||t.parent(),draw:n.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:n.polyline().node,path:i}},d.supported=function(){return!!document.createElementNS&&!!document.createElementNS(d.ns,"svg").createSVGRect}(),!d.supported)return!1;d.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,d.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&d.extend(e,t.extend),t.construct&&d.extend(t.parent||d.Container,t.construct),e},d.adopt=function(t){if(t.instance)return t.instance;var n;return n="svg"==t.nodeName?t.parentNode instanceof SVGElement?new d.Nested:new d.Doc:"lineairGradient"==t.nodeName?new d.Gradient("lineair"):"radialGradient"==t.nodeName?new d.Gradient("radial"):d[e(t.nodeName)]?new(d[e(t.nodeName)]):new d.Element(t),n.type=t.nodeName,n.node=t,t.instance=n,n instanceof d.Doc&&n.namespace().defs(),n},d.regex={unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i,isEvent:/^[\w]+:[\w]+$/},d.utils={map:function(t,e){var n,i=t.length,r=[];for(n=0;i>n;n++)r.push(e(t[n]));return r},radians:function(t){return t%360*Math.PI/180},degrees:function(t){return 180*t/Math.PI%360}},d.defaults={attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},trans:{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}},d.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?d.regex.isRgb.test(t)?(e=d.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):d.regex.isHex.test(t)&&(e=d.regex.hex.exec(n(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)},d.extend(d.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+i(this.r)+i(this.g)+i(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new d.Color(t),this},at:function(t){return this.destination?(t=0>t?0:t>1?1:t,new d.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}}),d.Color.test=function(t){return t+="",d.regex.isHex.test(t)||d.regex.isRgb.test(t)},d.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},d.Color.isColor=function(t){return d.Color.isRgb(t)||d.Color.test(t)},d.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},d.extend(d.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],n=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(n);for(;this.value.lengtht;t++)-1==n.indexOf(this.value[t])&&n.push(this.value[t]);return this.value=n},at:function(t){if(!this.destination)return this;for(var e=0,n=this.value.length,i=[];n>e;e++)i.push(this.value[e]+(this.destination[e]-this.value[e])*t);return new d.Array(i)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)},split:function(t){return t.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")},reverse:function(){return this.value.reverse(),this}}),d.PointArray=function(t,e){this.constructor.call(this,t,e||[[0,0]])},d.PointArray.prototype=new d.Array,d.extend(d.PointArray,{toString:function(){for(var t=0,e=this.value.length,n=[];e>t;t++)n.push(this.value[t].join(","));return n.join(" ")},toLine:function(){return{x1:this.value[0][0],y1:this.value[0][1],x2:this.value[1][0],y2:this.value[1][1]}},at:function(t){if(!this.destination)return this;for(var e=0,n=this.value.length,i=[];n>e;e++)i.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 d.PointArray(i)},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;t=this.split(t);for(var e,n=0,i=t.length,r=[];i>n;n++)e=t[n].split(","),r.push([parseFloat(e[0]),parseFloat(e[1])]);return r},move:function(t,e){var n=this.bbox();if(t-=n.x,e-=n.y,!isNaN(t)&&!isNaN(e))for(var i=this.value.length-1;i>=0;i--)this.value[i]=[this.value[i][0]+t,this.value[i][1]+e];return this},size:function(t,e){var n,i=this.bbox();for(n=this.value.length-1;n>=0;n--)this.value[n][0]=(this.value[n][0]-i.x)*t/i.width+i.x,this.value[n][1]=(this.value[n][1]-i.y)*e/i.height+i.y;return this},bbox:function(){return d.parser.poly.setAttribute("points",this.toString()),d.parser.poly.getBBox()}}),d.PathArray=function(t,e){this.constructor.call(this,t,e||[["M",0,0]])},d.PathArray.prototype=new d.Array,d.extend(d.PathArray,{toString:function(){return a(this.value)},move:function(t,e){var n=this.bbox();if(t-=n.x,e-=n.y,!isNaN(t)&&!isNaN(e))for(var i,r=this.value.length-1;r>=0;r--)i=this.value[r][0],"M"==i||"L"==i||"T"==i?(this.value[r][1]+=t,this.value[r][2]+=e):"H"==i?this.value[r][1]+=t:"V"==i?this.value[r][1]+=e:"C"==i||"S"==i||"Q"==i?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"==i&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"==i&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var n,i,r=this.bbox();for(n=this.value.length-1;n>=0;n--)i=this.value[n][0],"M"==i||"L"==i||"T"==i?(this.value[n][1]=(this.value[n][1]-r.x)*t/r.width+r.x,this.value[n][2]=(this.value[n][2]-r.y)*e/r.height+r.y):"H"==i?this.value[n][1]=(this.value[n][1]-r.x)*t/r.width+r.x:"V"==i?this.value[n][1]=(this.value[n][1]-r.y)*e/r.height+r.y:"C"==i||"S"==i||"Q"==i?(this.value[n][1]=(this.value[n][1]-r.x)*t/r.width+r.x,this.value[n][2]=(this.value[n][2]-r.y)*e/r.height+r.y,this.value[n][3]=(this.value[n][3]-r.x)*t/r.width+r.x,this.value[n][4]=(this.value[n][4]-r.y)*e/r.height+r.y,"C"==i&&(this.value[n][5]=(this.value[n][5]-r.x)*t/r.width+r.x,this.value[n][6]=(this.value[n][6]-r.y)*e/r.height+r.y)):"A"==i&&(this.value[n][1]=this.value[n][1]*t/r.width,this.value[n][2]=this.value[n][2]*e/r.height,this.value[n][6]=(this.value[n][6]-r.x)*t/r.width+r.x,this.value[n][7]=(this.value[n][7]-r.y)*e/r.height+r.y);return this},parse:function(t){if(t instanceof d.PathArray)return t.valueOf();var e,n,i,r,s,h,o,u,l,c,f,p=0,m=0;for(d.parser.path.setAttribute("d","string"==typeof t?t:a(t)),f=d.parser.path.pathSegList,e=0,n=f.numberOfItems;n>e;++e)c=f.getItem(e),l=c.pathSegTypeAsLetter,"M"==l||"L"==l||"H"==l||"V"==l||"C"==l||"S"==l||"Q"==l||"T"==l||"A"==l?("x"in c&&(p=c.x),"y"in c&&(m=c.y)):("x1"in c&&(s=p+c.x1),"x2"in c&&(o=p+c.x2),"y1"in c&&(h=m+c.y1),"y2"in c&&(u=m+c.y2),"x"in c&&(p+=c.x),"y"in c&&(m+=c.y),"m"==l?f.replaceItem(d.parser.path.createSVGPathSegMovetoAbs(p,m),e):"l"==l?f.replaceItem(d.parser.path.createSVGPathSegLinetoAbs(p,m),e):"h"==l?f.replaceItem(d.parser.path.createSVGPathSegLinetoHorizontalAbs(p),e):"v"==l?f.replaceItem(d.parser.path.createSVGPathSegLinetoVerticalAbs(m),e):"c"==l?f.replaceItem(d.parser.path.createSVGPathSegCurvetoCubicAbs(p,m,s,h,o,u),e):"s"==l?f.replaceItem(d.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(p,m,o,u),e):"q"==l?f.replaceItem(d.parser.path.createSVGPathSegCurvetoQuadraticAbs(p,m,s,h),e):"t"==l?f.replaceItem(d.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(p,m),e):"a"==l?f.replaceItem(d.parser.path.createSVGPathSegArcAbs(p,m,c.r1,c.r2,c.angle,c.largeArcFlag,c.sweepFlag),e):("z"==l||"Z"==l)&&(p=i,m=r)),("M"==l||"m"==l)&&(i=p,r=m);for(t=[],f=d.parser.path.pathSegList,e=0,n=f.numberOfItems;n>e;++e)c=f.getItem(e),l=c.pathSegTypeAsLetter,p=[l],"M"==l||"L"==l||"T"==l?p.push(c.x,c.y):"H"==l?p.push(c.x):"V"==l?p.push(c.y):"C"==l?p.push(c.x1,c.y1,c.x2,c.y2,c.x,c.y):"S"==l?p.push(c.x2,c.y2,c.x,c.y):"Q"==l?p.push(c.x1,c.y1,c.x,c.y):"A"==l&&p.push(c.r1,c.r2,c.angle,0|c.largeArcFlag,0|c.sweepFlag,c.x,c.y),t.push(p);return t},bbox:function(){return d.parser.path.setAttribute("d",this.toString()),d.parser.path.getBBox()}}),d.Number=function(t){if(this.value=0,this.unit="","number"==typeof t)this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;else if("string"==typeof t){var e=t.match(d.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]?this.value/=100:"s"==e[2]&&(this.value*=1e3),this.unit=e[2])}else t instanceof d.Number&&(this.value=t.value,this.unit=t.unit)},d.extend(d.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:"s"==this.unit?this.value/1e3:this.value)+this.unit},valueOf:function(){return this.value},plus:function(t){return this.value=this+new d.Number(t),this},minus:function(t){return this.plus(-new d.Number(t))},times:function(t){return this.value=this*new d.Number(t),this},divide:function(t){return this.value=this/new d.Number(t),this},to:function(t){return"string"==typeof t&&(this.unit=t),this},morph:function(t){return this.destination=new d.Number(t),this},at:function(t){return this.destination?new d.Number(this.destination).minus(this).times(t).plus(this):this}}),d.ViewBox=function(t){var e,n,i,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g),u=t,l=t;for(i=new d.Number(t.width()),r=new d.Number(t.height());"%"==i.unit;)s*=i.value,i=new d.Number(u instanceof d.Doc?u.parent().offsetWidth:u.parent().width()),u=u.parent();for(;"%"==r.unit;)h*=r.value,r=new d.Number(l instanceof d.Doc?l.parent().offsetHeight:l.parent().height()),l=l.parent();this.x=o.x,this.y=o.y,this.width=i*s,this.height=r*h,this.zoom=1,a&&(e=parseFloat(a[0]),n=parseFloat(a[1]),i=parseFloat(a[2]),r=parseFloat(a[3]),this.zoom=this.width/this.height>i/r?this.height/r:this.width/i,this.x=e,this.y=n,this.width=i,this.height=r)},d.extend(d.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),d.Element=d.invent({create:function(t){this._stroke=d.defaults.attrs.stroke,(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._stroke=t.getAttribute("stroke")||this._stroke)},extend:{x:function(t){return null!=t&&(t=new d.Number(t),t.value/=this.transform("scaleX")),this.attr("x",t)},y:function(t){return null!=t&&(t=new d.Number(t),t.value/=this.transform("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 n=r(this.bbox(),t,e);return this.width(new d.Number(n.width)).height(new d.Number(n.height))},clone:function(){return u(this.node.cloneNode(!0))},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},id:function(t){return this.attr("id",t)},inside:function(t,e){var n=this.bbox();return t>n.x&&e>n.y&&t=0;e--)this[m[e]]="number"==typeof t[m[e]]?t[m[e]]:n[m[e]]},extend:{extract:function(){var t=s(this,0,1),e=s(this,1,0),n=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,skewX:n,skewY:180/Math.PI*Math.atan2(e.y,e.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:n}},multiply:function(t){return new d.Matrix(this.native().multiply(t.native()))},inverse:function(){return new d.Matrix(this.native().inverse())},translate:function(t,e){return new d.Matrix(this.native().translate(t||0,e||0))},scale:function(t,e,n,i){return(1==arguments.length||3==arguments.length)&&(e=t),3==arguments.length&&(i=n,n=e),this.multiply(new d.Matrix(1,0,0,1,n||0,i||0)).multiply(new d.Matrix(t,0,0,e,0,0)).multiply(new d.Matrix(1,0,0,1,-n||0,-i||0))},rotate:function(t,e,n){return t=d.utils.radians(t),this.multiply(new d.Matrix(1,0,0,1,e||0,n||0)).multiply(new d.Matrix(Math.cos(t),Math.sin(t),-Math.sin(t),Math.cos(t),0,0)).multiply(new d.Matrix(1,0,0,1,-e||0,-n||0))},flip:function(t){return new d.Matrix(this.native()["flip"+t.toUpperCase()]())},skew:function(t,e){return new d.Matrix(this.native().skewX(t||0).skewY(e||0))},"native":function(){var t,e=d.parser.draw.node.createSVGMatrix();for(t=m.length-1;t>=0;t--)e[m[t]]=this[m[t]];return e},toString:function(){return"matrix("+[this.a,this.b,this.c,this.d,this.e,this.f].join()+")"}},parent:d.Element,construct:{ctm:function(){return new d.Matrix(this)}}}),d.extend(d.Element,{attr:function(t,e,n){if(null==t){for(t={},e=this.node.attributes,n=e.length-1;n>=0;n--)t[e[n].nodeName]=d.regex.isNumber.test(e[n].nodeValue)?parseFloat(e[n].nodeValue):e[n].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return e=this.node.getAttribute(t),null==e?d.defaults.attrs[t]:d.regex.isNumber.test(e)?parseFloat(e):e;"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),("fill"==t||"stroke"==t)&&(d.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof d.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new d.Number(e):d.Color.isColor(e)?e=new d.Color(e):Array.isArray(e)&&(e=new d.Array(e)),"leading"==t?this.leading&&this.leading(e):"string"==typeof n?this.node.setAttributeNS(n,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!=t&&"x"!=t||this.rebuild(t,e)}return this}}),d.extend(d.Element,{transform:function(t){if(null==t)return this.ctm().extract();if("string"==typeof t)return this.ctm().extract()[t];var e=new d.Matrix(this);return null!=t.a?e=e.multiply(new d.Matrix(t)):t.rotation?e=e.rotate(t.rotation,null==t.cx?this.bbox().cx:t.cx,null==t.cy?this.bbox().cy:t.cy):null!=t.scale||null!=t.scaleX||null!=t.scaleY?e=e.scale(null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,null!=t.cx?t.cx:this.bbox().x,null!=t.cy?t.cy:this.bbox().y):t.skewX||t.skewY?e=e.skew(t.skewX,t.skewY):(t.x||t.y)&&(e=e.translate(t.x,t.y)),this.attr("transform",e)},untransform:function(){return this.attr("transform",null)}}),d.extend(d.Element,{style:function(e,n){if(0==arguments.length)return this.node.style.cssText||"";if(arguments.length<2)if("object"==typeof e)for(n in e)this.style(n,e[n]);else{if(!d.regex.isCss.test(e))return this.node.style[t(e)];e=e.split(";");for(var i=0;i=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 n,i,r=this.children();for(n=0,i=r.length;i>n;n++)r[n]instanceof d.Element&&t.apply(r[n],[n,r]),e&&r[n]instanceof d.Container&&r[n].each(t,e);return this},removeElement:function(t){return this.node.removeChild(t.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,this},defs:function(){return this.doc().defs()}}}),d.Container=d.invent({create:function(t){this.constructor.call(this,t)},inherit:d.Parent,extend:{viewbox:function(t){return 0==arguments.length?new d.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),d.extend(d.Parent,d.Text,{svg:function(t){var e=document.createElement("div");if(t){t=t.replace(/\n/,"").replace(/<(\w+)([^<]+?)\/>/g,"<$1$2>"),e.innerHTML=""+t+"";for(var n=e.firstChild.childNodes.length-1;n>=0;n--)1==e.firstChild.childNodes[n].nodeType&&this.node.appendChild(e.firstChild.childNodes[n]);return this}var i=this.node.cloneNode(!0);return e.appendChild(i),e.innerHTML}}),d.FX=d.invent({create:function(t){this.target=t},extend:{animate:function(t,e,n){var i,r,s,h,a=this.target,u=this;return"object"==typeof t&&(n=t.delay,e=t.ease,t=t.duration),t="="==t?t:null==t?1e3:new d.Number(t).valueOf(),e=e||"<>",u.to=function(t){var n;if(t=0>t?0:t>1?1:t,null==i){i=[];for(h in u.attrs)i.push(h);if(a.morphArray&&(u._plot||i.indexOf("points")>-1)){var l,c=new a.morphArray(u._plot||u.attrs.points||a.array);u._size&&c.size(u._size.width.to,u._size.height.to),l=c.bbox(),u._x?c.move(u._x.to,l.y):u._cx&&c.move(u._cx.to-l.width/2,l.y),l=c.bbox(),u._y?c.move(l.x,u._y.to):u._cy&&c.move(l.x,u._cy.to-l.height/2),delete u._x,delete u._y,delete u._cx,delete u._cy,delete u._size,u._plot=a.array.morph(c)}}if(null==r){r=[];for(h in u.trans)r.push(h)}if(null==s){s=[];for(h in u.styles)s.push(h)}for(t="<>"==e?-Math.cos(t*Math.PI)/2+.5:">"==e?Math.sin(t*Math.PI/2):"<"==e?-Math.cos(t*Math.PI/2)+1:"-"==e?t:"function"==typeof e?e(t):t,u._plot?a.plot(u._plot.at(t)):(u._x?a.x(u._x.at(t)):u._cx&&a.cx(u._cx.at(t)),u._y?a.y(u._y.at(t)):u._cy&&a.cy(u._cy.at(t)),u._size&&a.size(u._size.width.at(t),u._size.height.at(t))),u._viewbox&&a.viewbox(u._viewbox.x.at(t),u._viewbox.y.at(t),u._viewbox.width.at(t),u._viewbox.height.at(t)),u._leading&&a.leading(u._leading.at(t)),n=i.length-1;n>=0;n--)a.attr(i[n],o(u.attrs[i[n]],t));for(n=r.length-1;n>=0;n--)a.transform(r[n],o(u.trans[r[n]],t));for(n=s.length-1;n>=0;n--)a.style(s[n],o(u.styles[s[n]],t));u._during&&u._during.call(a,t,function(e,n){return o({from:e,to:n},t)})},"number"==typeof t&&(this.timeout=setTimeout(function(){var i=(new Date).getTime();u.situation={interval:1e3/60,start:i,play:!0,finish:i+t,duration:t},u.render=function(){if(u.situation.play===!0){var i=(new Date).getTime(),r=i>u.situation.finish?1:(i-u.situation.start)/t;u.to(r),i>u.situation.finish?(u._plot&&a.plot(new d.PointArray(u._plot.destination).settle()),u._loop===!0||"number"==typeof u._loop&&u._loop>1?("number"==typeof u._loop&&--u._loop,u.animate(t,e,n)):u._after?u._after.apply(a,[u]):u.stop()):requestAnimFrame(u.render)}else requestAnimFrame(u.render)},u.render()},new d.Number(n).valueOf())),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var n in t)this.attr(n,t[n]);else{var i=this.target.attr(t);this.attrs[t]=d.Color.isColor(i)?new d.Color(i).morph(e):d.regex.unit.test(i)?new d.Number(i).morph(e):{from:i,to:e}}return this},transform:function(){},style:function(t,e){if("object"==typeof t)for(var n in t)this.style(n,t[n]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x=new d.Number(this.target.x()).morph(t),this},y:function(t){return this._y=new d.Number(this.target.y()).morph(t),this},cx:function(t){return this._cx=new d.Number(this.target.cx()).morph(t),this},cy:function(t){return this._cy=new d.Number(this.target.cy()).morph(t),this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof d.Text)this.attr("font-size",t);else{var n=this.target.bbox();this._size={width:new d.Number(n.width).morph(t),height:new d.Number(n.height).morph(e)}}return this},plot:function(t){return this._plot=t,this},leading:function(t){return this.target._leading&&(this._leading=new d.Number(this.target._leading).morph(t)),this},viewbox:function(t,e,n,i){if(this.target instanceof d.Container){var r=this.target.viewbox();this._viewbox={x:new d.Number(r.x).morph(t),y:new d.Number(r.y).morph(e),width:new d.Number(r.width).morph(n),height:new d.Number(r.height).morph(i)}}return this},update:function(t){return this.target instanceof d.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 d.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},loop:function(t){return this._loop=t||!0,this},stop:function(t){return t===!0?(this.animate(0),this._after&&this._after.apply(this.target,[this])):(clearTimeout(this.timeout),this.attrs={},this.trans={},this.styles={},this.situation={},delete this._x,delete this._y,delete this._cx,delete this._cy,delete this._size,delete this._plot,delete this._loop,delete this._after,delete this._during,delete this._leading,delete this._viewbox),this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var t=(new Date).getTime()-this.situation.pause;this.situation.finish+=t,this.situation.start+=t,this.situation.play=!0}return this}},parent:d.Element,construct:{animate:function(t,e,n){return(this.fx||(this.fx=new d.FX(this))).stop().animate(t,e,n)},stop:function(t){return this.fx&&this.fx.stop(t),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}}),d.extend(d.Element,d.FX,{dx:function(t){return this.x((this.target||this).x()+t)},dy:function(t){return this.y((this.target||this).y()+t)},dmove:function(t,e){return this.dx(t).dy(e)}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){d.Element.prototype[t]=function(e){var n=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(n,arguments)}:null,this}}),d.events={},d.listeners={},d.registerEvent=function(t){d.events[t]||(d.events[t]=new f(t))},d.on=function(t,e,n){var i=n.bind(t.instance||t);d.listeners[n]=i,t.addEventListener(e,i,!1)},d.off=function(t,e,n){t.removeEventListener(e,d.listeners[n],!1),delete d.listeners[n]},d.extend(d.Element,{on:function(t,e){return d.on(this.node,t,e),this},off:function(t,e){return d.off(this.node,t,e),this},fire:function(t,e){return d.events[t].detail=e,this.node.dispatchEvent(d.events[t]),delete d.events[t].detail,this}}),d.Defs=d.invent({create:"defs",inherit:d.Container}),d.G=d.invent({create:"g",inherit:d.Container,extend:{x:function(t){return this.transform(null==t?"x":{x:t})},y:function(t){return this.transform(null==t?"y":{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 d.G)}}}),d.extend(d.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,e=this.parent();return e.removeElement(this).add(this,t),e instanceof d.Doc&&e.node.appendChild(e.defs().node),this},backward:function(){var t=this.position();return t>0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof d.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent().add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent().add(t,e+1),this}}),d.Mask=d.invent({create:function(){this.constructor.call(this,d.create("mask")),this.targets=[]},inherit:d.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 d.Mask)}}}),d.extend(d.Element,{maskWith:function(t){return this.masker=t instanceof d.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)}}),d.ClipPath=d.invent({create:function(){this.constructor.call(this,d.create("clipPath")),this.targets=[]},inherit:d.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 d.ClipPath)}}}),d.extend(d.Element,{clipWith:function(t){return this.clipper=t instanceof d.ClipPath?t:this.parent().clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),d.Gradient=d.invent({create:function(t){this.constructor.call(this,d.create(t+"Gradient")),this.type=t},inherit:d.Container,extend:{from:function(t,e){return this.attr("radial"==this.type?{fx:new d.Number(t),fy:new d.Number(e)}:{x1:new d.Number(t),y1:new d.Number(e)})},to:function(t,e){return this.attr("radial"==this.type?{cx:new d.Number(t),cy:new d.Number(e)}:{x2:new d.Number(t),y2:new d.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new d.Number(t)}):this},at:function(t,e,n){return this.put(new d.Stop).update(t,e,n)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),d.extend(d.Defs,{gradient:function(t,e){return this.put(new d.Gradient(t)).update(e)}}),d.Stop=d.invent({create:"stop",inherit:d.Element,extend:{update:function(t){return("number"==typeof t||t instanceof d.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new d.Number(t.offset)),this}}}),d.Pattern=d.invent({create:"pattern",inherit:d.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()}},construct:{pattern:function(t,e,n){return this.defs().pattern(t,e,n)}}}),d.extend(d.Defs,{pattern:function(t,e,n){return this.put(new d.Pattern).update(n).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"}) +}}),d.Doc=d.invent({create:function(t){t&&(t="string"==typeof t?document.getElementById(t):t,"svg"==t.nodeName?this.constructor.call(this,t):(this.constructor.call(this,d.create("svg")),t.appendChild(this.node)),this.namespace().size("100%","100%").defs())},inherit:d.Container,extend:{namespace:function(){return this.attr({xmlns:d.ns,version:"1.1"}).attr("xmlns:xlink",d.xlink,d.xmlns)},defs:function(){if(!this._defs){var t;this._defs=(t=this.node.getElementsByTagName("defs")[0])?d.adopt(t):new d.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return"#document"==this.node.parentNode.nodeName?null:this.node.parentNode}}}),d.extend(d.Doc,{spof:function(){if(this.doSpof){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){var t=this;return this.doSpof=!0,d.on(window,"resize",function(){t.spof()}),this.spof()}}),d.Shape=d.invent({create:function(t){this.constructor.call(this,t)},inherit:d.Element}),d.Symbol=d.invent({create:"symbol",inherit:d.Container,construct:{symbol:function(){return this.defs().put(new d.Symbol)}}}),d.Use=d.invent({create:"use",inherit:d.Shape,extend:{element:function(t){return this.target=t,this.attr("href","#"+t,d.xlink)}},construct:{use:function(t){return this.put(new d.Use).element(t)}}}),d.Rect=d.invent({create:"rect",inherit:d.Shape,construct:{rect:function(t,e){return this.put((new d.Rect).size(t,e))}}}),d.Circle=d.invent({create:"circle",inherit:d.Shape,construct:{circle:function(t){return this.put(new d.Circle).rx(new d.Number(t).divide(2)).move(0,0)}}}),d.extend(d.Circle,d.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),d.Ellipse=d.invent({create:"ellipse",inherit:d.Shape,construct:{ellipse:function(t,e){return this.put(new d.Ellipse).size(t,e).move(0,0)}}}),d.extend(d.Ellipse,d.Rect,d.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),d.extend(d.Circle,d.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new d.Number(t).divide(this.transform("scaleX")))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new d.Number(t).divide(this.transform("scaleY")))},width:function(t){return null==t?2*this.rx():this.rx(new d.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new d.Number(t).divide(2))},size:function(t,e){var n=r(this.bbox(),t,e);return this.rx(new d.Number(n.width).divide(2)).ry(new d.Number(n.height).divide(2))}}),d.Line=d.invent({create:"line",inherit:d.Shape,extend:{array:function(){return new d.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,n,i){return t=4==arguments.length?{x1:t,y1:e,x2:n,y2:i}:new d.PointArray(t).toLine(),this.attr(t)},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var n=r(this.bbox(),t,e);return this.attr(this.array().size(n.width,n.height).toLine())}},construct:{line:function(t,e,n,i){return this.put(new d.Line).plot(t,e,n,i)}}}),d.Polyline=d.invent({create:"polyline",inherit:d.Shape,construct:{polyline:function(t){return this.put(new d.Polyline).plot(t)}}}),d.Polygon=d.invent({create:"polygon",inherit:d.Shape,construct:{polygon:function(t){return this.put(new d.Polygon).plot(t)}}}),d.extend(d.Polyline,d.Polygon,{array:function(){return this._array||(this._array=new d.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new d.PointArray(t))},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var n=r(this.bbox(),t,e);return this.attr("points",this.array().size(n.width,n.height))}}),d.extend(d.Line,d.Polyline,d.Polygon,{morphArray:d.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),d.Path=d.invent({create:"path",inherit:d.Shape,extend:{morphArray:d.PathArray,array:function(){return this._array||(this._array=new d.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new d.PathArray(t))},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var n=r(this.bbox(),t,e);return this.attr("d",this.array().size(n.width,n.height))},width:function(t){return null==t?this.bbox().width:this.size(t,this.bbox().height)},height:function(t){return null==t?this.bbox().height:this.size(this.bbox().width,t)}},construct:{path:function(t){return this.put(new d.Path).plot(t)}}}),d.Image=d.invent({create:"image",inherit:d.Shape,extend:{load:function(t){if(!t)return this;var e=this,n=document.createElement("img");return n.onload=function(){var i=e.doc(d.Pattern);0==e.width()&&0==e.height()&&e.size(n.width,n.height),i&&0==i.width()&&0==i.height()&&i.size(e.width(),e.height()),"function"==typeof e._loaded&&e._loaded.call(e,{width:n.width,height:n.height,ratio:n.width/n.height,url:t})},this.attr("href",n.src=this.src=t,d.xlink)},loaded:function(t){return this._loaded=t,this}},construct:{image:function(t,e,n){return this.put(new d.Image).load(t).size(e||0,n||e||0)}}}),d.Text=d.invent({create:function(){this.constructor.call(this,d.create("text")),this._leading=new d.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",d.defaults.attrs["font-family"])},inherit:d.Shape,extend:{x:function(t){return null==t?this.attr("x"):(this.textPath||this.lines.each(function(){this.newLined&&this.x(t)}),this.attr("x",t))},y:function(t){var e=this.attr("y"),n="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-n:e:this.attr("y","number"==typeof t?t+n:t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},text:function(t){if("undefined"==typeof t)return this.content;if(this.clear().build(!0),"function"==typeof t)t.call(this,this);else{t=(this.content=t).split("\n");for(var e=0,n=t.length;n>e;e++)this.tspan(t[e]).newLine()}return this.build(!1).rebuild()},size:function(t){return this.attr("font-size",t).rebuild()},leading:function(t){return null==t?this._leading:(this._leading=new d.Number(t),this.rebuild())},rebuild:function(t){if("boolean"==typeof t&&(this._rebuild=t),this._rebuild){var e=this;this.lines.each(function(){this.newLined&&(this.textPath||this.attr("x",e.attr("x")),this.attr("dy",e._leading*new d.Number(e.attr("font-size"))))}),this.fire("rebuild")}return this},build:function(t){return this._build=!!t,this}},construct:{text:function(t){return this.put(new d.Text).text(t)},plain:function(t){return this.put(new d.Text).plain(t)}}}),d.Tspan=d.invent({create:"tspan",inherit:d.Shape,extend:{text:function(t){return"function"==typeof t?t.call(this,this):this.plain(t),this},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){var t=this.doc(d.Text);return this.newLined=!0,this.dy(t._leading*t.attr("font-size")).attr("x",t.x())}}}),d.extend(d.Text,d.Tspan,{plain:function(t){return this._build===!1&&this.clear(),this.node.appendChild(document.createTextNode(this.content=t)),this},tspan:function(t){var e=(this.textPath||this).node,n=new d.Tspan;return this._build===!1&&this.clear(),e.appendChild(n.node),this instanceof d.Text&&this.lines.add(n),n.text(t)},clear:function(){for(var t=(this.textPath||this).node;t.hasChildNodes();)t.removeChild(t.lastChild);return this instanceof d.Text&&(delete this.lines,this.lines=new d.Set,this.content=""),this},length:function(){return this.node.getComputedTextLength()}}),d.registerEvent("rebuild"),d.TextPath=d.invent({create:"textPath",inherit:d.Element,parent:d.Text,construct:{path:function(t){for(this.textPath=new d.TextPath;this.node.hasChildNodes();)this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,d.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}}),d.Nested=d.invent({create:function(){this.constructor.call(this,d.create("svg")),this.style("overflow","visible")},inherit:d.Container,construct:{nested:function(){return this.put(new d.Nested)}}}),d.A=d.invent({create:"a",inherit:d.Container,extend:{to:function(t){return this.attr("href",t,d.xlink)},show:function(t){return this.attr("show",t,d.xlink)},target:function(t){return this.attr("target",t)}},construct:{link:function(t){return this.put(new d.A).to(t)}}}),d.extend(d.Element,{linkTo:function(t){var e=new d.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent().put(e).put(this)}}),d.Marker=d.invent({create:"marker",inherit:d.Container,extend:{width:function(t){return this.attr("markerWidth",t)},height:function(t){return this.attr("markerHeight",t)},ref:function(t,e){return this.attr("refX",t).attr("refY",e)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return"url(#"+this.id()+")"}},construct:{marker:function(t,e,n){return this.defs().marker(t,e,n)}}}),d.extend(d.Defs,{marker:function(t,e,n){return this.put(new d.Marker).size(t,e).ref(t/2,e/2).viewbox(0,0,t,e).attr("orient","auto").update(n)}}),d.extend(d.Line,d.Polyline,d.Polygon,d.Path,{marker:function(t,e,n,i){var r=["marker"];return"all"!=t&&r.push(t),r=r.join("-"),t=arguments[1]instanceof d.Marker?arguments[1]:this.doc().marker(e,n,i),this.attr(r,t)}});var p={stroke:["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],fill:["color","opacity","rule"],prefix:function(t,e){return"color"==e?t:t+"-"+e}};["fill","stroke"].forEach(function(t){var e,n={};n[t]=function(n){if("string"==typeof n||d.Color.isRgb(n)||n&&"function"==typeof n.fill)this.attr(t,n);else for(e=p[t].length-1;e>=0;e--)null!=n[p[t][e]]&&this.attr(p.prefix(t,p[t][e]),n[p[t][e]]);return this},d.extend(d.Element,d.FX,n)}),d.extend(d.Element,d.FX,{rotate:function(t,e,n){return this.transform({rotation:t,cx:e,cy:n})},skew:function(t,e){return this.transform({skewX:t,skewY:e})},scale:function(t,e,n,i){return this.transform(1==arguments.length||3==arguments.length?{scale:t,cx:e,cy:n}:{scaleX:t,scaleY:e,cx:n,cy:i})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.attr("transform",new d.Matrix(t))},opacity:function(t){return this.attr("opacity",t)}}),d.extend(d.Rect,d.Ellipse,d.Circle,d.FX,{radius:function(t,e){return this.rx(t).ry(null==e?t:e)}}),d.extend(d.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),d.extend(d.Parent,d.Text,d.FX,{font:function(t){for(var e in t)"leading"==e?this.leading(t[e]):"anchor"==e?this.attr("text-anchor",t[e]):"size"==e||"family"==e||"weight"==e||"stretch"==e||"variant"==e||"style"==e?this.attr("font-"+e,t[e]):this.attr(e,t[e]);return this}}),d.Set=d.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){var t,e,n=[].slice.call(arguments);for(t=0,e=n.length;e>t;t++)this.members.push(n[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,n=this.members.length;n>e;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new d.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new d.Set(t)}}}),d.SetFX=d.invent({create:function(t){this.set=t}}),d.Set.inherit=function(){var t,e=[];for(var t in d.Shape.prototype)"function"==typeof d.Shape.prototype[t]&&"function"!=typeof d.Set.prototype[t]&&e.push(t);e.forEach(function(t){d.Set.prototype[t]=function(){for(var e=0,n=this.members.length;n>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 d.SetFX(this)):this}}),e=[];for(var t in d.FX.prototype)"function"==typeof d.FX.prototype[t]&&"function"!=typeof d.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){d.SetFX.prototype[t]=function(){for(var e=0,n=this.set.members.length;n>e;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},d.extend(d.Element,{data:function(t,e,n){if("object"==typeof t)for(e in t)this.data(e,t[e]);else if(arguments.length<2)try{return JSON.parse(this.attr("data-"+t))}catch(i){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:n===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this}}),d.extend(d.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={})}}),d.get=function(t){var e=document.getElementById(c(t)||t);return e?d.adopt(e):void 0},d.select=function(t,e){return new d.Set(d.utils.map((e||document).querySelectorAll(t),function(t){return d.adopt(t)}))},d.extend(d.Parent,{select:function(t){return d.select(t,this.node)}}),"function"==typeof define&&define.amd?define(function(){return d}):"undefined"!=typeof exports&&(exports.SVG=d);var m="abcdef".split("");window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),"function"!=typeof f&&(f.prototype=window.Event.prototype,window.CustomEvent=f)}).call(this); \ No newline at end of file -- cgit v1.2.3