PageSourceSearch

https://thesteellawfirm.com/files/svg.js

js thesteellawfirm.com collected 2026-10-05 10:53:07 UTC 143,567 bytes, 5,601 lines download raw bytes

vendor: 143,567 bytes, lines 1-5601
1/*!
2* svg.js - A lightweight library for manipulating and animating SVG.
3* @version 2.7.1
4* https://svgdotjs.github.io/
5*
6* @copyright Wout Fierens <[email protected]>
7* @license MIT
8*
9* BUILT: Fri Nov 30 2018 10:01:55 GMT+0100 (GMT+01:00)
10*/;
11(function(root, factory) {
12  /* istanbul ignore next */
13  if (typeof define === 'function' && define.amd) {
14    define(function(){
15      return factory(root, root.document)
16    })
17  } else if (typeof exports === 'object') {
18    module.exports = root.document ? factory(root, root.document) : function(w){ return factory(w, w.document) }
19  } else {
20    root.SVG = factory(root, root.document)
21  }
22}(typeof window !== "undefined" ? window : this, function(window, document) {
23
24// Find global reference - uses 'this' by default when available,
25// falls back to 'window' otherwise (for bundlers like Webpack)
26var globalRef = (typeof this !== "undefined") ? this : window;
27
28// The main wrapping element
29var SVG = globalRef.SVG = function(element) {
30  if (SVG.supported) {
31    element = new SVG.Doc(element)
32
33    if(!SVG.parser.draw)
34      SVG.prepare()
35
36    return element
37  }
38}
39
40// Default namespaces
41SVG.ns    = 'http://www.w3.org/2000/svg'
42SVG.xmlns = 'http://www.w3.org/2000/xmlns/'
43SVG.xlink = 'http://www.w3.org/1999/xlink'
44SVG.svgjs = 'http://svgjs.com/svgjs'
45
46// Svg support test
47SVG.supported = (function() {
48  return !! document.createElementNS &&
49         !! document.createElementNS(SVG.ns,'svg').createSVGRect
50})()
51
52// Don't bother to continue if SVG is not supported
53if (!SVG.supported) return false
54
55// Element id sequence
56SVG.did  = 1000
57
58// Get next named element id
59SVG.eid = function(name) {
60  return 'Svgjs' + capitalize(name) + (SVG.did++)
61}
62
63// Method for element creation
64SVG.create = function(name) {
65  // create element
66  var element = document.createElementNS(this.ns, name)
67
68  // apply unique id
69  element.setAttribute('id', this.eid(name))
70
71  return element
72}
73
74// Method for extending objects
75SVG.extend = function() {
76  var modules, methods, key, i
77
78  // Get list of modules
79  modules = [].slice.call(arguments)
80
81  // Get object with extensions
82  methods = modules.pop()
83
84  for (i = modules.length - 1; i >= 0; i--)
85    if (modules[i])
86      for (key in methods)
87        modules[i].prototype[key] = methods[key]
88
89  // Make sure SVG.Set inherits any newly added methods
90  if (SVG.Set && SVG.Set.inherit)
91    SVG.Set.inherit()
92}
93
94// Invent new element
95SVG.invent = function(config) {
96  // Create element initializer
97  var initializer = typeof config.create == 'function' ?
98    config.create :
99    function() {
100      this.constructor.call(this, SVG.create(config.create))
101    }
102
103  // Inherit prototype
104  if (config.inherit)
105    initializer.prototype = new config.inherit
106
107  // Extend with methods
108  if (config.extend)
109    SVG.extend(initializer, config.extend)
110
111  // Attach construct method to parent
112  if (config.construct)
113    SVG.extend(config.parent || SVG.Container, config.construct)
114
115  return initializer
116}
117
118// Adopt existing svg elements
119SVG.adopt = function(node) {
120  // check for presence of node
121  if (!node) return null
122
123  // make sure a node isn't already adopted
124  if (node.instance) return node.instance
125
126  // initialize variables
127  var element
128
129  // adopt with element-specific settings
130  if (node.nodeName == 'svg')
131    element = node.parentNode instanceof window.SVGElement ? new SVG.Nested : new SVG.Doc
132  else if (node.nodeName == 'linearGradient')
133    element = new SVG.Gradient('linear')
134  else if (node.nodeName == 'radialGradient')
135    element = new SVG.Gradient('radial')
136  else if (SVG[capitalize(node.nodeName)])
137    element = new SVG[capitalize(node.nodeName)]
138  else
139    element = new SVG.Element(node)
140
141  // ensure references
142  element.type  = node.nodeName
143  element.node  = node
144  node.instance = element
145
146  // SVG.Class specific preparations
147  if (element instanceof SVG.Doc)
148    element.namespace().defs()
149
150  // pull svgjs data from the dom (getAttributeNS doesn't work in html5)
151  element.setData(JSON.parse(node.getAttribute('svgjs:data')) || {})
152
153  return element
154}
155
156// Initialize parsing element
157SVG.prepare = function() {
158  // Select document body and create invisible svg element
159  var body = document.getElementsByTagName('body')[0]
160    , draw = (body ? new SVG.Doc(body) : SVG.adopt(document.documentElement).nested()).size(2, 0)
161
162  // Create parser object
163  SVG.parser = {
164    body: body || document.documentElement
165  , draw: draw.style('opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden').attr('focusable', 'false').node
166  , poly: draw.polyline().node
167  , path: draw.path().node
168  , native: SVG.create('svg')
169  }
170}
171
172SVG.parser = {
173  native: SVG.create('svg')
174}
175
176document.addEventListener('DOMContentLoaded', function() {
177  if(!SVG.parser.draw)
178    SVG.prepare()
179}, false)
180
181// Storage for regular expressions
182SVG.regex = {
183  // Parse unit value
184  numberAndUnit:    /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i
185
186  // Parse hex value
187, hex:              /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
188
189  // Parse rgb value
190, rgb:              /rgb\((\d+),(\d+),(\d+)\)/
191
192  // Parse reference id
193, reference:        /#([a-z0-9\-_]+)/i
194
195  // splits a transformation chain
196, transforms:       /\)\s*,?\s*/
197
198  // Whitespace
199, whitespace:       /\s/g
200
201  // Test hex value
202, isHex:            /^#[a-f0-9]{3,6}$/i
203
204  // Test rgb value
205, isRgb:            /^rgb\(/
206
207  // Test css declaration
208, isCss:            /[^:]+:[^;]+;?/
209
210  // Test for blank string
211, isBlank:          /^(\s+)?$/
212
213  // Test for numeric string
214, isNumber:         /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i
215
216  // Test for percent value
217, isPercent:        /^-?[\d\.]+%$/
218
219  // Test for image url
220, isImage:          /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i
221
222  // split at whitespace and comma
223, delimiter:        /[\s,]+/
224
225  // The following regex are used to parse the d attribute of a path
226
227  // Matches all hyphens which are not after an exponent
228, hyphen:           /([^e])\-/gi
229
230  // Replaces and tests for all path letters
231, pathLetters:      /[MLHVCSQTAZ]/gi
232
233  // yes we need this one, too
234, isPathLetter:     /[MLHVCSQTAZ]/i
235
236  // matches 0.154.23.45
237, numbersWithDots:  /((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi
238
239  // matches .
240, dots:             /\./g
241}
242
243SVG.utils = {
244  // Map function
245  map: function(array, block) {
246    var i
247      , il = array.length
248      , result = []
249
250    for (i = 0; i < il; i++)
251      result.push(block(array[i]))
252
253    return result
254  }
255
256  // Filter function
257, filter: function(array, block) {
258    var i
259      , il = array.length
260      , result = []
261
262    for (i = 0; i < il; i++)
263      if (block(array[i]))
264        result.push(array[i])
265
266    return result
267  }
268
269  // Degrees to radians
270, radians: function(d) {
271    return d % 360 * Math.PI / 180
272  }
273
274  // Radians to degrees
275, degrees: function(r) {
276    return r * 180 / Math.PI % 360
277  }
278
279, filterSVGElements: function(nodes) {
280    return this.filter( nodes, function(el) { return el instanceof window.SVGElement })
281  }
282
283}
284
285SVG.defaults = {
286  // Default attribute values
287  attrs: {
288    // fill and stroke
289    'fill-opacity':     1
290  , 'stroke-opacity':   1
291  , 'stroke-width':     0
292  , 'stroke-linejoin':  'miter'
293  , 'stroke-linecap':   'butt'
294  , fill:               '#000000'
295  , stroke:             '#000000'
296  , opacity:            1
297    // position
298  , x:                  0
299  , y:                  0
300  , cx:                 0
301  , cy:                 0
302    // size
303  , width:              0
304  , height:             0
305    // radius
306  , r:                  0
307  , rx:                 0
308  , ry:                 0
309    // gradient
310  , offset:             0
311  , 'stop-opacity':     1
312  , 'stop-color':       '#000000'
313    // text
314  , 'font-size':        16
315  , 'font-family':      'Helvetica, Arial, sans-serif'
316  , 'text-anchor':      'start'
317  }
318
319}
320// Module for color convertions
321SVG.Color = function(color) {
322  var match
323
324  // initialize defaults
325  this.r = 0
326  this.g = 0
327  this.b = 0
328
329  if(!color) return
330
331  // parse color
332  if (typeof color === 'string') {
333    if (SVG.regex.isRgb.test(color)) {
334      // get rgb values
335      match = SVG.regex.rgb.exec(color.replace(SVG.regex.whitespace,''))
336
337      // parse numeric values
338      this.r = parseInt(match[1])
339      this.g = parseInt(match[2])
340      this.b = parseInt(match[3])
341
342    } else if (SVG.regex.isHex.test(color)) {
343      // get hex values
344      match = SVG.regex.hex.exec(fullHex(color))
345
346      // parse numeric values
347      this.r = parseInt(match[1], 16)
348      this.g = parseInt(match[2], 16)
349      this.b = parseInt(match[3], 16)
350
351    }
352
353  } else if (typeof color === 'object') {
354    this.r = color.r
355    this.g = color.g
356    this.b = color.b
357
358  }
359
360}
361
362SVG.extend(SVG.Color, {
363  // Default to hex conversion
364  toString: function() {
365    return this.toHex()
366  }
367  // Build hex value
368, toHex: function() {
369    return '#'
370      + compToHex(this.r)
371      + compToHex(this.g)
372      + compToHex(this.b)
373  }
374  // Build rgb value
375, toRgb: function() {
376    return 'rgb(' + [this.r, this.g, this.b].join() + ')'
377  }
378  // Calculate true brightness
379, brightness: function() {
380    return (this.r / 255 * 0.30)
381         + (this.g / 255 * 0.59)
382         + (this.b / 255 * 0.11)
383  }
384  // Make color morphable
385, morph: function(color) {
386    this.destination = new SVG.Color(color)
387
388    return this
389  }
390  // Get morphed color at given position
391, at: function(pos) {
392    // make sure a destination is defined
393    if (!this.destination) return this
394
395    // normalise pos
396    pos = pos < 0 ? 0 : pos > 1 ? 1 : pos
397
398    // generate morphed color
399    return new SVG.Color({
400      r: ~~(this.r + (this.destination.r - this.r) * pos)
401    , g: ~~(this.g + (this.destination.g - this.g) * pos)
402    , b: ~~(this.b + (this.destination.b - this.b) * pos)
403    })
404  }
405
406})
407
408// Testers
409
410// Test if given value is a color string
411SVG.Color.test = function(color) {
412  color += ''
413  return SVG.regex.isHex.test(color)
414      || SVG.regex.isRgb.test(color)
415}
416
417// Test if given value is a rgb object
418SVG.Color.isRgb = function(color) {
419  return color && typeof color.r == 'number'
420               && typeof color.g == 'number'
421               && typeof color.b == 'number'
422}
423
424// Test if given value is a color
425SVG.Color.isColor = function(color) {
426  return SVG.Color.isRgb(color) || SVG.Color.test(color)
427}
428// Module for array conversion
429SVG.Array = function(array, fallback) {
430  array = (array || []).valueOf()
431
432  // if array is empty and fallback is provided, use fallback
433  if (array.length == 0 && fallback)
434    array = fallback.valueOf()
435
436  // parse array
437  this.value = this.parse(array)
438}
439
440SVG.extend(SVG.Array, {
441  // Make array morphable
442  morph: function(array) {
443    this.destination = this.parse(array)
444
445    // normalize length of arrays
446    if (this.value.length != this.destination.length) {
447      var lastValue       = this.value[this.value.length - 1]
448        , lastDestination = this.destination[this.destination.length - 1]
449
450      while(this.value.length > this.destination.length)
451        this.destination.push(lastDestination)
452      while(this.value.length < this.destination.length)
453        this.value.push(lastValue)
454    }
455
456    return this
457  }
458  // Clean up any duplicate points
459, settle: function() {
460    // find all unique values
461    for (var i = 0, il = this.value.length, seen = []; i < il; i++)
462      if (seen.indexOf(this.value[i]) == -1)
463        seen.push(this.value[i])
464
465    // set new value
466    return this.value = seen
467  }
468  // Get morphed array at given position
469, at: function(pos) {
470    // make sure a destination is defined
471    if (!this.destination) return this
472
473    // generate morphed array
474    for (var i = 0, il = this.value.length, array = []; i < il; i++)
475      array.push(this.value[i] + (this.destination[i] - this.value[i]) * pos)
476
477    return new SVG.Array(array)
478  }
479  // Convert array to string
480, toString: function() {
481    return this.value.join(' ')
482  }
483  // Real value
484, valueOf: function() {
485    return this.value
486  }
487  // Parse whitespace separated string
488, parse: function(array) {
489    array = array.valueOf()
490
491    // if already is an array, no need to parse it
492    if (Array.isArray(array)) return array
493
494    return this.split(array)
495  }
496  // Strip unnecessary whitespace
497, split: function(string) {
498    return string.trim().split(SVG.regex.delimiter).map(parseFloat)
499  }
500  // Reverse array
501, reverse: function() {
502    this.value.reverse()
503
504    return this
505  }
506, clone: function() {
507    var clone = new this.constructor()
508    clone.value = array_clone(this.value)
509    return clone
510  }
511})
512// Poly points array
513SVG.PointArray = function(array, fallback) {
514  SVG.Array.call(this, array, fallback || [[0,0]])
515}
516
517// Inherit from SVG.Array
518SVG.PointArray.prototype = new SVG.Array
519SVG.PointArray.prototype.constructor = SVG.PointArray
520
521SVG.extend(SVG.PointArray, {
522  // Convert array to string
523  toString: function() {
524    // convert to a poly point string
525    for (var i = 0, il = this.value.length, array = []; i < il; i++)
526      array.push(this.value[i].join(','))
527
528    return array.join(' ')
529  }
530  // Convert array to line object
531, toLine: function() {
532    return {
533      x1: this.value[0][0]
534    , y1: this.value[0][1]
535    , x2: this.value[1][0]
536    , y2: this.value[1][1]
537    }
538  }
539  // Get morphed array at given position
540, at: function(pos) {
541    // make sure a destination is defined
542    if (!this.destination) return this
543
544    // generate morphed point string
545    for (var i = 0, il = this.value.length, array = []; i < il; i++)
546      array.push([
547        this.value[i][0] + (this.destination[i][0] - this.value[i][0]) * pos
548      , this.value[i][1] + (this.destination[i][1] - this.value[i][1]) * pos
549      ])
550
551    return new SVG.PointArray(array)
552  }
553  // Parse point string and flat array
554, parse: function(array) {
555    var points = []
556
557    array = array.valueOf()
558
559    // if it is an array
560    if (Array.isArray(array)) {
561      // and it is not flat, there is no need to parse it
562      if(Array.isArray(array[0])) {
563        // make sure to use a clone
564        return array.map(function (el) { return el.slice() })
565      } else if (array[0].x != null){
566        // allow point objects to be passed
567        return array.map(function (el) { return [el.x, el.y] })
568      }
569    } else { // Else, it is considered as a string
570      // parse points
571      array = array.trim().split(SVG.regex.delimiter).map(parseFloat)
572    }
573
574    // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
575    // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
576    if (array.length % 2 !== 0) array.pop()
577
578    // wrap points in two-tuples and parse points as floats
579    for(var i = 0, len = array.length; i < len; i = i + 2)
580      points.push([ array[i], array[i+1] ])
581
582    return points
583  }
584  // Move point string
585, move: function(x, y) {
586    var box = this.bbox()
587
588    // get relative offset
589    x -= box.x
590    y -= box.y
591
592    // move every point
593    if (!isNaN(x) && !isNaN(y))
594      for (var i = this.value.length - 1; i >= 0; i--)
595        this.value[i] = [this.value[i][0] + x, this.value[i][1] + y]
596
597    return this
598  }
599  // Resize poly string
600, size: function(width, height) {
601    var i, box = this.bbox()
602
603    // recalculate position of all points according to new size
604    for (i = this.value.length - 1; i >= 0; i--) {
605      if(box.width) this.value[i][0] = ((this.value[i][0] - box.x) * width)  / box.width  + box.x
606      if(box.height) this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y
607    }
608
609    return this
610  }
611  // Get bounding box of points
612, bbox: function() {
613    SVG.parser.poly.setAttribute('points', this.toString())
614
615    return SVG.parser.poly.getBBox()
616  }
617})
618
619var pathHandlers = {
620  M: function(c, p, p0) {
621    p.x = p0.x = c[0]
622    p.y = p0.y = c[1]
623
624    return ['M', p.x, p.y]
625  },
626  L: function(c, p) {
627    p.x = c[0]
628    p.y = c[1]
629    return ['L', c[0], c[1]]
630  },
631  H: function(c, p) {
632    p.x = c[0]
633    return ['H', c[0]]
634  },
635  V: function(c, p) {
636    p.y = c[0]
637    return ['V', c[0]]
638  },
639  C: function(c, p) {
640    p.x = c[4]
641    p.y = c[5]
642    return ['C', c[0], c[1], c[2], c[3], c[4], c[5]]
643  },
644  S: function(c, p) {
645    p.x = c[2]
646    p.y = c[3]
647    return ['S', c[0], c[1], c[2], c[3]]
648  },
649  Q: function(c, p) {
650    p.x = c[2]
651    p.y = c[3]
652    return ['Q', c[0], c[1], c[2], c[3]]
653  },
654  T: function(c, p) {
655    p.x = c[0]
656    p.y = c[1]
657    return ['T', c[0], c[1]]
658  },
659  Z: function(c, p, p0) {
660    p.x = p0.x
661    p.y = p0.y
662    return ['Z']
663  },
664  A: function(c, p) {
665    p.x = c[5]
666    p.y = c[6]
667    return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]]
668  }
669}
670
671var mlhvqtcsa = 'mlhvqtcsaz'.split('')
672
673for(var i = 0, il = mlhvqtcsa.length; i < il; ++i){
674  pathHandlers[mlhvqtcsa[i]] = (function(i){
675    return function(c, p, p0) {
676      if(i == 'H') c[0] = c[0] + p.x
677      else if(i == 'V') c[0] = c[0] + p.y
678      else if(i == 'A'){
679        c[5] = c[5] + p.x,
680        c[6] = c[6] + p.y
681      }
682      else
683        for(var j = 0, jl = c.length; j < jl; ++j) {
684          c[j] = c[j] + (j%2 ? p.y : p.x)
685        }
686
687      return pathHandlers[i](c, p, p0)
688    }
689  })(mlhvqtcsa[i].toUpperCase())
690}
691
692// Path points array
693SVG.PathArray = function(array, fallback) {
694  SVG.Array.call(this, array, fallback || [['M', 0, 0]])
695}
696
697// Inherit from SVG.Array
698SVG.PathArray.prototype = new SVG.Array
699SVG.PathArray.prototype.constructor = SVG.PathArray
700
701SVG.extend(SVG.PathArray, {
702  // Convert array to string
703  toString: function() {
704    return arrayToString(this.value)
705  }
706  // Move path string
707, move: function(x, y) {
708    // get bounding box of current situation
709    var box = this.bbox()
710
711    // get relative offset
712    x -= box.x
713    y -= box.y
714
715    if (!isNaN(x) && !isNaN(y)) {
716      // move every point
717      for (var l, i = this.value.length - 1; i >= 0; i--) {
718        l = this.value[i][0]
719
720        if (l == 'M' || l == 'L' || l == 'T')  {
721          this.value[i][1] += x
722          this.value[i][2] += y
723
724        } else if (l == 'H')  {
725          this.value[i][1] += x
726
727        } else if (l == 'V')  {
728          this.value[i][1] += y
729
730        } else if (l == 'C' || l == 'S' || l == 'Q')  {
731          this.value[i][1] += x
732          this.value[i][2] += y
733          this.value[i][3] += x
734          this.value[i][4] += y
735
736          if (l == 'C')  {
737            this.value[i][5] += x
738            this.value[i][6] += y
739          }
740
741        } else if (l == 'A')  {
742          this.value[i][6] += x
743          this.value[i][7] += y
744        }
745
746      }
747    }
748
749    return this
750  }
751  // Resize path string
752, size: function(width, height) {
753    // get bounding box of current situation
754    var i, l, box = this.bbox()
755
756    // recalculate position of all points according to new size
757    for (i = this.value.length - 1; i >= 0; i--) {
758      l = this.value[i][0]
759
760      if (l == 'M' || l == 'L' || l == 'T')  {
761        this.value[i][1] = ((this.value[i][1] - box.x) * width)  / box.width  + box.x
762        this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y
763
764      } else if (l == 'H')  {
765        this.value[i][1] = ((this.value[i][1] - box.x) * width)  / box.width  + box.x
766
767      } else if (l == 'V')  {
768        this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y
769
770      } else if (l == 'C' || l == 'S' || l == 'Q')  {
771        this.value[i][1] = ((this.value[i][1] - box.x) * width)  / box.width  + box.x
772        this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y
773        this.value[i][3] = ((this.value[i][3] - box.x) * width)  / box.width  + box.x
774        this.value[i][4] = ((this.value[i][4] - box.y) * height) / box.height + box.y
775
776        if (l == 'C')  {
777          this.value[i][5] = ((this.value[i][5] - box.x) * width)  / box.width  + box.x
778          this.value[i][6] = ((this.value[i][6] - box.y) * height) / box.height + box.y
779        }
780
781      } else if (l == 'A')  {
782        // resize radii
783        this.value[i][1] = (this.value[i][1] * width)  / box.width
784        this.value[i][2] = (this.value[i][2] * height) / box.height
785
786        // move position values
787        this.value[i][6] = ((this.value[i][6] - box.x) * width)  / box.width  + box.x
788        this.value[i][7] = ((this.value[i][7] - box.y) * height) / box.height + box.y
789      }
790
791    }
792
793    return this
794  }
795  // Test if the passed path array use the same path data commands as this path array
796, equalCommands: function(pathArray) {
797    var i, il, equalCommands
798
799    pathArray = new SVG.PathArray(pathArray)
800
801    equalCommands = this.value.length === pathArray.value.length
802    for(i = 0, il = this.value.length; equalCommands && i < il; i++) {
803      equalCommands = this.value[i][0] === pathArray.value[i][0]
804    }
805
806    return equalCommands
807  }
808  // Make path array morphable
809, morph: function(pathArray) {
810    pathArray = new SVG.PathArray(pathArray)
811
812    if(this.equalCommands(pathArray)) {
813      this.destination = pathArray
814    } else {
815      this.destination = null
816    }
817
818    return this
819  }
820  // Get morphed path array at given position
821, at: function(pos) {
822    // make sure a destination is defined
823    if (!this.destination) return this
824
825    var sourceArray = this.value
826      , destinationArray = this.destination.value
827      , array = [], pathArray = new SVG.PathArray()
828      , i, il, j, jl
829
830    // Animate has specified in the SVG spec
831    // See: https://www.w3.org/TR/SVG11/paths.html#PathElement
832    for (i = 0, il = sourceArray.length; i < il; i++) {
833      array[i] = [sourceArray[i][0]]
834      for(j = 1, jl = sourceArray[i].length; j < jl; j++) {
835        array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos
836      }
837      // For the two flags of the elliptical arc command, the SVG spec say:
838      // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true
839      // Elliptical arc command as an array followed by corresponding indexes:
840      // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
841      //   0    1   2        3                 4             5      6  7
842      if(array[i][0] === 'A') {
843        array[i][4] = +(array[i][4] != 0)
844        array[i][5] = +(array[i][5] != 0)
845      }
846    }
847
848    // Directly modify the value of a path array, this is done this way for performance
849    pathArray.value = array
850    return pathArray
851  }
852  // Absolutize and parse path to array
853, parse: function(array) {
854    // if it's already a patharray, no need to parse it
855    if (array instanceof SVG.PathArray) return array.valueOf()
856
857    // prepare for parsing
858    var i, x0, y0, s, seg, arr
859      , x = 0
860      , y = 0
861      , paramCnt = { 'M':2, 'L':2, 'H':1, 'V':1, 'C':6, 'S':4, 'Q':4, 'T':2, 'A':7, 'Z':0 }
862
863    if(typeof array == 'string'){
864
865      array = array
866        .replace(SVG.regex.numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
867        .replace(SVG.regex.pathLetters, ' $& ') // put some room between letters and numbers
868        .replace(SVG.regex.hyphen, '$1 -')      // add space before hyphen
869        .trim()                                 // trim
870        .split(SVG.regex.delimiter)   // split into array
871
872    }else{
873      array = array.reduce(function(prev, curr){
874        return [].concat.call(prev, curr)
875      }, [])
876    }
877
878    // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...]
879    var arr = []
880      , p = new SVG.Point()
881      , p0 = new SVG.Point()
882      , index = 0
883      , len = array.length
884
885    do{
886      // Test if we have a path letter
887      if(SVG.regex.isPathLetter.test(array[index])){
888        s = array[index]
889        ++index
890      // If last letter was a move command and we got no new, it defaults to [L]ine
891      }else if(s == 'M'){
892        s = 'L'
893      }else if(s == 'm'){
894        s = 'l'
895      }
896
897      arr.push(pathHandlers[s].call(null,
898          array.slice(index, (index = index + paramCnt[s.toUpperCase()])).map(parseFloat),
899          p, p0
900        )
901      )
902
903    }while(len > index)
904
905    return arr
906
907  }
908  // Get bounding box of path
909, bbox: function() {
910    SVG.parser.path.setAttribute('d', this.toString())
911
912    return SVG.parser.path.getBBox()
913  }
914
915})
916
917// Module for unit convertions
918SVG.Number = SVG.invent({
919  // Initialize
920  create: function(value, unit) {
921    // initialize defaults
922    this.value = 0
923    this.unit  = unit || ''
924
925    // parse value
926    if (typeof value === 'number') {
927      // ensure a valid numeric value
928      this.value = isNaN(value) ? 0 : !isFinite(value) ? (value < 0 ? -3.4e+38 : +3.4e+38) : value
929
930    } else if (typeof value === 'string') {
931      unit = value.match(SVG.regex.numberAndUnit)
932
933      if (unit) {
934        // make value numeric
935        this.value = parseFloat(unit[1])
936
937        // normalize
938        if (unit[5] == '%')
939          this.value /= 100
940        else if (unit[5] == 's')
941          this.value *= 1000
942
943        // store unit
944        this.unit = unit[5]
945      }
946
947    } else {
948      if (value instanceof SVG.Number) {
949        this.value = value.valueOf()
950        this.unit  = value.unit
951      }
952    }
953
954  }
955  // Add methods
956, extend: {
957    // Stringalize
958    toString: function() {
959      return (
960        this.unit == '%' ?
961          ~~(this.value * 1e8) / 1e6:
962        this.unit == 's' ?
963          this.value / 1e3 :
964          this.value
965      ) + this.unit
966    }
967  , toJSON: function() {
968      return this.toString()
969    }
970  , // Convert to primitive
971    valueOf: function() {
972      return this.value
973    }
974    // Add number
975  , plus: function(number) {
976      number = new SVG.Number(number)
977      return new SVG.Number(this + number, this.unit || number.unit)
978    }
979    // Subtract number
980  , minus: function(number) {
981      number = new SVG.Number(number)
982      return new SVG.Number(this - number, this.unit || number.unit)
983    }
984    // Multiply number
985  , times: function(number) {
986      number = new SVG.Number(number)
987      return new SVG.Number(this * number, this.unit || number.unit)
988    }
989    // Divide number
990  , divide: function(number) {
991      number = new SVG.Number(number)
992      return new SVG.Number(this / number, this.unit || number.unit)
993    }
994    // Convert to different unit
995  , to: function(unit) {
996      var number = new SVG.Number(this)
997
998      if (typeof unit === 'string')
999        number.unit = unit
1000
1001      return number
1002    }
1003    // Make number morphable
1004  , morph: function(number) {
1005      this.destination = new SVG.Number(number)
1006
1007      if(number.relative) {
1008        this.destination.value += this.value
1009      }
1010
1011      return this
1012    }
1013    // Get morphed number at given position
1014  , at: function(pos) {
1015      // Make sure a destination is defined
1016      if (!this.destination) return this
1017
1018      // Generate new morphed number
1019      return new SVG.Number(this.destination)
1020          .minus(this)
1021          .times(pos)
1022          .plus(this)
1023    }
1024
1025  }
1026})
1027
1028
1029SVG.Element = SVG.invent({
1030  // Initialize node
1031  create: function(node) {
1032    // make stroke value accessible dynamically
1033    this._stroke = SVG.defaults.attrs.stroke
1034    this._event = null
1035    this._events = {}
1036
1037    // initialize data object
1038    this.dom = {}
1039
1040    // create circular reference
1041    if (this.node = node) {
1042      this.type = node.nodeName
1043      this.node.instance = this
1044      this._events = node._events || {}
1045
1046      // store current attribute value
1047      this._stroke = node.getAttribute('stroke') || this._stroke
1048    }
1049  }
1050
1051  // Add class methods
1052, extend: {
1053    // Move over x-axis
1054    x: function(x) {
1055      return this.attr('x', x)
1056    }
1057    // Move over y-axis
1058  , y: function(y) {
1059      return this.attr('y', y)
1060    }
1061    // Move by center over x-axis
1062  , cx: function(x) {
1063      return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2)
1064    }
1065    // Move by center over y-axis
1066  , cy: function(y) {
1067      return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2)
1068    }
1069    // Move element to given x and y values
1070  , move: function(x, y) {
1071      return this.x(x).y(y)
1072    }
1073    // Move element by its center
1074  , center: function(x, y) {
1075      return this.cx(x).cy(y)
1076    }
1077    // Set width of element
1078  , width: function(width) {
1079      return this.attr('width', width)
1080    }
1081    // Set height of element
1082  , height: function(height) {
1083      return this.attr('height', height)
1084    }
1085    // Set element size to given width and height
1086  , size: function(width, height) {
1087      var p = proportionalSize(this, width, height)
1088
1089      return this
1090        .width(new SVG.Number(p.width))
1091        .height(new SVG.Number(p.height))
1092    }
1093    // Clone element
1094  , clone: function(parent) {
1095      // write dom data to the dom so the clone can pickup the data
1096      this.writeDataToDom()
1097
1098      // clone element and assign new id
1099      var clone = assignNewId(this.node.cloneNode(true))
1100
1101      // insert the clone in the given parent or after myself
1102      if(parent) parent.add(clone)
1103      else this.after(clone)
1104
1105      return clone
1106    }
1107    // Remove element
1108  , remove: function() {
1109      if (this.parent())
1110        this.parent().removeElement(this)
1111
1112      return this
1113    }
1114    // Replace element
1115  , replace: function(element) {
1116      this.after(element).remove()
1117
1118      return element
1119    }
1120    // Add element to given container and return self
1121  , addTo: function(parent) {
1122      return parent.put(this)
1123    }
1124    // Add element to given container and return container
1125  , putIn: function(parent) {
1126      return parent.add(this)
1127    }
1128    // Get / set id
1129  , id: function(id) {
1130      return this.attr('id', id)
1131    }
1132    // Checks whether the given point inside the bounding box of the element
1133  , inside: function(x, y) {
1134      var box = this.bbox()
1135
1136      return x > box.x
1137          && y > box.y
1138          && x < box.x + box.width
1139          && y < box.y + box.height
1140    }
1141    // Show element
1142  , show: function() {
1143      return this.style('display', '')
1144    }
1145    // Hide element
1146  , hide: function() {
1147      return this.style('display', 'none')
1148    }
1149    // Is element visible?
1150  , visible: function() {
1151      return this.style('display') != 'none'
1152    }
1153    // Return id on string conversion
1154  , toString: function() {
1155      return this.attr('id')
1156    }
1157    // Return array of classes on the node
1158  , classes: function() {
1159      var attr = this.attr('class')
1160
1161      return attr == null ? [] : attr.trim().split(SVG.regex.delimiter)
1162    }
1163    // Return true if class exists on the node, false otherwise
1164  , hasClass: function(name) {
1165      return this.classes().indexOf(name) != -1
1166    }
1167    // Add class to the node
1168  , addClass: function(name) {
1169      if (!this.hasClass(name)) {
1170        var array = this.classes()
1171        array.push(name)
1172        this.attr('class', array.join(' '))
1173      }
1174
1175      return this
1176    }
1177    // Remove class from the node
1178  , removeClass: function(name) {
1179      if (this.hasClass(name)) {
1180        this.attr('class', this.classes().filter(function(c) {
1181          return c != name
1182        }).join(' '))
1183      }
1184
1185      return this
1186    }
1187    // Toggle the presence of a class on the node
1188  , toggleClass: function(name) {
1189      return this.hasClass(name) ? this.removeClass(name) : this.addClass(name)
1190    }
1191    // Get referenced element form attribute value
1192  , reference: function(attr) {
1193      return SVG.get(this.attr(attr))
1194    }
1195    // Returns the parent element instance
1196  , parent: function(type) {
1197      var parent = this
1198
1199      // check for parent
1200      if(!parent.node.parentNode) return null
1201
1202      // get parent element
1203      parent = SVG.adopt(parent.node.parentNode)
1204
1205      if(!type) return parent
1206
1207      // loop trough ancestors if type is given
1208      while(parent && parent.node instanceof window.SVGElement){
1209        if(typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent
1210        if(!parent.node.parentNode || parent.node.parentNode.nodeName == '#document' || parent.node.parentNode.nodeName == '#document-fragment') return null // #759, #720
1211        parent = SVG.adopt(parent.node.parentNode)
1212      }
1213    }
1214    // Get parent document
1215  , doc: function() {
1216      return this instanceof SVG.Doc ? this : this.parent(SVG.Doc)
1217    }
1218    // return array of all ancestors of given type up to the root svg
1219  , parents: function(type) {
1220      var parents = [], parent = this
1221
1222      do{
1223        parent = parent.parent(type)
1224        if(!parent || !parent.node) break
1225
1226        parents.push(parent)
1227      } while(parent.parent)
1228
1229      return parents
1230    }
1231    // matches the element vs a css selector
1232  , matches: function(selector){
1233      return matches(this.node, selector)
1234    }
1235    // Returns the svg node to call native svg methods on it
1236  , native: function() {
1237      return this.node
1238    }
1239    // Import raw svg
1240  , svg: function(svg) {
1241      // create temporary holder
1242      var well = document.createElement('svg')
1243
1244      // act as a setter if svg is given
1245      if (svg && this instanceof SVG.Parent) {
1246        // dump raw svg
1247        well.innerHTML = '<svg>' + svg.replace(/\n/, '').replace(/<([\w:-]+)([^<]+?)\/>/g, '<$1$2></$1>') + '</svg>'
1248
1249        // transplant nodes
1250        for (var i = 0, il = well.firstChild.childNodes.length; i < il; i++)
1251          this.node.appendChild(well.firstChild.firstChild)
1252
1253      // otherwise act as a getter
1254      } else {
1255        // create a wrapping svg element in case of partial content
1256        well.appendChild(svg = document.createElement('svg'))
1257
1258        // write svgjs data to the dom
1259        this.writeDataToDom()
1260
1261        // insert a copy of this node
1262        svg.appendChild(this.node.cloneNode(true))
1263
1264        // return target element
1265        return well.innerHTML.replace(/^<svg>/, '').replace(/<\/svg>$/, '')
1266      }
1267
1268      return this
1269    }
1270  // write svgjs data to the dom
1271  , writeDataToDom: function() {
1272
1273      // dump variables recursively
1274      if(this.each || this.lines){
1275        var fn = this.each ? this : this.lines();
1276        fn.each(function(){
1277          this.writeDataToDom()
1278        })
1279      }
1280
1281      // remove previously set data
1282      this.node.removeAttribute('svgjs:data')
1283
1284      if(Object.keys(this.dom).length)
1285        this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)) // see #428
1286
1287      return this
1288    }
1289  // set given data to the elements data property
1290  , setData: function(o){
1291      this.dom = o
1292      return this
1293    }
1294  , is: function(obj){
1295      return is(this, obj)
1296    }
1297  }
1298})
1299
1300SVG.easing = {
1301  '-': function(pos){return pos}
1302, '<>':function(pos){return -Math.cos(pos * Math.PI) / 2 + 0.5}
1303, '>': function(pos){return  Math.sin(pos * Math.PI / 2)}
1304, '<': function(pos){return -Math.cos(pos * Math.PI / 2) + 1}
1305}
1306
1307SVG.morph = function(pos){
1308  return function(from, to) {
1309    return new SVG.MorphObj(from, to).at(pos)
1310  }
1311}
1312
1313SVG.Situation = SVG.invent({
1314
1315  create: function(o){
1316    this.init = false
1317    this.reversed = false
1318    this.reversing = false
1319
1320    this.duration = new SVG.Number(o.duration).valueOf()
1321    this.delay = new SVG.Number(o.delay).valueOf()
1322
1323    this.start = +new Date() + this.delay
1324    this.finish = this.start + this.duration
1325    this.ease = o.ease
1326
1327    // this.loop is incremented from 0 to this.loops
1328    // it is also incremented when in an infinite loop (when this.loops is true)
1329    this.loop = 0
1330    this.loops = false
1331
1332    this.animations = {
1333      // functionToCall: [list of morphable objects]
1334      // e.g. move: [SVG.Number, SVG.Number]
1335    }
1336
1337    this.attrs = {
1338      // holds all attributes which are not represented from a function svg.js provides
1339      // e.g. someAttr: SVG.Number
1340    }
1341
1342    this.styles = {
1343      // holds all styles which should be animated
1344      // e.g. fill-color: SVG.Color
1345    }
1346
1347    this.transforms = [
1348      // holds all transformations as transformation objects
1349      // e.g. [SVG.Rotate, SVG.Translate, SVG.Matrix]
1350    ]
1351
1352    this.once = {
1353      // functions to fire at a specific position
1354      // e.g. "0.5": function foo(){}
1355    }
1356
1357  }
1358
1359})
1360
1361
1362SVG.FX = SVG.invent({
1363
1364  create: function(element) {
1365    this._target = element
1366    this.situations = []
1367    this.active = false
1368    this.situation = null
1369    this.paused = false
1370    this.lastPos = 0
1371    this.pos = 0
1372    // The absolute position of an animation is its position in the context of its complete duration (including delay and loops)
1373    // When performing a delay, absPos is below 0 and when performing a loop, its value is above 1
1374    this.absPos = 0
1375    this._speed = 1
1376  }
1377
1378, extend: {
1379
1380    /**
1381     * sets or returns the target of this animation
1382     * @param o object || number In case of Object it holds all parameters. In case of number its the duration of the animation
1383     * @param ease function || string Function which should be used for easing or easing keyword
1384     * @param delay Number indicating the delay before the animation starts
1385     * @return target || this
1386     */
1387    animate: function(o, ease, delay){
1388
1389      if(typeof o == 'object'){
1390        ease = o.ease
1391        delay = o.delay
1392        o = o.duration
1393      }
1394
1395      var situation = new SVG.Situation({
1396        duration: o || 1000,
1397        delay: delay || 0,
1398        ease: SVG.easing[ease || '-'] || ease
1399      })
1400
1401      this.queue(situation)
1402
1403      return this
1404    }
1405
1406    /**
1407     * sets a delay before the next element of the queue is called
1408     * @param delay Duration of delay in milliseconds
1409     * @return this.target()
1410     */
1411  , delay: function(delay){
1412      // The delay is performed by an empty situation with its duration
1413      // attribute set to the duration of the delay
1414      var situation = new SVG.Situation({
1415        duration: delay,
1416        delay: 0,
1417        ease: SVG.easing['-']
1418      })
1419
1420      return this.queue(situation)
1421    }
1422
1423    /**
1424     * sets or returns the target of this animation
1425     * @param null || target SVG.Element which should be set as new target
1426     * @return target || this
1427     */
1428  , target: function(target){
1429      if(target && target instanceof SVG.Element){
1430        this._target = target
1431        return this
1432      }
1433
1434      return this._target
1435    }
1436
1437    // returns the absolute position at a given time
1438  , timeToAbsPos: function(timestamp){
1439      return (timestamp - this.situation.start) / (this.situation.duration/this._speed)
1440    }
1441
1442    // returns the timestamp from a given absolute positon
1443  , absPosToTime: function(absPos){
1444      return this.situation.duration/this._speed * absPos + this.situation.start
1445    }
1446
1447    // starts the animationloop
1448  , startAnimFrame: function(){
1449      this.stopAnimFrame()
1450      this.animationFrame = window.requestAnimationFrame(function(){ this.step() }.bind(this))
1451    }
1452
1453    // cancels the animationframe
1454  , stopAnimFrame: function(){
1455      window.cancelAnimationFrame(this.animationFrame)
1456    }
1457
1458    // kicks off the animation - only does something when the queue is currently not active and at least one situation is set
1459  , start: function(){
1460      // dont start if already started
1461      if(!this.active && this.situation){
1462        this.active = true
1463        this.startCurrent()
1464      }
1465
1466      return this
1467    }
1468
1469    // start the current situation
1470  , startCurrent: function(){
1471      this.situation.start = +new Date + this.situation.delay/this._speed
1472      this.situation.finish = this.situation.start + this.situation.duration/this._speed
1473      return this.initAnimations().step()
1474    }
1475
1476    /**
1477     * adds a function / Situation to the animation queue
1478     * @param fn function / situation to add
1479     * @return this
1480     */
1481  , queue: function(fn){
1482      if(typeof fn == 'function' || fn instanceof SVG.Situation)
1483        this.situations.push(fn)
1484
1485      if(!this.situation) this.situation = this.situations.shift()
1486
1487      return this
1488    }
1489
1490    /**
1491     * pulls next element from the queue and execute it
1492     * @return this
1493     */
1494  , dequeue: function(){
1495      // stop current animation
1496      this.stop()
1497
1498      // get next animation from queue
1499      this.situation = this.situations.shift()
1500
1501      if(this.situation){
1502        if(this.situation instanceof SVG.Situation) {
1503          this.start()
1504        } else {
1505          // If it is not a SVG.Situation, then it is a function, we execute it
1506          this.situation.call(this)
1507        }
1508      }
1509
1510      return this
1511    }
1512
1513    // updates all animations to the current state of the element
1514    // this is important when one property could be changed from another property
1515  , initAnimations: function() {
1516      var i, j, source
1517      var s = this.situation
1518
1519      if(s.init) return this
1520
1521      for(i in s.animations){
1522        source = this.target()[i]()
1523
1524        if(!Array.isArray(source)) {
1525          source = [source]
1526        }
1527
1528        if(!Array.isArray(s.animations[i])) {
1529          s.animations[i] = [s.animations[i]]
1530        }
1531
1532        //if(s.animations[i].length > source.length) {
1533        //  source.concat = source.concat(s.animations[i].slice(source.length, s.animations[i].length))
1534        //}
1535
1536        for(j = source.length; j--;) {
1537          // The condition is because some methods return a normal number instead
1538          // of a SVG.Number
1539          if(s.animations[i][j] instanceof SVG.Number)
1540            source[j] = new SVG.Number(source[j])
1541
1542          s.animations[i][j] = source[j].morph(s.animations[i][j])
1543        }
1544      }
1545
1546      for(i in s.attrs){
1547        s.attrs[i] = new SVG.MorphObj(this.target().attr(i), s.attrs[i])
1548      }
1549
1550      for(i in s.styles){
1551        s.styles[i] = new SVG.MorphObj(this.target().style(i), s.styles[i])
1552      }
1553
1554      s.initialTransformation = this.target().matrixify()
1555
1556      s.init = true
1557      return this
1558    }
1559  , clearQueue: function(){
1560      this.situations = []
1561      return this
1562    }
1563  , clearCurrent: function(){
1564      this.situation = null
1565      return this
1566    }
1567    /** stops the animation immediately
1568     * @param jumpToEnd A Boolean indicating whether to complete the current animation immediately.
1569     * @param clearQueue A Boolean indicating whether to remove queued animation as well.
1570     * @return this
1571     */
1572  , stop: function(jumpToEnd, clearQueue){
1573      var active = this.active
1574      this.active = false
1575
1576      if(clearQueue){
1577        this.clearQueue()
1578      }
1579
1580      if(jumpToEnd && this.situation){
1581        // initialize the situation if it was not
1582        !active && this.startCurrent()
1583        this.atEnd()
1584      }
1585
1586      this.stopAnimFrame()
1587
1588      return this.clearCurrent()
1589    }
1590
1591    /** resets the element to the state where the current element has started
1592     * @return this
1593     */
1594  , reset: function(){
1595      if(this.situation){
1596        var temp = this.situation
1597        this.stop()
1598        this.situation = temp
1599        this.atStart()
1600      }
1601      return this
1602    }
1603
1604    // Stop the currently-running animation, remove all queued animations, and complete all animations for the element.
1605  , finish: function(){
1606
1607      this.stop(true, false)
1608
1609      while(this.dequeue().situation && this.stop(true, false));
1610
1611      this.clearQueue().clearCurrent()
1612
1613      return this
1614    }
1615
1616    // set the internal animation pointer at the start position, before any loops, and updates the visualisation
1617  , atStart: function() {
1618      return this.at(0, true)
1619    }
1620
1621    // set the internal animation pointer at the end position, after all the loops, and updates the visualisation
1622  , atEnd: function() {
1623      if (this.situation.loops === true) {
1624        // If in a infinite loop, we end the current iteration
1625        this.situation.loops = this.situation.loop + 1
1626      }
1627
1628      if(typeof this.situation.loops == 'number') {
1629        // If performing a finite number of loops, we go after all the loops
1630        return this.at(this.situation.loops, true)
1631      } else {
1632        // If no loops, we just go at the end
1633        return this.at(1, true)
1634      }
1635    }
1636
1637    // set the internal animation pointer to the specified position and updates the visualisation
1638    // if isAbsPos is true, pos is treated as an absolute position
1639  , at: function(pos, isAbsPos){
1640      var durDivSpd = this.situation.duration/this._speed
1641
1642      this.absPos = pos
1643      // If pos is not an absolute position, we convert it into one
1644      if (!isAbsPos) {
1645        if (this.situation.reversed) this.absPos = 1 - this.absPos
1646        this.absPos += this.situation.loop
1647      }
1648
1649      this.situation.start = +new Date - this.absPos * durDivSpd
1650      this.situation.finish = this.situation.start + durDivSpd
1651
1652      return this.step(true)
1653    }
1654
1655    /**
1656     * sets or returns the speed of the animations
1657     * @param speed null || Number The new speed of the animations
1658     * @return Number || this
1659     */
1660  , speed: function(speed){
1661      if (speed === 0) return this.pause()
1662
1663      if (speed) {
1664        this._speed = speed
1665        // We use an absolute position here so that speed can affect the delay before the animation
1666        return this.at(this.absPos, true)
1667      } else return this._speed
1668    }
1669
1670    // Make loopable
1671  , loop: function(times, reverse) {
1672      var c = this.last()
1673
1674      // store total loops
1675      c.loops = (times != null) ? times : true
1676      c.loop = 0
1677
1678      if(reverse) c.reversing = true
1679      return this
1680    }
1681
1682    // pauses the animation
1683  , pause: function(){
1684      this.paused = true
1685      this.stopAnimFrame()
1686
1687      return this
1688    }
1689
1690    // unpause the animation
1691  , play: function(){
1692      if(!this.paused) return this
1693      this.paused = false
1694      // We use an absolute position here so that the delay before the animation can be paused
1695      return this.at(this.absPos, true)
1696    }
1697
1698    /**
1699     * toggle or set the direction of the animation
1700     * true sets direction to backwards while false sets it to forwards
1701     * @param reversed Boolean indicating whether to reverse the animation or not (default: toggle the reverse status)
1702     * @return this
1703     */
1704  , reverse: function(reversed){
1705      var c = this.last()
1706
1707      if(typeof reversed == 'undefined') c.reversed = !c.reversed
1708      else c.reversed = reversed
1709
1710      return this
1711    }
1712
1713
1714    /**
1715     * returns a float from 0-1 indicating the progress of the current animation
1716     * @param eased Boolean indicating whether the returned position should be eased or not
1717     * @return number
1718     */
1719  , progress: function(easeIt){
1720      return easeIt ? this.situation.ease(this.pos) : this.pos
1721    }
1722
1723    /**
1724     * adds a callback function which is called when the current animation is finished
1725     * @param fn Function which should be executed as callback
1726     * @return number
1727     */
1728  , after: function(fn){
1729      var c = this.last()
1730        , wrapper = function wrapper(e){
1731            if(e.detail.situation == c){
1732              fn.call(this, c)
1733              this.off('finished.fx', wrapper) // prevent memory leak
1734            }
1735          }
1736
1737      this.target().on('finished.fx', wrapper)
1738
1739      return this._callStart()
1740    }
1741
1742    // adds a callback which is called whenever one animation step is performed
1743  , during: function(fn){
1744      var c = this.last()
1745        , wrapper = function(e){
1746            if(e.detail.situation == c){
1747              fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, c)
1748            }
1749          }
1750
1751      // see above
1752      this.target().off('during.fx', wrapper).on('during.fx', wrapper)
1753
1754      this.after(function(){
1755        this.off('during.fx', wrapper)
1756      })
1757
1758      return this._callStart()
1759    }
1760
1761    // calls after ALL animations in the queue are finished
1762  , afterAll: function(fn){
1763      var wrapper = function wrapper(e){
1764            fn.call(this)
1765            this.off('allfinished.fx', wrapper)
1766          }
1767
1768      // see above
1769      this.target().off('allfinished.fx', wrapper).on('allfinished.fx', wrapper)
1770
1771      return this._callStart()
1772    }
1773
1774    // calls on every animation step for all animations
1775  , duringAll: function(fn){
1776      var wrapper = function(e){
1777            fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, e.detail.situation)
1778          }
1779
1780      this.target().off('during.fx', wrapper).on('during.fx', wrapper)
1781
1782      this.afterAll(function(){
1783        this.off('during.fx', wrapper)
1784      })
1785
1786      return this._callStart()
1787    }
1788
1789  , last: function(){
1790      return this.situations.length ? this.situations[this.situations.length-1] : this.situation
1791    }
1792
1793    // adds one property to the animations
1794  , add: function(method, args, type){
1795      this.last()[type || 'animations'][method] = args
1796      return this._callStart()
1797    }
1798
1799    /** perform one step of the animation
1800     *  @param ignoreTime Boolean indicating whether to ignore time and use position directly or recalculate position based on time
1801     *  @return this
1802     */
1803  , step: function(ignoreTime){
1804
1805      // convert current time to an absolute position
1806      if(!ignoreTime) this.absPos = this.timeToAbsPos(+new Date)
1807
1808      // This part convert an absolute position to a position
1809      if(this.situation.loops !== false) {
1810        var absPos, absPosInt, lastLoop
1811
1812        // If the absolute position is below 0, we just treat it as if it was 0
1813        absPos = Math.max(this.absPos, 0)
1814        absPosInt = Math.floor(absPos)
1815
1816        if(this.situation.loops === true || absPosInt < this.situation.loops) {
1817          this.pos = absPos - absPosInt
1818          lastLoop = this.situation.loop
1819          this.situation.loop = absPosInt
1820        } else {
1821          this.absPos = this.situation.loops
1822          this.pos = 1
1823          // The -1 here is because we don't want to toggle reversed when all the loops have been completed
1824          lastLoop = this.situation.loop - 1
1825          this.situation.loop = this.situation.loops
1826        }
1827
1828        if(this.situation.reversing) {
1829          // Toggle reversed if an odd number of loops as occured since the last call of step
1830          this.situation.reversed = this.situation.reversed != Boolean((this.situation.loop - lastLoop) % 2)
1831        }
1832
1833      } else {
1834        // If there are no loop, the absolute position must not be above 1
1835        this.absPos = Math.min(this.absPos, 1)
1836        this.pos = this.absPos
1837      }
1838
1839      // while the absolute position can be below 0, the position must not be below 0
1840      if(this.pos < 0) this.pos = 0
1841
1842      if(this.situation.reversed) this.pos = 1 - this.pos
1843
1844
1845      // apply easing
1846      var eased = this.situation.ease(this.pos)
1847
1848      // call once-callbacks
1849      for(var i in this.situation.once){
1850        if(i > this.lastPos && i <= eased){
1851          this.situation.once[i].call(this.target(), this.pos, eased)
1852          delete this.situation.once[i]
1853        }
1854      }
1855
1856      // fire during callback with position, eased position and current situation as parameter
1857      if(this.active) this.target().fire('during', {pos: this.pos, eased: eased, fx: this, situation: this.situation})
1858
1859      // the user may call stop or finish in the during callback
1860      // so make sure that we still have a valid situation
1861      if(!this.situation){
1862        return this
1863      }
1864
1865      // apply the actual animation to every property
1866      this.eachAt()
1867
1868      // do final code when situation is finished
1869      if((this.pos == 1 && !this.situation.reversed) || (this.situation.reversed && this.pos == 0)){
1870
1871        // stop animation callback
1872        this.stopAnimFrame()
1873
1874        // fire finished callback with current situation as parameter
1875        this.target().fire('finished', {fx:this, situation: this.situation})
1876
1877        if(!this.situations.length){
1878          this.target().fire('allfinished')
1879
1880          // Recheck the length since the user may call animate in the afterAll callback
1881          if(!this.situations.length){
1882            this.target().off('.fx') // there shouldnt be any binding left, but to make sure...
1883            this.active = false
1884          }
1885        }
1886
1887        // start next animation
1888        if(this.active) this.dequeue()
1889        else this.clearCurrent()
1890
1891      }else if(!this.paused && this.active){
1892        // we continue animating when we are not at the end
1893        this.startAnimFrame()
1894      }
1895
1896      // save last eased position for once callback triggering
1897      this.lastPos = eased
1898      return this
1899
1900    }
1901
1902    // calculates the step for every property and calls block with it
1903  , eachAt: function(){
1904      var i, len, at, self = this, target = this.target(), s = this.situation
1905
1906      // apply animations which can be called trough a method
1907      for(i in s.animations){
1908
1909        at = [].concat(s.animations[i]).map(function(el){
1910          return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
1911        })
1912
1913        target[i].apply(target, at)
1914
1915      }
1916
1917      // apply animation which has to be applied with attr()
1918      for(i in s.attrs){
1919
1920        at = [i].concat(s.attrs[i]).map(function(el){
1921          return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
1922        })
1923
1924        target.attr.apply(target, at)
1925
1926      }
1927
1928      // apply animation which has to be applied with style()
1929      for(i in s.styles){
1930
1931        at = [i].concat(s.styles[i]).map(function(el){
1932          return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
1933        })
1934
1935        target.style.apply(target, at)
1936
1937      }
1938
1939      // animate initialTransformation which has to be chained
1940      if(s.transforms.length){
1941
1942        // get initial initialTransformation
1943        at = s.initialTransformation
1944        for(i = 0, len = s.transforms.length; i < len; i++){
1945
1946          // get next transformation in chain
1947          var a = s.transforms[i]
1948
1949          // multiply matrix directly
1950          if(a instanceof SVG.Matrix){
1951
1952            if(a.relative){
1953              at = at.multiply(new SVG.Matrix().morph(a).at(s.ease(this.pos)))
1954            }else{
1955              at = at.morph(a).at(s.ease(this.pos))
1956            }
1957            continue
1958          }
1959
1960          // when transformation is absolute we have to reset the needed transformation first
1961          if(!a.relative)
1962            a.undo(at.extract())
1963
1964          // and reapply it after
1965          at = at.multiply(a.at(s.ease(this.pos)))
1966
1967        }
1968
1969        // set new matrix on element
1970        target.matrix(at)
1971      }
1972
1973      return this
1974
1975    }
1976
1977
1978    // adds an once-callback which is called at a specific position and never again
1979  , once: function(pos, fn, isEased){
1980      var c = this.last()
1981      if(!isEased) pos = c.ease(pos)
1982
1983      c.once[pos] = fn
1984
1985      return this
1986    }
1987
1988  , _callStart: function() {
1989      setTimeout(function(){this.start()}.bind(this), 0)
1990      return this
1991    }
1992
1993  }
1994
1995, parent: SVG.Element
1996
1997  // Add method to parent elements
1998, construct: {
1999    // Get fx module or create a new one, then animate with given duration and ease
2000    animate: function(o, ease, delay) {
2001      return (this.fx || (this.fx = new SVG.FX(this))).animate(o, ease, delay)
2002    }
2003  , delay: function(delay){
2004      return (this.fx || (this.fx = new SVG.FX(this))).delay(delay)
2005    }
2006  , stop: function(jumpToEnd, clearQueue) {
2007      if (this.fx)
2008        this.fx.stop(jumpToEnd, clearQueue)
2009
2010      return this
2011    }
2012  , finish: function() {
2013      if (this.fx)
2014        this.fx.finish()
2015
2016      return this
2017    }
2018    // Pause current animation
2019  , pause: function() {
2020      if (this.fx)
2021        this.fx.pause()
2022
2023      return this
2024    }
2025    // Play paused current animation
2026  , play: function() {
2027      if (this.fx)
2028        this.fx.play()
2029
2030      return this
2031    }
2032    // Set/Get the speed of the animations
2033  , speed: function(speed) {
2034      if (this.fx)
2035        if (speed == null)
2036          return this.fx.speed()
2037        else
2038          this.fx.speed(speed)
2039
2040      return this
2041    }
2042  }
2043
2044})
2045
2046// MorphObj is used whenever no morphable object is given
2047SVG.MorphObj = SVG.invent({
2048
2049  create: function(from, to){
2050    // prepare color for morphing
2051    if(SVG.Color.isColor(to)) return new SVG.Color(from).morph(to)
2052    // check if we have a list of values
2053    if(SVG.regex.delimiter.test(from)) {
2054      // prepare path for morphing
2055      if(SVG.regex.pathLetters.test(from)) return new SVG.PathArray(from).morph(to)
2056      // prepare value list for morphing
2057      else return new SVG.Array(from).morph(to)
2058    }
2059    // prepare number for morphing
2060    if(SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to)
2061
2062    // prepare for plain morphing
2063    this.value = from
2064    this.destination = to
2065  }
2066
2067, extend: {
2068    at: function(pos, real){
2069      return real < 1 ? this.value : this.destination
2070    },
2071
2072    valueOf: function(){
2073      return this.value
2074    }
2075  }
2076
2077})
2078
2079SVG.extend(SVG.FX, {
2080  // Add animatable attributes
2081  attr: function(a, v, relative) {
2082    // apply attributes individually
2083    if (typeof a == 'object') {
2084      for (var key in a)
2085        this.attr(key, a[key])
2086
2087    } else {
2088      this.add(a, v, 'attrs')
2089    }
2090
2091    return this
2092  }
2093  // Add animatable styles
2094, style: function(s, v) {
2095    if (typeof s == 'object')
2096      for (var key in s)
2097        this.style(key, s[key])
2098
2099    else
2100      this.add(s, v, 'styles')
2101
2102    return this
2103  }
2104  // Animatable x-axis
2105, x: function(x, relative) {
2106    if(this.target() instanceof SVG.G){
2107      this.transform({x:x}, relative)
2108      return this
2109    }
2110
2111    var num = new SVG.Number(x)
2112    num.relative = relative
2113    return this.add('x', num)
2114  }
2115  // Animatable y-axis
2116, y: function(y, relative) {
2117    if(this.target() instanceof SVG.G){
2118      this.transform({y:y}, relative)
2119      return this
2120    }
2121
2122    var num = new SVG.Number(y)
2123    num.relative = relative
2124    return this.add('y', num)
2125  }
2126  // Animatable center x-axis
2127, cx: function(x) {
2128    return this.add('cx', new SVG.Number(x))
2129  }
2130  // Animatable center y-axis
2131, cy: function(y) {
2132    return this.add('cy', new SVG.Number(y))
2133  }
2134  // Add animatable move
2135, move: function(x, y) {
2136    return this.x(x).y(y)
2137  }
2138  // Add animatable center
2139, center: function(x, y) {
2140    return this.cx(x).cy(y)
2141  }
2142  // Add animatable size
2143, size: function(width, height) {
2144    if (this.target() instanceof SVG.Text) {
2145      // animate font size for Text elements
2146      this.attr('font-size', width)
2147
2148    } else {
2149      // animate bbox based size for all other elements
2150      var box
2151
2152      if(!width || !height){
2153        box = this.target().bbox()
2154      }
2155
2156      if(!width){
2157        width = box.width / box.height  * height
2158      }
2159
2160      if(!height){
2161        height = box.height / box.width  * width
2162      }
2163
2164      this.add('width' , new SVG.Number(width))
2165          .add('height', new SVG.Number(height))
2166
2167    }
2168
2169    return this
2170  }
2171  // Add animatable width
2172, width: function(width) {
2173    return this.add('width', new SVG.Number(width))
2174  }
2175  // Add animatable height
2176, height: function(height) {
2177    return this.add('height', new SVG.Number(height))
2178  }
2179  // Add animatable plot
2180, plot: function(a, b, c, d) {
2181    // Lines can be plotted with 4 arguments
2182    if(arguments.length == 4) {
2183      return this.plot([a, b, c, d])
2184    }
2185
2186    return this.add('plot', new (this.target().morphArray)(a))
2187  }
2188  // Add leading method
2189, leading: function(value) {
2190    return this.target().leading ?
2191      this.add('leading', new SVG.Number(value)) :
2192      this
2193  }
2194  // Add animatable viewbox
2195, viewbox: function(x, y, width, height) {
2196    if (this.target() instanceof SVG.Container) {
2197      this.add('viewbox', new SVG.ViewBox(x, y, width, height))
2198    }
2199
2200    return this
2201  }
2202, update: function(o) {
2203    if (this.target() instanceof SVG.Stop) {
2204      if (typeof o == 'number' || o instanceof SVG.Number) {
2205        return this.update({
2206          offset:  arguments[0]
2207        , color:   arguments[1]
2208        , opacity: arguments[2]
2209        })
2210      }
2211
2212      if (o.opacity != null) this.attr('stop-opacity', o.opacity)
2213      if (o.color   != null) this.attr('stop-color', o.color)
2214      if (o.offset  != null) this.attr('offset', o.offset)
2215    }
2216
2217    return this
2218  }
2219})
2220
2221SVG.Box = SVG.invent({
2222  create: function(x, y, width, height) {
2223    if (typeof x == 'object' && !(x instanceof SVG.Element)) {
2224      // chromes getBoundingClientRect has no x and y property
2225      return SVG.Box.call(this, x.left != null ? x.left : x.x , x.top != null ? x.top : x.y, x.width, x.height)
2226    } else if (arguments.length == 4) {
2227      this.x = x
2228      this.y = y
2229      this.width = width
2230      this.height = height
2231    }
2232
2233    // add center, right, bottom...
2234    fullBox(this)
2235  }
2236, extend: {
2237    // Merge rect box with another, return a new instance
2238    merge: function(box) {
2239      var b = new this.constructor()
2240
2241      // merge boxes
2242      b.x      = Math.min(this.x, box.x)
2243      b.y      = Math.min(this.y, box.y)
2244      b.width  = Math.max(this.x + this.width,  box.x + box.width)  - b.x
2245      b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
2246
2247      return fullBox(b)
2248    }
2249
2250  , transform: function(m) {
2251      var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, p, bbox
2252
2253      var pts = [
2254        new SVG.Point(this.x, this.y),
2255        new SVG.Point(this.x2, this.y),
2256        new SVG.Point(this.x, this.y2),
2257        new SVG.Point(this.x2, this.y2)
2258      ]
2259
2260      pts.forEach(function(p) {
2261        p = p.transform(m)
2262        xMin = Math.min(xMin,p.x)
2263        xMax = Math.max(xMax,p.x)
2264        yMin = Math.min(yMin,p.y)
2265        yMax = Math.max(yMax,p.y)
2266      })
2267
2268      bbox = new this.constructor()
2269      bbox.x = xMin
2270      bbox.width = xMax-xMin
2271      bbox.y = yMin
2272      bbox.height = yMax-yMin
2273
2274      fullBox(bbox)
2275
2276      return bbox
2277    }
2278  }
2279})
2280
2281SVG.BBox = SVG.invent({
2282  // Initialize
2283  create: function(element) {
2284    SVG.Box.apply(this, [].slice.call(arguments))
2285
2286    // get values if element is given
2287    if (element instanceof SVG.Element) {
2288      var box
2289
2290      // yes this is ugly, but Firefox can be a pain when it comes to elements that are not yet rendered
2291      try {
2292
2293        if (!document.documentElement.contains){
2294          // This is IE - it does not support contains() for top-level SVGs
2295          var topParent = element.node
2296          while (topParent.parentNode){
2297            topParent = topParent.parentNode
2298          }
2299          if (topParent != document) throw new Exception('Element not in the dom')
2300        } else {
2301          // the element is NOT in the dom, throw error
2302          if(!document.documentElement.contains(element.node)) throw new Exception('Element not in the dom')
2303        }
2304
2305        // find native bbox
2306        box = element.node.getBBox()
2307      } catch(e) {
2308        if(element instanceof SVG.Shape){
2309          var clone = element.clone(SVG.parser.draw.instance).show()
2310          box = clone.node.getBBox()
2311          clone.remove()
2312        }else{
2313          box = {
2314            x:      element.node.clientLeft
2315          , y:      element.node.clientTop
2316          , width:  element.node.clientWidth
2317          , height: element.node.clientHeight
2318          }
2319        }
2320      }
2321
2322      SVG.Box.call(this, box)
2323    }
2324
2325  }
2326
2327  // Define ancestor
2328, inherit: SVG.Box
2329
2330  // Define Parent
2331, parent: SVG.Element
2332
2333  // Constructor
2334, construct: {
2335    // Get bounding box
2336    bbox: function() {
2337      return new SVG.BBox(this)
2338    }
2339  }
2340
2341})
2342
2343SVG.BBox.prototype.constructor = SVG.BBox
2344
2345
2346SVG.extend(SVG.Element, {
2347  tbox: function(){
2348    console.warn('Use of TBox is deprecated and mapped to RBox. Use .rbox() instead.')
2349    return this.rbox(this.doc())
2350  }
2351})
2352
2353SVG.RBox = SVG.invent({
2354  // Initialize
2355  create: function(element) {
2356    SVG.Box.apply(this, [].slice.call(arguments))
2357
2358    if (element instanceof SVG.Element) {
2359      SVG.Box.call(this, element.node.getBoundingClientRect())
2360    }
2361  }
2362
2363, inherit: SVG.Box
2364
2365  // define Parent
2366, parent: SVG.Element
2367
2368, extend: {
2369    addOffset: function() {
2370      // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
2371      this.x += window.pageXOffset
2372      this.y += window.pageYOffset
2373      return this
2374    }
2375  }
2376
2377  // Constructor
2378, construct: {
2379    // Get rect box
2380    rbox: function(el) {
2381      if (el) return new SVG.RBox(this).transform(el.screenCTM().inverse())
2382      return new SVG.RBox(this).addOffset()
2383    }
2384  }
2385
2386})
2387
2388SVG.RBox.prototype.constructor = SVG.RBox
2389
2390SVG.Matrix = SVG.invent({
2391  // Initialize
2392  create: function(source) {
2393    var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0])
2394
2395    // ensure source as object
2396    source = source instanceof SVG.Element ?
2397      source.matrixify() :
2398    typeof source === 'string' ?
2399      arrayToMatrix(source.split(SVG.regex.delimiter).map(parseFloat)) :
2400    arguments.length == 6 ?
2401      arrayToMatrix([].slice.call(arguments)) :
2402    Array.isArray(source) ?
2403      arrayToMatrix(source) :
2404    typeof source === 'object' ?
2405      source : base
2406
2407    // merge source
2408    for (i = abcdef.length - 1; i >= 0; --i)
2409      this[abcdef[i]] = source[abcdef[i]] != null ?
2410        source[abcdef[i]] : base[abcdef[i]]
2411  }
2412
2413  // Add methods
2414, extend: {
2415    // Extract individual transformations
2416    extract: function() {
2417      // find delta transform points
2418      var px    = deltaTransformPoint(this, 0, 1)
2419        , py    = deltaTransformPoint(this, 1, 0)
2420        , skewX = 180 / Math.PI * Math.atan2(px.y, px.x) - 90
2421
2422      return {
2423        // translation
2424        x:        this.e
2425      , y:        this.f
2426      , transformedX:(this.e * Math.cos(skewX * Math.PI / 180) + this.f * Math.sin(skewX * Math.PI / 180)) / Math.sqrt(this.a * this.a + this.b * this.b)
2427      , transformedY:(this.f * Math.cos(skewX * Math.PI / 180) + this.e * Math.sin(-skewX * Math.PI / 180)) / Math.sqrt(this.c * this.c + this.d * this.d)
2428        // skew
2429      , skewX:    -skewX
2430      , skewY:    180 / Math.PI * Math.atan2(py.y, py.x)
2431        // scale
2432      , scaleX:   Math.sqrt(this.a * this.a + this.b * this.b)
2433      , scaleY:   Math.sqrt(this.c * this.c + this.d * this.d)
2434        // rotation
2435      , rotation: skewX
2436      , a: this.a
2437      , b: this.b
2438      , c: this.c
2439      , d: this.d
2440      , e: this.e
2441      , f: this.f
2442      , matrix: new SVG.Matrix(this)
2443      }
2444    }
2445    // Clone matrix
2446  , clone: function() {
2447      return new SVG.Matrix(this)
2448    }
2449    // Morph one matrix into another
2450  , morph: function(matrix) {
2451      // store new destination
2452      this.destination = new SVG.Matrix(matrix)
2453
2454      return this
2455    }
2456    // Get morphed matrix at a given position
2457  , at: function(pos) {
2458      // make sure a destination is defined
2459      if (!this.destination) return this
2460
2461      // calculate morphed matrix at a given position
2462      var matrix = new SVG.Matrix({
2463        a: this.a + (this.destination.a - this.a) * pos
2464      , b: this.b + (this.destination.b - this.b) * pos
2465      , c: this.c + (this.destination.c - this.c) * pos
2466      , d: this.d + (this.destination.d - this.d) * pos
2467      , e: this.e + (this.destination.e - this.e) * pos
2468      , f: this.f + (this.destination.f - this.f) * pos
2469      })
2470
2471      return matrix
2472    }
2473    // Multiplies by given matrix
2474  , multiply: function(matrix) {
2475      return new SVG.Matrix(this.native().multiply(parseMatrix(matrix).native()))
2476    }
2477    // Inverses matrix
2478  , inverse: function() {
2479      return new SVG.Matrix(this.native().inverse())
2480    }
2481    // Translate matrix
2482  , translate: function(x, y) {
2483      return new SVG.Matrix(this.native().translate(x || 0, y || 0))
2484    }
2485    // Scale matrix
2486  , scale: function(x, y, cx, cy) {
2487      // support uniformal scale
2488      if (arguments.length == 1) {
2489        y = x
2490      } else if (arguments.length == 3) {
2491        cy = cx
2492        cx = y
2493        y = x
2494      }
2495
2496      return this.around(cx, cy, new SVG.Matrix(x, 0, 0, y, 0, 0))
2497    }
2498    // Rotate matrix
2499  , rotate: function(r, cx, cy) {
2500      // convert degrees to radians
2501      r = SVG.utils.radians(r)
2502
2503      return this.around(cx, cy, new SVG.Matrix(Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0))
2504    }
2505    // Flip matrix on x or y, at a given offset
2506  , flip: function(a, o) {
2507      return a == 'x' ?
2508          this.scale(-1, 1, o, 0) :
2509        a == 'y' ?
2510          this.scale(1, -1, 0, o) :
2511          this.scale(-1, -1, a, o != null ? o : a)
2512    }
2513    // Skew
2514  , skew: function(x, y, cx, cy) {
2515      // support uniformal skew
2516      if (arguments.length == 1) {
2517        y = x
2518      } else if (arguments.length == 3) {
2519        cy = cx
2520        cx = y
2521        y = x
2522      }
2523
2524      // convert degrees to radians
2525      x = SVG.utils.radians(x)
2526      y = SVG.utils.radians(y)
2527
2528      return this.around(cx, cy, new SVG.Matrix(1, Math.tan(y), Math.tan(x), 1, 0, 0))
2529    }
2530    // SkewX
2531  , skewX: function(x, cx, cy) {
2532      return this.skew(x, 0, cx, cy)
2533    }
2534    // SkewY
2535  , skewY: function(y, cx, cy) {
2536      return this.skew(0, y, cx, cy)
2537    }
2538    // Transform around a center point
2539  , around: function(cx, cy, matrix) {
2540      return this
2541        .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0))
2542        .multiply(matrix)
2543        .multiply(new SVG.Matrix(1, 0, 0, 1, -cx || 0, -cy || 0))
2544    }
2545    // Convert to native SVGMatrix
2546  , native: function() {
2547      // create new matrix
2548      var matrix = SVG.parser.native.createSVGMatrix()
2549
2550      // update with current values
2551      for (var i = abcdef.length - 1; i >= 0; i--)
2552        matrix[abcdef[i]] = this[abcdef[i]]
2553
2554      return matrix
2555    }
2556    // Convert matrix to string
2557  , toString: function() {
2558      // Construct the matrix directly, avoid values that are too small
2559      return 'matrix(' + float32String(this.a) + ',' + float32String(this.b)
2560        + ',' + float32String(this.c) + ',' + float32String(this.d)
2561        + ',' + float32String(this.e) + ',' + float32String(this.f)
2562        + ')'
2563    }
2564  }
2565
2566  // Define parent
2567, parent: SVG.Element
2568
2569  // Add parent method
2570, construct: {
2571    // Get current matrix
2572    ctm: function() {
2573      return new SVG.Matrix(this.node.getCTM())
2574    },
2575    // Get current screen matrix
2576    screenCTM: function() {
2577      /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537
2578         This is needed because FF does not return the transformation matrix
2579         for the inner coordinate system when getScreenCTM() is called on nested svgs.
2580         However all other Browsers do that */
2581      if(this instanceof SVG.Nested) {
2582        var rect = this.rect(1,1)
2583        var m = rect.node.getScreenCTM()
2584        rect.remove()
2585        return new SVG.Matrix(m)
2586      }
2587      return new SVG.Matrix(this.node.getScreenCTM())
2588    }
2589
2590  }
2591
2592})
2593
2594SVG.Point = SVG.invent({
2595  // Initialize
2596  create: function(x,y) {
2597    var i, source, base = {x:0, y:0}
2598
2599    // ensure source as object
2600    source = Array.isArray(x) ?
2601      {x:x[0], y:x[1]} :
2602    typeof x === 'object' ?
2603      {x:x.x, y:x.y} :
2604    x != null ?
2605      {x:x, y:(y != null ? y : x)} : base // If y has no value, then x is used has its value
2606
2607    // merge source
2608    this.x = source.x
2609    this.y = source.y
2610  }
2611
2612  // Add methods
2613, extend: {
2614    // Clone point
2615    clone: function() {
2616      return new SVG.Point(this)
2617    }
2618    // Morph one point into another
2619  , morph: function(x, y) {
2620      // store new destination
2621      this.destination = new SVG.Point(x, y)
2622
2623      return this
2624    }
2625    // Get morphed point at a given position
2626  , at: function(pos) {
2627      // make sure a destination is defined
2628      if (!this.destination) return this
2629
2630      // calculate morphed matrix at a given position
2631      var point = new SVG.Point({
2632        x: this.x + (this.destination.x - this.x) * pos
2633      , y: this.y + (this.destination.y - this.y) * pos
2634      })
2635
2636      return point
2637    }
2638    // Convert to native SVGPoint
2639  , native: function() {
2640      // create new point
2641      var point = SVG.parser.native.createSVGPoint()
2642
2643      // update with current values
2644      point.x = this.x
2645      point.y = this.y
2646
2647      return point
2648    }
2649    // transform point with matrix
2650  , transform: function(matrix) {
2651      return new SVG.Point(this.native().matrixTransform(matrix.native()))
2652    }
2653
2654  }
2655
2656})
2657
2658SVG.extend(SVG.Element, {
2659
2660  // Get point
2661  point: function(x, y) {
2662    return new SVG.Point(x,y).transform(this.screenCTM().inverse());
2663  }
2664
2665})
2666
2667SVG.extend(SVG.Element, {
2668  // Set svg element attribute
2669  attr: function(a, v, n) {
2670    // act as full getter
2671    if (a == null) {
2672      // get an object of attributes
2673      a = {}
2674      v = this.node.attributes
2675      for (n = v.length - 1; n >= 0; n--)
2676        a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue
2677
2678      return a
2679
2680    } else if (typeof a == 'object') {
2681      // apply every attribute individually if an object is passed
2682      for (v in a) this.attr(v, a[v])
2683
2684    } else if (v === null) {
2685        // remove value
2686        this.node.removeAttribute(a)
2687
2688    } else if (v == null) {
2689      // act as a getter if the first and only argument is not an object
2690      v = this.node.getAttribute(a)
2691      return v == null ?
2692        SVG.defaults.attrs[a] :
2693      SVG.regex.isNumber.test(v) ?
2694        parseFloat(v) : v
2695
2696    } else {
2697      // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0
2698      if (a == 'stroke-width')
2699        this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null)
2700      else if (a == 'stroke')
2701        this._stroke = v
2702
2703      // convert image fill and stroke to patterns
2704      if (a == 'fill' || a == 'stroke') {
2705        if (SVG.regex.isImage.test(v))
2706          v = this.doc().defs().image(v, 0, 0)
2707
2708        if (v instanceof SVG.Image)
2709          v = this.doc().defs().pattern(0, 0, function() {
2710            this.add(v)
2711          })
2712      }
2713
2714      // ensure correct numeric values (also accepts NaN and Infinity)
2715      if (typeof v === 'number')
2716        v = new SVG.Number(v)
2717
2718      // ensure full hex color
2719      else if (SVG.Color.isColor(v))
2720        v = new SVG.Color(v)
2721
2722      // parse array values
2723      else if (Array.isArray(v))
2724        v = new SVG.Array(v)
2725
2726      // if the passed attribute is leading...
2727      if (a == 'leading') {
2728        // ... call the leading method instead
2729        if (this.leading)
2730          this.leading(v)
2731      } else {
2732        // set given attribute on node
2733        typeof n === 'string' ?
2734          this.node.setAttributeNS(n, a, v.toString()) :
2735          this.node.setAttribute(a, v.toString())
2736      }
2737
2738      // rebuild if required
2739      if (this.rebuild && (a == 'font-size' || a == 'x'))
2740        this.rebuild(a, v)
2741    }
2742
2743    return this
2744  }
2745})
2746SVG.extend(SVG.Element, {
2747  // Add transformations
2748  transform: function(o, relative) {
2749    // get target in case of the fx module, otherwise reference this
2750    var target = this
2751      , matrix, bbox
2752
2753    // act as a getter
2754    if (typeof o !== 'object') {
2755      // get current matrix
2756      matrix = new SVG.Matrix(target).extract()
2757
2758      return typeof o === 'string' ? matrix[o] : matrix
2759    }
2760
2761    // get current matrix
2762    matrix = new SVG.Matrix(target)
2763
2764    // ensure relative flag
2765    relative = !!relative || !!o.relative
2766
2767    // act on matrix
2768    if (o.a != null) {
2769      matrix = relative ?
2770        // relative
2771        matrix.multiply(new SVG.Matrix(o)) :
2772        // absolute
2773        new SVG.Matrix(o)
2774
2775    // act on rotation
2776    } else if (o.rotation != null) {
2777      // ensure centre point
2778      ensureCentre(o, target)
2779
2780      // apply transformation
2781      matrix = relative ?
2782        // relative
2783        matrix.rotate(o.rotation, o.cx, o.cy) :
2784        // absolute
2785        matrix.rotate(o.rotation - matrix.extract().rotation, o.cx, o.cy)
2786
2787    // act on scale
2788    } else if (o.scale != null || o.scaleX != null || o.scaleY != null) {
2789      // ensure centre point
2790      ensureCentre(o, target)
2791
2792      // ensure scale values on both axes
2793      o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
2794      o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
2795
2796      if (!relative) {
2797        // absolute; multiply inversed values
2798        var e = matrix.extract()
2799        o.scaleX = o.scaleX * 1 / e.scaleX
2800        o.scaleY = o.scaleY * 1 / e.scaleY
2801      }
2802
2803      matrix = matrix.scale(o.scaleX, o.scaleY, o.cx, o.cy)
2804
2805    // act on skew
2806    } else if (o.skew != null || o.skewX != null || o.skewY != null) {
2807      // ensure centre point
2808      ensureCentre(o, target)
2809
2810      // ensure skew values on both axes
2811      o.skewX = o.skew != null ? o.skew : o.skewX != null ? o.skewX : 0
2812      o.skewY = o.skew != null ? o.skew : o.skewY != null ? o.skewY : 0
2813
2814      if (!relative) {
2815        // absolute; reset skew values
2816        var e = matrix.extract()
2817        matrix = matrix.multiply(new SVG.Matrix().skew(e.skewX, e.skewY, o.cx, o.cy).inverse())
2818      }
2819
2820      matrix = matrix.skew(o.skewX, o.skewY, o.cx, o.cy)
2821
2822    // act on flip
2823    } else if (o.flip) {
2824      if(o.flip == 'x' || o.flip == 'y') {
2825        o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset
2826      } else {
2827        if(o.offset == null) {
2828          bbox = target.bbox()
2829          o.flip = bbox.cx
2830          o.offset = bbox.cy
2831        } else {
2832          o.flip = o.offset
2833        }
2834      }
2835
2836      matrix = new SVG.Matrix().flip(o.flip, o.offset)
2837
2838    // act on translate
2839    } else if (o.x != null || o.y != null) {
2840      if (relative) {
2841        // relative
2842        matrix = matrix.translate(o.x, o.y)
2843      } else {
2844        // absolute
2845        if (o.x != null) matrix.e = o.x
2846        if (o.y != null) matrix.f = o.y
2847      }
2848    }
2849
2850    return this.attr('transform', matrix)
2851  }
2852})
2853
2854SVG.extend(SVG.FX, {
2855  transform: function(o, relative) {
2856    // get target in case of the fx module, otherwise reference this
2857    var target = this.target()
2858      , matrix, bbox
2859
2860    // act as a getter
2861    if (typeof o !== 'object') {
2862      // get current matrix
2863      matrix = new SVG.Matrix(target).extract()
2864
2865      return typeof o === 'string' ? matrix[o] : matrix
2866    }
2867
2868    // ensure relative flag
2869    relative = !!relative || !!o.relative
2870
2871    // act on matrix
2872    if (o.a != null) {
2873      matrix = new SVG.Matrix(o)
2874
2875    // act on rotation
2876    } else if (o.rotation != null) {
2877      // ensure centre point
2878      ensureCentre(o, target)
2879
2880      // apply transformation
2881      matrix = new SVG.Rotate(o.rotation, o.cx, o.cy)
2882
2883    // act on scale
2884    } else if (o.scale != null || o.scaleX != null || o.scaleY != null) {
2885      // ensure centre point
2886      ensureCentre(o, target)
2887
2888      // ensure scale values on both axes
2889      o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
2890      o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
2891
2892      matrix = new SVG.Scale(o.scaleX, o.scaleY, o.cx, o.cy)
2893
2894    // act on skew
2895    } else if (o.skewX != null || o.skewY != null) {
2896      // ensure centre point
2897      ensureCentre(o, target)
2898
2899      // ensure skew values on both axes
2900      o.skewX = o.skewX != null ? o.skewX : 0
2901      o.skewY = o.skewY != null ? o.skewY : 0
2902
2903      matrix = new SVG.Skew(o.skewX, o.skewY, o.cx, o.cy)
2904
2905    // act on flip
2906    } else if (o.flip) {
2907      if(o.flip == 'x' || o.flip == 'y') {
2908        o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset
2909      } else {
2910        if(o.offset == null) {
2911          bbox = target.bbox()
2912          o.flip = bbox.cx
2913          o.offset = bbox.cy
2914        } else {
2915          o.flip = o.offset
2916        }
2917      }
2918
2919      matrix = new SVG.Matrix().flip(o.flip, o.offset)
2920
2921    // act on translate
2922    } else if (o.x != null || o.y != null) {
2923      matrix = new SVG.Translate(o.x, o.y)
2924    }
2925
2926    if(!matrix) return this
2927
2928    matrix.relative = relative
2929
2930    this.last().transforms.push(matrix)
2931
2932    return this._callStart()
2933  }
2934})
2935
2936SVG.extend(SVG.Element, {
2937  // Reset all transformations
2938  untransform: function() {
2939    return this.attr('transform', null)
2940  },
2941  // merge the whole transformation chain into one matrix and returns it
2942  matrixify: function() {
2943
2944    var matrix = (this.attr('transform') || '')
2945      // split transformations
2946      .split(SVG.regex.transforms).slice(0,-1).map(function(str){
2947        // generate key => value pairs
2948        var kv = str.trim().split('(')
2949        return [kv[0], kv[1].split(SVG.regex.delimiter).map(function(str){ return parseFloat(str) })]
2950      })
2951      // merge every transformation into one matrix
2952      .reduce(function(matrix, transform){
2953
2954        if(transform[0] == 'matrix') return matrix.multiply(arrayToMatrix(transform[1]))
2955        return matrix[transform[0]].apply(matrix, transform[1])
2956
2957      }, new SVG.Matrix())
2958
2959    return matrix
2960  },
2961  // add an element to another parent without changing the visual representation on the screen
2962  toParent: function(parent) {
2963    if(this == parent) return this
2964    var ctm = this.screenCTM()
2965    var pCtm = parent.screenCTM().inverse()
2966
2967    this.addTo(parent).untransform().transform(pCtm.multiply(ctm))
2968
2969    return this
2970  },
2971  // same as above with parent equals root-svg
2972  toDoc: function() {
2973    return this.toParent(this.doc())
2974  }
2975
2976})
2977
2978SVG.Transformation = SVG.invent({
2979
2980  create: function(source, inversed){
2981
2982    if(arguments.length > 1 && typeof inversed != 'boolean'){
2983      return this.constructor.call(this, [].slice.call(arguments))
2984    }
2985
2986    if(Array.isArray(source)){
2987      for(var i = 0, len = this.arguments.length; i < len; ++i){
2988        this[this.arguments[i]] = source[i]
2989      }
2990    } else if(typeof source == 'object'){
2991      for(var i = 0, len = this.arguments.length; i < len; ++i){
2992        this[this.arguments[i]] = source[this.arguments[i]]
2993      }
2994    }
2995
2996    this.inversed = false
2997
2998    if(inversed === true){
2999      this.inversed = true
3000    }
3001
3002  }
3003
3004, extend: {
3005
3006    arguments: []
3007  , method: ''
3008
3009  , at: function(pos){
3010
3011      var params = []
3012
3013      for(var i = 0, len = this.arguments.length; i < len; ++i){
3014        params.push(this[this.arguments[i]])
3015      }
3016
3017      var m = this._undo || new SVG.Matrix()
3018
3019      m = new SVG.Matrix().morph(SVG.Matrix.prototype[this.method].apply(m, params)).at(pos)
3020
3021      return this.inversed ? m.inverse() : m
3022
3023    }
3024
3025  , undo: function(o){
3026      for(var i = 0, len = this.arguments.length; i < len; ++i){
3027        o[this.arguments[i]] = typeof this[this.arguments[i]] == 'undefined' ? 0 : o[this.arguments[i]]
3028      }
3029
3030      // The method SVG.Matrix.extract which was used before calling this
3031      // method to obtain a value for the parameter o doesn't return a cx and
3032      // a cy so we use the ones that were provided to this object at its creation
3033      o.cx = this.cx
3034      o.cy = this.cy
3035
3036      this._undo = new SVG[capitalize(this.method)](o, true).at(1)
3037
3038      return this
3039    }
3040
3041  }
3042
3043})
3044
3045SVG.Translate = SVG.invent({
3046
3047  parent: SVG.Matrix
3048, inherit: SVG.Transformation
3049
3050, create: function(source, inversed){
3051    this.constructor.apply(this, [].slice.call(arguments))
3052  }
3053
3054, extend: {
3055    arguments: ['transformedX', 'transformedY']
3056  , method: 'translate'
3057  }
3058
3059})
3060
3061SVG.Rotate = SVG.invent({
3062
3063  parent: SVG.Matrix
3064, inherit: SVG.Transformation
3065
3066, create: function(source, inversed){
3067    this.constructor.apply(this, [].slice.call(arguments))
3068  }
3069
3070, extend: {
3071    arguments: ['rotation', 'cx', 'cy']
3072  , method: 'rotate'
3073  , at: function(pos){
3074      var m = new SVG.Matrix().rotate(new SVG.Number().morph(this.rotation - (this._undo ? this._undo.rotation : 0)).at(pos), this.cx, this.cy)
3075      return this.inversed ? m.inverse() : m
3076    }
3077  , undo: function(o){
3078      this._undo = o
3079      return this
3080    }
3081  }
3082
3083})
3084
3085SVG.Scale = SVG.invent({
3086
3087  parent: SVG.Matrix
3088, inherit: SVG.Transformation
3089
3090, create: function(source, inversed){
3091    this.constructor.apply(this, [].slice.call(arguments))
3092  }
3093
3094, extend: {
3095    arguments: ['scaleX', 'scaleY', 'cx', 'cy']
3096  , method: 'scale'
3097  }
3098
3099})
3100
3101SVG.Skew = SVG.invent({
3102
3103  parent: SVG.Matrix
3104, inherit: SVG.Transformation
3105
3106, create: function(source, inversed){
3107    this.constructor.apply(this, [].slice.call(arguments))
3108  }
3109
3110, extend: {
3111    arguments: ['skewX', 'skewY', 'cx', 'cy']
3112  , method: 'skew'
3113  }
3114
3115})
3116
3117SVG.extend(SVG.Element, {
3118  // Dynamic style generator
3119  style: function(s, v) {
3120    if (arguments.length == 0) {
3121      // get full style
3122      return this.node.style.cssText || ''
3123
3124    } else if (arguments.length < 2) {
3125      // apply every style individually if an object is passed
3126      if (typeof s == 'object') {
3127        for (v in s) this.style(v, s[v])
3128
3129      } else if (SVG.regex.isCss.test(s)) {
3130        // parse css string
3131        s = s.split(/\s*;\s*/)
3132          // filter out suffix ; and stuff like ;;
3133          .filter(function(e) { return !!e })
3134          .map(function(e){ return e.split(/\s*:\s*/) })
3135
3136        // apply every definition individually
3137        while (v = s.pop()) {
3138          this.style(v[0], v[1])
3139        }
3140      } else {
3141        // act as a getter if the first and only argument is not an object
3142        return this.node.style[camelCase(s)]
3143      }
3144
3145    } else {
3146      this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v
3147    }
3148
3149    return this
3150  }
3151})
3152SVG.Parent = SVG.invent({
3153  // Initialize node
3154  create: function(element) {
3155    this.constructor.call(this, element)
3156  }
3157
3158  // Inherit from
3159, inherit: SVG.Element
3160
3161  // Add class methods
3162, extend: {
3163    // Returns all child elements
3164    children: function() {
3165      return SVG.utils.map(SVG.utils.filterSVGElements(this.node.childNodes), function(node) {
3166        return SVG.adopt(node)
3167      })
3168    }
3169    // Add given element at a position
3170  , add: function(element, i) {
3171      if (i == null)
3172        this.node.appendChild(element.node)
3173      else if (element.node != this.node.childNodes[i])
3174        this.node.insertBefore(element.node, this.node.childNodes[i])
3175
3176      return this
3177    }
3178    // Basically does the same as `add()` but returns the added element instead
3179  , put: function(element, i) {
3180      this.add(element, i)
3181      return element
3182    }
3183    // Checks if the given element is a child
3184  , has: function(element) {
3185      return this.index(element) >= 0
3186    }
3187    // Gets index of given element
3188  , index: function(element) {
3189      return [].slice.call(this.node.childNodes).indexOf(element.node)
3190    }
3191    // Get a element at the given index
3192  , get: function(i) {
3193      return SVG.adopt(this.node.childNodes[i])
3194    }
3195    // Get first child
3196  , first: function() {
3197      return this.get(0)
3198    }
3199    // Get the last child
3200  , last: function() {
3201      return this.get(this.node.childNodes.length - 1)
3202    }
3203    // Iterates over all children and invokes a given block
3204  , each: function(block, deep) {
3205      var i, il
3206        , children = this.children()
3207
3208      for (i = 0, il = children.length; i < il; i++) {
3209        if (children[i] instanceof SVG.Element)
3210          block.apply(children[i], [i, children])
3211
3212        if (deep && (children[i] instanceof SVG.Container))
3213          children[i].each(block, deep)
3214      }
3215
3216      return this
3217    }
3218    // Remove a given child
3219  , removeElement: function(element) {
3220      this.node.removeChild(element.node)
3221
3222      return this
3223    }
3224    // Remove all elements in this container
3225  , clear: function() {
3226      // remove children
3227      while(this.node.hasChildNodes())
3228        this.node.removeChild(this.node.lastChild)
3229
3230      // remove defs reference
3231      delete this._defs
3232
3233      return this
3234    }
3235  , // Get defs
3236    defs: function() {
3237      return this.doc().defs()
3238    }
3239  }
3240
3241})
3242
3243SVG.extend(SVG.Parent, {
3244
3245  ungroup: function(parent, depth) {
3246    if(depth === 0 || this instanceof SVG.Defs || this.node == SVG.parser.draw) return this
3247
3248    parent = parent || (this instanceof SVG.Doc ? this : this.parent(SVG.Parent))
3249    depth = depth || Infinity
3250
3251    this.each(function(){
3252      if(this instanceof SVG.Defs) return this
3253      if(this instanceof SVG.Parent) return this.ungroup(parent, depth-1)
3254      return this.toParent(parent)
3255    })
3256
3257    this.node.firstChild || this.remove()
3258
3259    return this
3260  },
3261
3262  flatten: function(parent, depth) {
3263    return this.ungroup(parent, depth)
3264  }
3265
3266})
3267SVG.Container = SVG.invent({
3268  // Initialize node
3269  create: function(element) {
3270    this.constructor.call(this, element)
3271  }
3272
3273  // Inherit from
3274, inherit: SVG.Parent
3275
3276})
3277
3278SVG.ViewBox = SVG.invent({
3279
3280  create: function(source) {
3281    var i, base = [0, 0, 0, 0]
3282
3283    var x, y, width, height, box, view, we, he
3284      , wm   = 1 // width multiplier
3285      , hm   = 1 // height multiplier
3286      , reg  = /[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/gi
3287
3288    if(source instanceof SVG.Element){
3289
3290      we = source
3291      he = source
3292      view = (source.attr('viewBox') || '').match(reg)
3293      box = source.bbox
3294
3295      // get dimensions of current node
3296      width  = new SVG.Number(source.width())
3297      height = new SVG.Number(source.height())
3298
3299      // find nearest non-percentual dimensions
3300      while (width.unit == '%') {
3301        wm *= width.value
3302        width = new SVG.Number(we instanceof SVG.Doc ? we.parent().offsetWidth : we.parent().width())
3303        we = we.parent()
3304      }
3305      while (height.unit == '%') {
3306        hm *= height.value
3307        height = new SVG.Number(he instanceof SVG.Doc ? he.parent().offsetHeight : he.parent().height())
3308        he = he.parent()
3309      }
3310
3311      // ensure defaults
3312      this.x      = 0
3313      this.y      = 0
3314      this.width  = width  * wm
3315      this.height = height * hm
3316      this.zoom   = 1
3317
3318      if (view) {
3319        // get width and height from viewbox
3320        x      = parseFloat(view[0])
3321        y      = parseFloat(view[1])
3322        width  = parseFloat(view[2])
3323        height = parseFloat(view[3])
3324
3325        // calculate zoom accoring to viewbox
3326        this.zoom = ((this.width / this.height) > (width / height)) ?
3327          this.height / height :
3328          this.width  / width
3329
3330        // calculate real pixel dimensions on parent SVG.Doc element
3331        this.x      = x
3332        this.y      = y
3333        this.width  = width
3334        this.height = height
3335
3336      }
3337
3338    }else{
3339
3340      // ensure source as object
3341      source = typeof source === 'string' ?
3342        source.match(reg).map(function(el){ return parseFloat(el) }) :
3343      Array.isArray(source) ?
3344        source :
3345      typeof source == 'object' ?
3346        [source.x, source.y, source.width, source.height] :
3347      arguments.length == 4 ?
3348        [].slice.call(arguments) :
3349        base
3350
3351      this.x = source[0]
3352      this.y = source[1]
3353      this.width = source[2]
3354      this.height = source[3]
3355    }
3356
3357
3358  }
3359
3360, extend: {
3361
3362    toString: function() {
3363      return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height
3364    }
3365  , morph: function(x, y, width, height){
3366      this.destination = new SVG.ViewBox(x, y, width, height)
3367      return this
3368    }
3369
3370  , at: function(pos) {
3371
3372      if(!this.destination) return this
3373
3374      return new SVG.ViewBox([
3375          this.x + (this.destination.x - this.x) * pos
3376        , this.y + (this.destination.y - this.y) * pos
3377        , this.width + (this.destination.width - this.width) * pos
3378        , this.height + (this.destination.height - this.height) * pos
3379      ])
3380
3381    }
3382
3383  }
3384
3385  // Define parent
3386, parent: SVG.Container
3387
3388  // Add parent method
3389, construct: {
3390
3391    // get/set viewbox
3392    viewbox: function(x, y, width, height) {
3393      if (arguments.length == 0)
3394        // act as a getter if there are no arguments
3395        return new SVG.ViewBox(this)
3396
3397      // otherwise act as a setter
3398      return this.attr('viewBox', new SVG.ViewBox(x, y, width, height))
3399    }
3400
3401  }
3402
3403})
3404// Add events to elements
3405
3406;[ 'click',
3407  'dblclick',
3408  'mousedown',
3409  'mouseup',
3410  'mouseover',
3411  'mouseout',
3412  'mousemove',
3413  'mouseenter',
3414  'mouseleave',
3415  'touchstart',
3416  'touchmove',
3417  'touchleave',
3418  'touchend',
3419  'touchcancel' ].forEach(function (event) {
3420    // add event to SVG.Element
3421    SVG.Element.prototype[event] = function (f) {
3422      // bind event to element rather than element node
3423      if (f == null) {
3424        SVG.off(this, event)
3425      } else {
3426        SVG.on(this, event, f)
3427      }
3428      return this
3429    }
3430  })
3431
3432SVG.listenerId = 0
3433
3434// Add event binder in the SVG namespace
3435SVG.on = function (node, events, listener, binding, options) {
3436  var l = listener.bind(binding || node)
3437  var n = node instanceof SVG.Element ? node.node : node
3438
3439  // ensure instance object for nodes which are not adopted
3440  n.instance = n.instance || {_events: {}}
3441
3442  var bag = n.instance._events
3443
3444  // add id to listener
3445  if (!listener._svgjsListenerId) { listener._svgjsListenerId = ++SVG.listenerId }
3446
3447  events.split(SVG.regex.delimiter).forEach(function (event) {
3448    var ev = event.split('.')[0]
3449    var ns = event.split('.')[1] || '*'
3450
3451    // ensure valid object
3452    bag[ev] = bag[ev] || {}
3453    bag[ev][ns] = bag[ev][ns] || {}
3454
3455    // reference listener
3456    bag[ev][ns][listener._svgjsListenerId] = l
3457
3458    // add listener
3459    n.addEventListener(ev, l, options || false)
3460  })
3461}
3462
3463// Add event unbinder in the SVG namespace
3464SVG.off = function (node, events, listener, options) {
3465  var n = node instanceof SVG.Element ? node.node : node
3466  if (!n.instance) return
3467
3468  // listener can be a function or a number
3469  if (typeof listener === 'function') {
3470    listener = listener._svgjsListenerId
3471    if (!listener) return
3472  }
3473
3474  var bag = n.instance._events
3475
3476  ;(events || '').split(SVG.regex.delimiter).forEach(function (event) {
3477    var ev = event && event.split('.')[0]
3478    var ns = event && event.split('.')[1]
3479    var namespace, l
3480
3481    if (listener) {
3482      // remove listener reference
3483      if (bag[ev] && bag[ev][ns || '*']) {
3484        // removeListener
3485        n.removeEventListener(ev, bag[ev][ns || '*'][listener], options || false)
3486
3487        delete bag[ev][ns || '*'][listener]
3488      }
3489    } else if (ev && ns) {
3490      // remove all listeners for a namespaced event
3491      if (bag[ev] && bag[ev][ns]) {
3492        for (l in bag[ev][ns]) { SVG.off(n, [ev, ns].join('.'), l) }
3493
3494        delete bag[ev][ns]
3495      }
3496    } else if (ns) {
3497      // remove all listeners for a specific namespace
3498      for (event in bag) {
3499        for (namespace in bag[event]) {
3500          if (ns === namespace) { SVG.off(n, [event, ns].join('.')) }
3501        }
3502      }
3503    } else if (ev) {
3504      // remove all listeners for the event
3505      if (bag[ev]) {
3506        for (namespace in bag[ev]) { SVG.off(n, [ev, namespace].join('.')) }
3507
3508        delete bag[ev]
3509      }
3510    } else {
3511      // remove all listeners on a given node
3512      for (event in bag) { SVG.off(n, event) }
3513
3514      n.instance._events = {}
3515    }
3516  })
3517}
3518
3519SVG.extend(SVG.Element, {
3520  // Bind given event to listener
3521  on: function (event, listener, binding, options) {
3522    SVG.on(this, event, listener, binding, options)
3523    return this
3524  },
3525  // Unbind event from listener
3526  off: function (event, listener) {
3527    SVG.off(this.node, event, listener)
3528    return this
3529  },
3530  fire: function (event, data) {
3531    // Dispatch event
3532    if (event instanceof window.Event) {
3533      this.node.dispatchEvent(event)
3534    } else {
3535      this.node.dispatchEvent(event = new SVG.CustomEvent(event, {detail: data, cancelable: true}))
3536    }
3537    this._event = event
3538    return this
3539  },
3540  event: function() {
3541    return this._event
3542  }
3543})
3544
3545
3546SVG.Defs = SVG.invent({
3547  // Initialize node
3548  create: 'defs'
3549
3550  // Inherit from
3551, inherit: SVG.Container
3552
3553})
3554SVG.G = SVG.invent({
3555  // Initialize node
3556  create: 'g'
3557
3558  // Inherit from
3559, inherit: SVG.Container
3560
3561  // Add class methods
3562, extend: {
3563    // Move over x-axis
3564    x: function(x) {
3565      return x == null ? this.transform('x') : this.transform({ x: x - this.x() }, true)
3566    }
3567    // Move over y-axis
3568  , y: function(y) {
3569      return y == null ? this.transform('y') : this.transform({ y: y - this.y() }, true)
3570    }
3571    // Move by center over x-axis
3572  , cx: function(x) {
3573      return x == null ? this.gbox().cx : this.x(x - this.gbox().width / 2)
3574    }
3575    // Move by center over y-axis
3576  , cy: function(y) {
3577      return y == null ? this.gbox().cy : this.y(y - this.gbox().height / 2)
3578    }
3579  , gbox: function() {
3580
3581      var bbox  = this.bbox()
3582        , trans = this.transform()
3583
3584      bbox.x  += trans.x
3585      bbox.x2 += trans.x
3586      bbox.cx += trans.x
3587
3588      bbox.y  += trans.y
3589      bbox.y2 += trans.y
3590      bbox.cy += trans.y
3591
3592      return bbox
3593    }
3594  }
3595
3596  // Add parent method
3597, construct: {
3598    // Create a group element
3599    group: function() {
3600      return this.put(new SVG.G)
3601    }
3602  }
3603})
3604
3605SVG.Doc = SVG.invent({
3606  // Initialize node
3607  create: function(element) {
3608    if (element) {
3609      // ensure the presence of a dom element
3610      element = typeof element == 'string' ?
3611        document.getElementById(element) :
3612        element
3613
3614      // If the target is an svg element, use that element as the main wrapper.
3615      // This allows svg.js to work with svg documents as well.
3616      if (element.nodeName == 'svg') {
3617        this.constructor.call(this, element)
3618      } else {
3619        this.constructor.call(this, SVG.create('svg'))
3620        element.appendChild(this.node)
3621        this.size('100%', '100%')
3622      }
3623
3624      // set svg element attributes and ensure defs node
3625      this.namespace().defs()
3626    }
3627  }
3628
3629  // Inherit from
3630, inherit: SVG.Container
3631
3632  // Add class methods
3633, extend: {
3634    // Add namespaces
3635    namespace: function() {
3636      return this
3637        .attr({ xmlns: SVG.ns, version: '1.1' })
3638        .attr('xmlns:xlink', SVG.xlink, SVG.xmlns)
3639        .attr('xmlns:svgjs', SVG.svgjs, SVG.xmlns)
3640    }
3641    // Creates and returns defs element
3642  , defs: function() {
3643      if (!this._defs) {
3644        var defs
3645
3646        // Find or create a defs element in this instance
3647        if (defs = this.node.getElementsByTagName('defs')[0])
3648          this._defs = SVG.adopt(defs)
3649        else
3650          this._defs = new SVG.Defs
3651
3652        // Make sure the defs node is at the end of the stack
3653        this.node.appendChild(this._defs.node)
3654      }
3655
3656      return this._defs
3657    }
3658    // custom parent method
3659  , parent: function() {
3660      if(!this.node.parentNode || this.node.parentNode.nodeName == '#document' || this.node.parentNode.nodeName == '#document-fragment') return null
3661      return this.node.parentNode
3662    }
3663    // Fix for possible sub-pixel offset. See:
3664    // https://bugzilla.mozilla.org/show_bug.cgi?id=608812
3665  , spof: function() {
3666      var pos = this.node.getScreenCTM()
3667
3668      if (pos)
3669        this
3670          .style('left', (-pos.e % 1) + 'px')
3671          .style('top',  (-pos.f % 1) + 'px')
3672
3673      return this
3674    }
3675
3676      // Removes the doc from the DOM
3677  , remove: function() {
3678      if(this.parent()) {
3679        this.parent().removeChild(this.node)
3680      }
3681
3682      return this
3683    }
3684  , clear: function() {
3685      // remove children
3686      while(this.node.hasChildNodes())
3687        this.node.removeChild(this.node.lastChild)
3688
3689      // remove defs reference
3690      delete this._defs
3691
3692      // add back parser
3693      if(!SVG.parser.draw.parentNode)
3694        this.node.appendChild(SVG.parser.draw)
3695
3696      return this
3697    }
3698  , clone: function (parent) {
3699      // write dom data to the dom so the clone can pickup the data
3700      this.writeDataToDom()
3701
3702      // get reference to node
3703      var node = this.node
3704
3705      // clone element and assign new id
3706      var clone = assignNewId(node.cloneNode(true))
3707
3708      // insert the clone in the given parent or after myself
3709      if(parent) {
3710        (parent.node || parent).appendChild(clone.node)
3711      } else {
3712        node.parentNode.insertBefore(clone.node, node.nextSibling)
3713      }
3714
3715      return clone
3716    }
3717  }
3718
3719})
3720
3721// ### This module adds backward / forward functionality to elements.
3722
3723//
3724SVG.extend(SVG.Element, {
3725  // Get all siblings, including myself
3726  siblings: function() {
3727    return this.parent().children()
3728  }
3729  // Get the curent position siblings
3730, position: function() {
3731    return this.parent().index(this)
3732  }
3733  // Get the next element (will return null if there is none)
3734, next: function() {
3735    return this.siblings()[this.position() + 1]
3736  }
3737  // Get the next element (will return null if there is none)
3738, previous: function() {
3739    return this.siblings()[this.position() - 1]
3740  }
3741  // Send given element one step forward
3742, forward: function() {
3743    var i = this.position() + 1
3744      , p = this.parent()
3745
3746    // move node one step forward
3747    p.removeElement(this).add(this, i)
3748
3749    // make sure defs node is always at the top
3750    if (p instanceof SVG.Doc)
3751      p.node.appendChild(p.defs().node)
3752
3753    return this
3754  }
3755  // Send given element one step backward
3756, backward: function() {
3757    var i = this.position()
3758
3759    if (i > 0)
3760      this.parent().removeElement(this).add(this, i - 1)
3761
3762    return this
3763  }
3764  // Send given element all the way to the front
3765, front: function() {
3766    var p = this.parent()
3767
3768    // Move node forward
3769    p.node.appendChild(this.node)
3770
3771    // Make sure defs node is always at the top
3772    if (p instanceof SVG.Doc)
3773      p.node.appendChild(p.defs().node)
3774
3775    return this
3776  }
3777  // Send given element all the way to the back
3778, back: function() {
3779    if (this.position() > 0)
3780      this.parent().removeElement(this).add(this, 0)
3781
3782    return this
3783  }
3784  // Inserts a given element before the targeted element
3785, before: function(element) {
3786    element.remove()
3787
3788    var i = this.position()
3789
3790    this.parent().add(element, i)
3791
3792    return this
3793  }
3794  // Insters a given element after the targeted element
3795, after: function(element) {
3796    element.remove()
3797
3798    var i = this.position()
3799
3800    this.parent().add(element, i + 1)
3801
3802    return this
3803  }
3804
3805})
3806SVG.Mask = SVG.invent({
3807  // Initialize node
3808  create: function() {
3809    this.constructor.call(this, SVG.create('mask'))
3810
3811    // keep references to masked elements
3812    this.targets = []
3813  }
3814
3815  // Inherit from
3816, inherit: SVG.Container
3817
3818  // Add class methods
3819, extend: {
3820    // Unmask all masked elements and remove itself
3821    remove: function() {
3822      // unmask all targets
3823      for (var i = this.targets.length - 1; i >= 0; i--)
3824        if (this.targets[i])
3825          this.targets[i].unmask()
3826      this.targets = []
3827
3828      // remove mask from parent
3829      SVG.Element.prototype.remove.call(this)
3830
3831      return this
3832    }
3833  }
3834
3835  // Add parent method
3836, construct: {
3837    // Create masking element
3838    mask: function() {
3839      return this.defs().put(new SVG.Mask)
3840    }
3841  }
3842})
3843
3844
3845SVG.extend(SVG.Element, {
3846  // Distribute mask to svg element
3847  maskWith: function(element) {
3848    // use given mask or create a new one
3849    this.masker = element instanceof SVG.Mask ? element : this.parent().mask().add(element)
3850
3851    // store reverence on self in mask
3852    this.masker.targets.push(this)
3853
3854    // apply mask
3855    return this.attr('mask', 'url("#' + this.masker.attr('id') + '")')
3856  }
3857  // Unmask element
3858, unmask: function() {
3859    delete this.masker
3860    return this.attr('mask', null)
3861  }
3862
3863})
3864
3865SVG.ClipPath = SVG.invent({
3866  // Initialize node
3867  create: function() {
3868    this.constructor.call(this, SVG.create('clipPath'))
3869
3870    // keep references to clipped elements
3871    this.targets = []
3872  }
3873
3874  // Inherit from
3875, inherit: SVG.Container
3876
3877  // Add class methods
3878, extend: {
3879    // Unclip all clipped elements and remove itself
3880    remove: function() {
3881      // unclip all targets
3882      for (var i = this.targets.length - 1; i >= 0; i--)
3883        if (this.targets[i])
3884          this.targets[i].unclip()
3885      this.targets = []
3886
3887      // remove clipPath from parent
3888      this.parent().removeElement(this)
3889
3890      return this
3891    }
3892  }
3893
3894  // Add parent method
3895, construct: {
3896    // Create clipping element
3897    clip: function() {
3898      return this.defs().put(new SVG.ClipPath)
3899    }
3900  }
3901})
3902
3903//
3904SVG.extend(SVG.Element, {
3905  // Distribute clipPath to svg element
3906  clipWith: function(element) {
3907    // use given clip or create a new one
3908    this.clipper = element instanceof SVG.ClipPath ? element : this.parent().clip().add(element)
3909
3910    // store reverence on self in mask
3911    this.clipper.targets.push(this)
3912
3913    // apply mask
3914    return this.attr('clip-path', 'url("#' + this.clipper.attr('id') + '")')
3915  }
3916  // Unclip element
3917, unclip: function() {
3918    delete this.clipper
3919    return this.attr('clip-path', null)
3920  }
3921
3922})
3923SVG.Gradient = SVG.invent({
3924  // Initialize node
3925  create: function(type) {
3926    this.constructor.call(this, SVG.create(type + 'Gradient'))
3927
3928    // store type
3929    this.type = type
3930  }
3931
3932  // Inherit from
3933, inherit: SVG.Container
3934
3935  // Add class methods
3936, extend: {
3937    // Add a color stop
3938    at: function(offset, color, opacity) {
3939      return this.put(new SVG.Stop).update(offset, color, opacity)
3940    }
3941    // Update gradient
3942  , update: function(block) {
3943      // remove all stops
3944      this.clear()
3945
3946      // invoke passed block
3947      if (typeof block == 'function')
3948        block.call(this, this)
3949
3950      return this
3951    }
3952    // Return the fill id
3953  , fill: function() {
3954      return 'url(#' + this.id() + ')'
3955    }
3956    // Alias string convertion to fill
3957  , toString: function() {
3958      return this.fill()
3959    }
3960    // custom attr to handle transform
3961  , attr: function(a, b, c) {
3962      if(a == 'transform') a = 'gradientTransform'
3963      return SVG.Container.prototype.attr.call(this, a, b, c)
3964    }
3965  }
3966
3967  // Add parent method
3968, construct: {
3969    // Create gradient element in defs
3970    gradient: function(type, block) {
3971      return this.defs().gradient(type, block)
3972    }
3973  }
3974})
3975
3976// Add animatable methods to both gradient and fx module
3977SVG.extend(SVG.Gradient, SVG.FX, {
3978  // From position
3979  from: function(x, y) {
3980    return (this._target || this).type == 'radial' ?
3981      this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) :
3982      this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) })
3983  }
3984  // To position
3985, to: function(x, y) {
3986    return (this._target || this).type == 'radial' ?
3987      this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) :
3988      this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) })
3989  }
3990})
3991
3992// Base gradient generation
3993SVG.extend(SVG.Defs, {
3994  // define gradient
3995  gradient: function(type, block) {
3996    return this.put(new SVG.Gradient(type)).update(block)
3997  }
3998
3999})
4000
4001SVG.Stop = SVG.invent({
4002  // Initialize node
4003  create: 'stop'
4004
4005  // Inherit from
4006, inherit: SVG.Element
4007
4008  // Add class methods
4009, extend: {
4010    // add color stops
4011    update: function(o) {
4012      if (typeof o == 'number' || o instanceof SVG.Number) {
4013        o = {
4014          offset:  arguments[0]
4015        , color:   arguments[1]
4016        , opacity: arguments[2]
4017        }
4018      }
4019
4020      // set attributes
4021      if (o.opacity != null) this.attr('stop-opacity', o.opacity)
4022      if (o.color   != null) this.attr('stop-color', o.color)
4023      if (o.offset  != null) this.attr('offset', new SVG.Number(o.offset))
4024
4025      return this
4026    }
4027  }
4028
4029})
4030
4031SVG.Pattern = SVG.invent({
4032  // Initialize node
4033  create: 'pattern'
4034
4035  // Inherit from
4036, inherit: SVG.Container
4037
4038  // Add class methods
4039, extend: {
4040    // Return the fill id
4041    fill: function() {
4042      return 'url(#' + this.id() + ')'
4043    }
4044    // Update pattern by rebuilding
4045  , update: function(block) {
4046      // remove content
4047      this.clear()
4048
4049      // invoke passed block
4050      if (typeof block == 'function')
4051        block.call(this, this)
4052
4053      return this
4054    }
4055    // Alias string convertion to fill
4056  , toString: function() {
4057      return this.fill()
4058    }
4059    // custom attr to handle transform
4060  , attr: function(a, b, c) {
4061      if(a == 'transform') a = 'patternTransform'
4062      return SVG.Container.prototype.attr.call(this, a, b, c)
4063    }
4064
4065  }
4066
4067  // Add parent method
4068, construct: {
4069    // Create pattern element in defs
4070    pattern: function(width, height, block) {
4071      return this.defs().pattern(width, height, block)
4072    }
4073  }
4074})
4075
4076SVG.extend(SVG.Defs, {
4077  // Define gradient
4078  pattern: function(width, height, block) {
4079    return this.put(new SVG.Pattern).update(block).attr({
4080      x:            0
4081    , y:            0
4082    , width:        width
4083    , height:       height
4084    , patternUnits: 'userSpaceOnUse'
4085    })
4086  }
4087
4088})
4089SVG.Shape = SVG.invent({
4090  // Initialize node
4091  create: function(element) {
4092    this.constructor.call(this, element)
4093  }
4094
4095  // Inherit from
4096, inherit: SVG.Element
4097
4098})
4099
4100SVG.Bare = SVG.invent({
4101  // Initialize
4102  create: function(element, inherit) {
4103    // construct element
4104    this.constructor.call(this, SVG.create(element))
4105
4106    // inherit custom methods
4107    if (inherit)
4108      for (var method in inherit.prototype)
4109        if (typeof inherit.prototype[method] === 'function')
4110          this[method] = inherit.prototype[method]
4111  }
4112
4113  // Inherit from
4114, inherit: SVG.Element
4115
4116  // Add methods
4117, extend: {
4118    // Insert some plain text
4119    words: function(text) {
4120      // remove contents
4121      while (this.node.hasChildNodes())
4122        this.node.removeChild(this.node.lastChild)
4123
4124      // create text node
4125      this.node.appendChild(document.createTextNode(text))
4126
4127      return this
4128    }
4129  }
4130})
4131
4132
4133SVG.extend(SVG.Parent, {
4134  // Create an element that is not described by SVG.js
4135  element: function(element, inherit) {
4136    return this.put(new SVG.Bare(element, inherit))
4137  }
4138})
4139
4140SVG.Symbol = SVG.invent({
4141  // Initialize node
4142  create: 'symbol'
4143
4144  // Inherit from
4145, inherit: SVG.Container
4146
4147, construct: {
4148    // create symbol
4149    symbol: function() {
4150      return this.put(new SVG.Symbol)
4151    }
4152  }
4153})
4154
4155SVG.Use = SVG.invent({
4156  // Initialize node
4157  create: 'use'
4158
4159  // Inherit from
4160, inherit: SVG.Shape
4161
4162  // Add class methods
4163, extend: {
4164    // Use element as a reference
4165    element: function(element, file) {
4166      // Set lined element
4167      return this.attr('href', (file || '') + '#' + element, SVG.xlink)
4168    }
4169  }
4170
4171  // Add parent method
4172, construct: {
4173    // Create a use element
4174    use: function(element, file) {
4175      return this.put(new SVG.Use).element(element, file)
4176    }
4177  }
4178})
4179SVG.Rect = SVG.invent({
4180  // Initialize node
4181  create: 'rect'
4182
4183  // Inherit from
4184, inherit: SVG.Shape
4185
4186  // Add parent method
4187, construct: {
4188    // Create a rect element
4189    rect: function(width, height) {
4190      return this.put(new SVG.Rect()).size(width, height)
4191    }
4192  }
4193})
4194SVG.Circle = SVG.invent({
4195  // Initialize node
4196  create: 'circle'
4197
4198  // Inherit from
4199, inherit: SVG.Shape
4200
4201  // Add parent method
4202, construct: {
4203    // Create circle element, based on ellipse
4204    circle: function(size) {
4205      return this.put(new SVG.Circle).rx(new SVG.Number(size).divide(2)).move(0, 0)
4206    }
4207  }
4208})
4209
4210SVG.extend(SVG.Circle, SVG.FX, {
4211  // Radius x value
4212  rx: function(rx) {
4213    return this.attr('r', rx)
4214  }
4215  // Alias radius x value
4216, ry: function(ry) {
4217    return this.rx(ry)
4218  }
4219})
4220
4221SVG.Ellipse = SVG.invent({
4222  // Initialize node
4223  create: 'ellipse'
4224
4225  // Inherit from
4226, inherit: SVG.Shape
4227
4228  // Add parent method
4229, construct: {
4230    // Create an ellipse
4231    ellipse: function(width, height) {
4232      return this.put(new SVG.Ellipse).size(width, height).move(0, 0)
4233    }
4234  }
4235})
4236
4237SVG.extend(SVG.Ellipse, SVG.Rect, SVG.FX, {
4238  // Radius x value
4239  rx: function(rx) {
4240    return this.attr('rx', rx)
4241  }
4242  // Radius y value
4243, ry: function(ry) {
4244    return this.attr('ry', ry)
4245  }
4246})
4247
4248// Add common method
4249SVG.extend(SVG.Circle, SVG.Ellipse, {
4250    // Move over x-axis
4251    x: function(x) {
4252      return x == null ? this.cx() - this.rx() : this.cx(x + this.rx())
4253    }
4254    // Move over y-axis
4255  , y: function(y) {
4256      return y == null ? this.cy() - this.ry() : this.cy(y + this.ry())
4257    }
4258    // Move by center over x-axis
4259  , cx: function(x) {
4260      return x == null ? this.attr('cx') : this.attr('cx', x)
4261    }
4262    // Move by center over y-axis
4263  , cy: function(y) {
4264      return y == null ? this.attr('cy') : this.attr('cy', y)
4265    }
4266    // Set width of element
4267  , width: function(width) {
4268      return width == null ? this.rx() * 2 : this.rx(new SVG.Number(width).divide(2))
4269    }
4270    // Set height of element
4271  , height: function(height) {
4272      return height == null ? this.ry() * 2 : this.ry(new SVG.Number(height).divide(2))
4273    }
4274    // Custom size function
4275  , size: function(width, height) {
4276      var p = proportionalSize(this, width, height)
4277
4278      return this
4279        .rx(new SVG.Number(p.width).divide(2))
4280        .ry(new SVG.Number(p.height).divide(2))
4281    }
4282})
4283SVG.Line = SVG.invent({
4284  // Initialize node
4285  create: 'line'
4286
4287  // Inherit from
4288, inherit: SVG.Shape
4289
4290  // Add class methods
4291, extend: {
4292    // Get array
4293    array: function() {
4294      return new SVG.PointArray([
4295        [ this.attr('x1'), this.attr('y1') ]
4296      , [ this.attr('x2'), this.attr('y2') ]
4297      ])
4298    }
4299    // Overwrite native plot() method
4300  , plot: function(x1, y1, x2, y2) {
4301      if (x1 == null)
4302        return this.array()
4303      else if (typeof y1 !== 'undefined')
4304        x1 = { x1: x1, y1: y1, x2: x2, y2: y2 }
4305      else
4306        x1 = new SVG.PointArray(x1).toLine()
4307
4308      return this.attr(x1)
4309    }
4310    // Move by left top corner
4311  , move: function(x, y) {
4312      return this.attr(this.array().move(x, y).toLine())
4313    }
4314    // Set element size to given width and height
4315  , size: function(width, height) {
4316      var p = proportionalSize(this, width, height)
4317
4318      return this.attr(this.array().size(p.width, p.height).toLine())
4319    }
4320  }
4321
4322  // Add parent method
4323, construct: {
4324    // Create a line element
4325    line: function(x1, y1, x2, y2) {
4326      // make sure plot is called as a setter
4327      // x1 is not necessarily a number, it can also be an array, a string and a SVG.PointArray
4328      return SVG.Line.prototype.plot.apply(
4329        this.put(new SVG.Line)
4330      , x1 != null ? [x1, y1, x2, y2] : [0, 0, 0, 0]
4331      )
4332    }
4333  }
4334})
4335
4336SVG.Polyline = SVG.invent({
4337  // Initialize node
4338  create: 'polyline'
4339
4340  // Inherit from
4341, inherit: SVG.Shape
4342
4343  // Add parent method
4344, construct: {
4345    // Create a wrapped polyline element
4346    polyline: function(p) {
4347      // make sure plot is called as a setter
4348      return this.put(new SVG.Polyline).plot(p || new SVG.PointArray)
4349    }
4350  }
4351})
4352
4353SVG.Polygon = SVG.invent({
4354  // Initialize node
4355  create: 'polygon'
4356
4357  // Inherit from
4358, inherit: SVG.Shape
4359
4360  // Add parent method
4361, construct: {
4362    // Create a wrapped polygon element
4363    polygon: function(p) {
4364      // make sure plot is called as a setter
4365      return this.put(new SVG.Polygon).plot(p || new SVG.PointArray)
4366    }
4367  }
4368})
4369
4370// Add polygon-specific functions
4371SVG.extend(SVG.Polyline, SVG.Polygon, {
4372  // Get array
4373  array: function() {
4374    return this._array || (this._array = new SVG.PointArray(this.attr('points')))
4375  }
4376  // Plot new path
4377, plot: function(p) {
4378    return (p == null) ?
4379      this.array() :
4380      this.clear().attr('points', typeof p == 'string' ? p : (this._array = new SVG.PointArray(p)))
4381  }
4382  // Clear array cache
4383, clear: function() {
4384    delete this._array
4385    return this
4386  }
4387  // Move by left top corner
4388, move: function(x, y) {
4389    return this.attr('points', this.array().move(x, y))
4390  }
4391  // Set element size to given width and height
4392, size: function(width, height) {
4393    var p = proportionalSize(this, width, height)
4394
4395    return this.attr('points', this.array().size(p.width, p.height))
4396  }
4397
4398})
4399
4400// unify all point to point elements
4401SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, {
4402  // Define morphable array
4403  morphArray:  SVG.PointArray
4404  // Move by left top corner over x-axis
4405, x: function(x) {
4406    return x == null ? this.bbox().x : this.move(x, this.bbox().y)
4407  }
4408  // Move by left top corner over y-axis
4409, y: function(y) {
4410    return y == null ? this.bbox().y : this.move(this.bbox().x, y)
4411  }
4412  // Set width of element
4413, width: function(width) {
4414    var b = this.bbox()
4415
4416    return width == null ? b.width : this.size(width, b.height)
4417  }
4418  // Set height of element
4419, height: function(height) {
4420    var b = this.bbox()
4421
4422    return height == null ? b.height : this.size(b.width, height)
4423  }
4424})
4425SVG.Path = SVG.invent({
4426  // Initialize node
4427  create: 'path'
4428
4429  // Inherit from
4430, inherit: SVG.Shape
4431
4432  // Add class methods
4433, extend: {
4434    // Define morphable array
4435    morphArray:  SVG.PathArray
4436    // Get array
4437  , array: function() {
4438      return this._array || (this._array = new SVG.PathArray(this.attr('d')))
4439    }
4440    // Plot new path
4441  , plot: function(d) {
4442      return (d == null) ?
4443        this.array() :
4444        this.clear().attr('d', typeof d == 'string' ? d : (this._array = new SVG.PathArray(d)))
4445    }
4446    // Clear array cache
4447  , clear: function() {
4448      delete this._array
4449      return this
4450    }
4451    // Move by left top corner
4452  , move: function(x, y) {
4453      return this.attr('d', this.array().move(x, y))
4454    }
4455    // Move by left top corner over x-axis
4456  , x: function(x) {
4457      return x == null ? this.bbox().x : this.move(x, this.bbox().y)
4458    }
4459    // Move by left top corner over y-axis
4460  , y: function(y) {
4461      return y == null ? this.bbox().y : this.move(this.bbox().x, y)
4462    }
4463    // Set element size to given width and height
4464  , size: function(width, height) {
4465      var p = proportionalSize(this, width, height)
4466
4467      return this.attr('d', this.array().size(p.width, p.height))
4468    }
4469    // Set width of element
4470  , width: function(width) {
4471      return width == null ? this.bbox().width : this.size(width, this.bbox().height)
4472    }
4473    // Set height of element
4474  , height: function(height) {
4475      return height == null ? this.bbox().height : this.size(this.bbox().width, height)
4476    }
4477
4478  }
4479
4480  // Add parent method
4481, construct: {
4482    // Create a wrapped path element
4483    path: function(d) {
4484      // make sure plot is called as a setter
4485      return this.put(new SVG.Path).plot(d || new SVG.PathArray)
4486    }
4487  }
4488})
4489
4490SVG.Image = SVG.invent({
4491  // Initialize node
4492  create: 'image'
4493
4494  // Inherit from
4495, inherit: SVG.Shape
4496
4497  // Add class methods
4498, extend: {
4499    // (re)load image
4500    load: function(url) {
4501      if (!url) return this
4502
4503      var self = this
4504        , img  = new window.Image()
4505
4506      // preload image
4507      SVG.on(img, 'load', function() {
4508        SVG.off(img)
4509
4510        var p = self.parent(SVG.Pattern)
4511
4512        if(p === null) return
4513
4514        // ensure image size
4515        if (self.width() == 0 && self.height() == 0)
4516          self.size(img.width, img.height)
4517
4518        // ensure pattern size if not set
4519        if (p && p.width() == 0 && p.height() == 0)
4520          p.size(self.width(), self.height())
4521
4522        // callback
4523        if (typeof self._loaded === 'function')
4524          self._loaded.call(self, {
4525            width:  img.width
4526          , height: img.height
4527          , ratio:  img.width / img.height
4528          , url:    url
4529          })
4530      })
4531
4532      SVG.on(img, 'error', function(e){
4533        SVG.off(img)
4534
4535        if (typeof self._error === 'function'){
4536            self._error.call(self, e)
4537        }
4538      })
4539
4540      return this.attr('href', (img.src = this.src = url), SVG.xlink)
4541    }
4542    // Add loaded callback
4543  , loaded: function(loaded) {
4544      this._loaded = loaded
4545      return this
4546    }
4547
4548  , error: function(error) {
4549      this._error = error
4550      return this
4551    }
4552  }
4553
4554  // Add parent method
4555, construct: {
4556    // create image element, load image and set its size
4557    image: function(source, width, height) {
4558      return this.put(new SVG.Image).load(source).size(width || 0, height || width || 0)
4559    }
4560  }
4561
4562})
4563SVG.Text = SVG.invent({
4564  // Initialize node
4565  create: function() {
4566    this.constructor.call(this, SVG.create('text'))
4567
4568    this.dom.leading = new SVG.Number(1.3)    // store leading value for rebuilding
4569    this._rebuild = true                      // enable automatic updating of dy values
4570    this._build   = false                     // disable build mode for adding multiple lines
4571
4572    // set default font
4573    this.attr('font-family', SVG.defaults.attrs['font-family'])
4574  }
4575
4576  // Inherit from
4577, inherit: SVG.Shape
4578
4579  // Add class methods
4580, extend: {
4581    // Move over x-axis
4582    x: function(x) {
4583      // act as getter
4584      if (x == null)
4585        return this.attr('x')
4586
4587      return this.attr('x', x)
4588    }
4589    // Move over y-axis
4590  , y: function(y) {
4591      var oy = this.attr('y')
4592        , o  = typeof oy === 'number' ? oy - this.bbox().y : 0
4593
4594      // act as getter
4595      if (y == null)
4596        return typeof oy === 'number' ? oy - o : oy
4597
4598      return this.attr('y', typeof y.valueOf() === 'number' ? y + o : y)
4599    }
4600    // Move center over x-axis
4601  , cx: function(x) {
4602      return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2)
4603    }
4604    // Move center over y-axis
4605  , cy: function(y) {
4606      return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2)
4607    }
4608    // Set the text content
4609  , text: function(text) {
4610      // act as getter
4611      if (typeof text === 'undefined'){
4612        var text = ''
4613        var children = this.node.childNodes
4614        for(var i = 0, len = children.length; i < len; ++i){
4615
4616          // add newline if its not the first child and newLined is set to true
4617          if(i != 0 && children[i].nodeType != 3 && SVG.adopt(children[i]).dom.newLined == true){
4618            text += '\n'
4619          }
4620
4621          // add content of this node
4622          text += children[i].textContent
4623        }
4624
4625        return text
4626      }
4627
4628      // remove existing content
4629      this.clear().build(true)
4630
4631      if (typeof text === 'function') {
4632        // call block
4633        text.call(this, this)
4634
4635      } else {
4636        // store text and make sure text is not blank
4637        text = text.split('\n')
4638
4639        // build new lines
4640        for (var i = 0, il = text.length; i < il; i++)
4641          this.tspan(text[i]).newLine()
4642      }
4643
4644      // disable build mode and rebuild lines
4645      return this.build(false).rebuild()
4646    }
4647    // Set font size
4648  , size: function(size) {
4649      return this.attr('font-size', size).rebuild()
4650    }
4651    // Set / get leading
4652  , leading: function(value) {
4653      // act as getter
4654      if (value == null)
4655        return this.dom.leading
4656
4657      // act as setter
4658      this.dom.leading = new SVG.Number(value)
4659
4660      return this.rebuild()
4661    }
4662    // Get all the first level lines
4663  , lines: function() {
4664      var node = (this.textPath && this.textPath() || this).node
4665
4666      // filter tspans and map them to SVG.js instances
4667      var lines = SVG.utils.map(SVG.utils.filterSVGElements(node.childNodes), function(el){
4668        return SVG.adopt(el)
4669      })
4670
4671      // return an instance of SVG.set
4672      return new SVG.Set(lines)
4673    }
4674    // Rebuild appearance type
4675  , rebuild: function(rebuild) {
4676      // store new rebuild flag if given
4677      if (typeof rebuild == 'boolean')
4678        this._rebuild = rebuild
4679
4680      // define position of all lines
4681      if (this._rebuild) {
4682        var self = this
4683          , blankLineOffset = 0
4684          , dy = this.dom.leading * new SVG.Number(this.attr('font-size'))
4685
4686        this.lines().each(function() {
4687          if (this.dom.newLined) {
4688            if (!self.textPath())
4689              this.attr('x', self.attr('x'))
4690            if(this.text() == '\n') {
4691              blankLineOffset += dy
4692            }else{
4693              this.attr('dy', dy + blankLineOffset)
4694              blankLineOffset = 0
4695            }
4696          }
4697        })
4698
4699        this.fire('rebuild')
4700      }
4701
4702      return this
4703    }
4704    // Enable / disable build mode
4705  , build: function(build) {
4706      this._build = !!build
4707      return this
4708    }
4709    // overwrite method from parent to set data properly
4710  , setData: function(o){
4711      this.dom = o
4712      this.dom.leading = new SVG.Number(o.leading || 1.3)
4713      return this
4714    }
4715  }
4716
4717  // Add parent method
4718, construct: {
4719    // Create text element
4720    text: function(text) {
4721      return this.put(new SVG.Text).text(text)
4722    }
4723    // Create plain text element
4724  , plain: function(text) {
4725      return this.put(new SVG.Text).plain(text)
4726    }
4727  }
4728
4729})
4730
4731SVG.Tspan = SVG.invent({
4732  // Initialize node
4733  create: 'tspan'
4734
4735  // Inherit from
4736, inherit: SVG.Shape
4737
4738  // Add class methods
4739, extend: {
4740    // Set text content
4741    text: function(text) {
4742      if(text == null) return this.node.textContent + (this.dom.newLined ? '\n' : '')
4743
4744      typeof text === 'function' ? text.call(this, this) : this.plain(text)
4745
4746      return this
4747    }
4748    // Shortcut dx
4749  , dx: function(dx) {
4750      return this.attr('dx', dx)
4751    }
4752    // Shortcut dy
4753  , dy: function(dy) {
4754      return this.attr('dy', dy)
4755    }
4756    // Create new line
4757  , newLine: function() {
4758      // fetch text parent
4759      var t = this.parent(SVG.Text)
4760
4761      // mark new line
4762      this.dom.newLined = true
4763
4764      // apply new hy¡n
4765      return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x())
4766    }
4767  }
4768
4769})
4770
4771SVG.extend(SVG.Text, SVG.Tspan, {
4772  // Create plain text node
4773  plain: function(text) {
4774    // clear if build mode is disabled
4775    if (this._build === false)
4776      this.clear()
4777
4778    // create text node
4779    this.node.appendChild(document.createTextNode(text))
4780
4781    return this
4782  }
4783  // Create a tspan
4784, tspan: function(text) {
4785    var node  = (this.textPath && this.textPath() || this).node
4786      , tspan = new SVG.Tspan
4787
4788    // clear if build mode is disabled
4789    if (this._build === false)
4790      this.clear()
4791
4792    // add new tspan
4793    node.appendChild(tspan.node)
4794
4795    return tspan.text(text)
4796  }
4797  // Clear all lines
4798, clear: function() {
4799    var node = (this.textPath && this.textPath() || this).node
4800
4801    // remove existing child nodes
4802    while (node.hasChildNodes())
4803      node.removeChild(node.lastChild)
4804
4805    return this
4806  }
4807  // Get length of text element
4808, length: function() {
4809    return this.node.getComputedTextLength()
4810  }
4811})
4812
4813SVG.TextPath = SVG.invent({
4814  // Initialize node
4815  create: 'textPath'
4816
4817  // Inherit from
4818, inherit: SVG.Parent
4819
4820  // Define parent class
4821, parent: SVG.Text
4822
4823  // Add parent method
4824, construct: {
4825    morphArray: SVG.PathArray
4826    // Create path for text to run on
4827  , path: function(d) {
4828      // create textPath element
4829      var path  = new SVG.TextPath
4830        , track = this.doc().defs().path(d)
4831
4832      // move lines to textpath
4833      while (this.node.hasChildNodes())
4834        path.node.appendChild(this.node.firstChild)
4835
4836      // add textPath element as child node
4837      this.node.appendChild(path.node)
4838
4839      // link textPath to path and add content
4840      path.attr('href', '#' + track, SVG.xlink)
4841
4842      return this
4843    }
4844    // return the array of the path track element
4845  , array: function() {
4846      var track = this.track()
4847
4848      return track ? track.array() : null
4849    }
4850    // Plot path if any
4851  , plot: function(d) {
4852      var track = this.track()
4853        , pathArray = null
4854
4855      if (track) {
4856        pathArray = track.plot(d)
4857      }
4858
4859      return (d == null) ? pathArray : this
4860    }
4861    // Get the path track element
4862  , track: function() {
4863      var path = this.textPath()
4864
4865      if (path)
4866        return path.reference('href')
4867    }
4868    // Get the textPath child
4869  , textPath: function() {
4870      if (this.node.firstChild && this.node.firstChild.nodeName == 'textPath')
4871        return SVG.adopt(this.node.firstChild)
4872    }
4873  }
4874})
4875
4876SVG.Nested = SVG.invent({
4877  // Initialize node
4878  create: function() {
4879    this.constructor.call(this, SVG.create('svg'))
4880
4881    this.style('overflow', 'visible')
4882  }
4883
4884  // Inherit from
4885, inherit: SVG.Container
4886
4887  // Add parent method
4888, construct: {
4889    // Create nested svg document
4890    nested: function() {
4891      return this.put(new SVG.Nested)
4892    }
4893  }
4894})
4895SVG.A = SVG.invent({
4896  // Initialize node
4897  create: 'a'
4898
4899  // Inherit from
4900, inherit: SVG.Container
4901
4902  // Add class methods
4903, extend: {
4904    // Link url
4905    to: function(url) {
4906      return this.attr('href', url, SVG.xlink)
4907    }
4908    // Link show attribute
4909  , show: function(target) {
4910      return this.attr('show', target, SVG.xlink)
4911    }
4912    // Link target attribute
4913  , target: function(target) {
4914      return this.attr('target', target)
4915    }
4916  }
4917
4918  // Add parent method
4919, construct: {
4920    // Create a hyperlink element
4921    link: function(url) {
4922      return this.put(new SVG.A).to(url)
4923    }
4924  }
4925})
4926
4927SVG.extend(SVG.Element, {
4928  // Create a hyperlink element
4929  linkTo: function(url) {
4930    var link = new SVG.A
4931
4932    if (typeof url == 'function')
4933      url.call(link, link)
4934    else
4935      link.to(url)
4936
4937    return this.parent().put(link).put(this)
4938  }
4939
4940})
4941SVG.Marker = SVG.invent({
4942  // Initialize node
4943  create: 'marker'
4944
4945  // Inherit from
4946, inherit: SVG.Container
4947
4948  // Add class methods
4949, extend: {
4950    // Set width of element
4951    width: function(width) {
4952      return this.attr('markerWidth', width)
4953    }
4954    // Set height of element
4955  , height: function(height) {
4956      return this.attr('markerHeight', height)
4957    }
4958    // Set marker refX and refY
4959  , ref: function(x, y) {
4960      return this.attr('refX', x).attr('refY', y)
4961    }
4962    // Update marker
4963  , update: function(block) {
4964      // remove all content
4965      this.clear()
4966
4967      // invoke passed block
4968      if (typeof block == 'function')
4969        block.call(this, this)
4970
4971      return this
4972    }
4973    // Return the fill id
4974  , toString: function() {
4975      return 'url(#' + this.id() + ')'
4976    }
4977  }
4978
4979  // Add parent method
4980, construct: {
4981    marker: function(width, height, block) {
4982      // Create marker element in defs
4983      return this.defs().marker(width, height, block)
4984    }
4985  }
4986
4987})
4988
4989SVG.extend(SVG.Defs, {
4990  // Create marker
4991  marker: function(width, height, block) {
4992    // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
4993    return this.put(new SVG.Marker)
4994      .size(width, height)
4995      .ref(width / 2, height / 2)
4996      .viewbox(0, 0, width, height)
4997      .attr('orient', 'auto')
4998      .update(block)
4999  }
5000
5001})
5002
5003SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, SVG.Path, {
5004  // Create and attach markers
5005  marker: function(marker, width, height, block) {
5006    var attr = ['marker']
5007
5008    // Build attribute name
5009    if (marker != 'all') attr.push(marker)
5010    attr = attr.join('-')
5011
5012    // Set marker attribute
5013    marker = arguments[1] instanceof SVG.Marker ?
5014      arguments[1] :
5015      this.doc().marker(width, height, block)
5016
5017    return this.attr(attr, marker)
5018  }
5019
5020})
5021// Define list of available attributes for stroke and fill
5022var sugar = {
5023  stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset']
5024, fill:   ['color', 'opacity', 'rule']
5025, prefix: function(t, a) {
5026    return a == 'color' ? t : t + '-' + a
5027  }
5028}
5029
5030// Add sugar for fill and stroke
5031;['fill', 'stroke'].forEach(function(m) {
5032  var i, extension = {}
5033
5034  extension[m] = function(o) {
5035    if (typeof o == 'undefined')
5036      return this
5037    if (typeof o == 'string' || SVG.Color.isRgb(o) || (o && typeof o.fill === 'function'))
5038      this.attr(m, o)
5039
5040    else
5041      // set all attributes from sugar.fill and sugar.stroke list
5042      for (i = sugar[m].length - 1; i >= 0; i--)
5043        if (o[sugar[m][i]] != null)
5044          this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]])
5045
5046    return this
5047  }
5048
5049  SVG.extend(SVG.Element, SVG.FX, extension)
5050
5051})
5052
5053SVG.extend(SVG.Element, SVG.FX, {
5054  // Map rotation to transform
5055  rotate: function(d, cx, cy) {
5056    return this.transform({ rotation: d, cx: cx, cy: cy })
5057  }
5058  // Map skew to transform
5059, skew: function(x, y, cx, cy) {
5060    return arguments.length == 1  || arguments.length == 3 ?
5061      this.transform({ skew: x, cx: y, cy: cx }) :
5062      this.transform({ skewX: x, skewY: y, cx: cx, cy: cy })
5063  }
5064  // Map scale to transform
5065, scale: function(x, y, cx, cy) {
5066    return arguments.length == 1  || arguments.length == 3 ?
5067      this.transform({ scale: x, cx: y, cy: cx }) :
5068      this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy })
5069  }
5070  // Map translate to transform
5071, translate: function(x, y) {
5072    return this.transform({ x: x, y: y })
5073  }
5074  // Map flip to transform
5075, flip: function(a, o) {
5076    o = typeof a == 'number' ? a : o
5077    return this.transform({ flip: a || 'both', offset: o })
5078  }
5079  // Map matrix to transform
5080, matrix: function(m) {
5081    return this.attr('transform', new SVG.Matrix(arguments.length == 6 ? [].slice.call(arguments) : m))
5082  }
5083  // Opacity
5084, opacity: function(value) {
5085    return this.attr('opacity', value)
5086  }
5087  // Relative move over x axis
5088, dx: function(x) {
5089    return this.x(new SVG.Number(x).plus(this instanceof SVG.FX ? 0 : this.x()), true)
5090  }
5091  // Relative move over y axis
5092, dy: function(y) {
5093    return this.y(new SVG.Number(y).plus(this instanceof SVG.FX ? 0 : this.y()), true)
5094  }
5095  // Relative move over x and y axes
5096, dmove: function(x, y) {
5097    return this.dx(x).dy(y)
5098  }
5099})
5100
5101SVG.extend(SVG.Rect, SVG.Ellipse, SVG.Circle, SVG.Gradient, SVG.FX, {
5102  // Add x and y radius
5103  radius: function(x, y) {
5104    var type = (this._target || this).type;
5105    return type == 'radial' || type == 'circle' ?
5106      this.attr('r', new SVG.Number(x)) :
5107      this.rx(x).ry(y == null ? x : y)
5108  }
5109})
5110
5111SVG.extend(SVG.Path, {
5112  // Get path length
5113  length: function() {
5114    return this.node.getTotalLength()
5115  }
5116  // Get point at length
5117, pointAt: function(length) {
5118    return this.node.getPointAtLength(length)
5119  }
5120})
5121
5122SVG.extend(SVG.Parent, SVG.Text, SVG.Tspan, SVG.FX, {
5123  // Set font
5124  font: function(a, v) {
5125    if (typeof a == 'object') {
5126      for (v in a) this.font(v, a[v])
5127    }
5128
5129    return a == 'leading' ?
5130        this.leading(v) :
5131      a == 'anchor' ?
5132        this.attr('text-anchor', v) :
5133      a == 'size' || a == 'family' || a == 'weight' || a == 'stretch' || a == 'variant' || a == 'style' ?
5134        this.attr('font-'+ a, v) :
5135        this.attr(a, v)
5136  }
5137})
5138
5139SVG.Set = SVG.invent({
5140  // Initialize
5141  create: function(members) {
5142    if (members instanceof SVG.Set) {
5143      this.members = members.members.slice()
5144    } else {
5145      Array.isArray(members) ? this.members = members : this.clear()
5146    }
5147  }
5148
5149  // Add class methods
5150, extend: {
5151    // Add element to set
5152    add: function() {
5153      var i, il, elements = [].slice.call(arguments)
5154
5155      for (i = 0, il = elements.length; i < il; i++)
5156        this.members.push(elements[i])
5157
5158      return this
5159    }
5160    // Remove element from set
5161  , remove: function(element) {
5162      var i = this.index(element)
5163
5164      // remove given child
5165      if (i > -1)
5166        this.members.splice(i, 1)
5167
5168      return this
5169    }
5170    // Iterate over all members
5171  , each: function(block) {
5172      for (var i = 0, il = this.members.length; i < il; i++)
5173        block.apply(this.members[i], [i, this.members])
5174
5175      return this
5176    }
5177    // Restore to defaults
5178  , clear: function() {
5179      // initialize store
5180      this.members = []
5181
5182      return this
5183    }
5184    // Get the length of a set
5185  , length: function() {
5186      return this.members.length
5187    }
5188    // Checks if a given element is present in set
5189  , has: function(element) {
5190      return this.index(element) >= 0
5191    }
5192    // retuns index of given element in set
5193  , index: function(element) {
5194      return this.members.indexOf(element)
5195    }
5196    // Get member at given index
5197  , get: function(i) {
5198      return this.members[i]
5199    }
5200    // Get first member
5201  , first: function() {
5202      return this.get(0)
5203    }
5204    // Get last member
5205  , last: function() {
5206      return this.get(this.members.length - 1)
5207    }
5208    // Default value
5209  , valueOf: function() {
5210      return this.members
5211    }
5212    // Get the bounding box of all members included or empty box if set has no items
5213  , bbox: function(){
5214      // return an empty box of there are no members
5215      if (this.members.length == 0)
5216        return new SVG.RBox()
5217
5218      // get the first rbox and update the target bbox
5219      var rbox = this.members[0].rbox(this.members[0].doc())
5220
5221      this.each(function() {
5222        // user rbox for correct position and visual representation
5223        rbox = rbox.merge(this.rbox(this.doc()))
5224      })
5225
5226      return rbox
5227    }
5228  }
5229
5230  // Add parent method
5231, construct: {
5232    // Create a new set
5233    set: function(members) {
5234      return new SVG.Set(members)
5235    }
5236  }
5237})
5238
5239SVG.FX.Set = SVG.invent({
5240  // Initialize node
5241  create: function(set) {
5242    // store reference to set
5243    this.set = set
5244  }
5245
5246})
5247
5248// Alias methods
5249SVG.Set.inherit = function() {
5250  var m
5251    , methods = []
5252
5253  // gather shape methods
5254  for(var m in SVG.Shape.prototype)
5255    if (typeof SVG.Shape.prototype[m] == 'function' && typeof SVG.Set.prototype[m] != 'function')
5256      methods.push(m)
5257
5258  // apply shape aliasses
5259  methods.forEach(function(method) {
5260    SVG.Set.prototype[method] = function() {
5261      for (var i = 0, il = this.members.length; i < il; i++)
5262        if (this.members[i] && typeof this.members[i][method] == 'function')
5263          this.members[i][method].apply(this.members[i], arguments)
5264
5265      return method == 'animate' ? (this.fx || (this.fx = new SVG.FX.Set(this))) : this
5266    }
5267  })
5268
5269  // clear methods for the next round
5270  methods = []
5271
5272  // gather fx methods
5273  for(var m in SVG.FX.prototype)
5274    if (typeof SVG.FX.prototype[m] == 'function' && typeof SVG.FX.Set.prototype[m] != 'function')
5275      methods.push(m)
5276
5277  // apply fx aliasses
5278  methods.forEach(function(method) {
5279    SVG.FX.Set.prototype[method] = function() {
5280      for (var i = 0, il = this.set.members.length; i < il; i++)
5281        this.set.members[i].fx[method].apply(this.set.members[i].fx, arguments)
5282
5283      return this
5284    }
5285  })
5286}
5287
5288
5289SVG.extend(SVG.Element, {
5290  // Store data values on svg nodes
5291  data: function(a, v, r) {
5292    if (typeof a == 'object') {
5293      for (v in a)
5294        this.data(v, a[v])
5295
5296    } else if (arguments.length < 2) {
5297      try {
5298        return JSON.parse(this.attr('data-' + a))
5299      } catch(e) {
5300        return this.attr('data-' + a)
5301      }
5302
5303    } else {
5304      this.attr(
5305        'data-' + a
5306      , v === null ?
5307          null :
5308        r === true || typeof v === 'string' || typeof v === 'number' ?
5309          v :
5310          JSON.stringify(v)
5311      )
5312    }
5313
5314    return this
5315  }
5316})
5317SVG.extend(SVG.Element, {
5318  // Remember arbitrary data
5319  remember: function(k, v) {
5320    // remember every item in an object individually
5321    if (typeof arguments[0] == 'object')
5322      for (var v in k)
5323        this.remember(v, k[v])
5324
5325    // retrieve memory
5326    else if (arguments.length == 1)
5327      return this.memory()[k]
5328
5329    // store memory
5330    else
5331      this.memory()[k] = v
5332
5333    return this
5334  }
5335
5336  // Erase a given memory
5337, forget: function() {
5338    if (arguments.length == 0)
5339      this._memory = {}
5340    else
5341      for (var i = arguments.length - 1; i >= 0; i--)
5342        delete this.memory()[arguments[i]]
5343
5344    return this
5345  }
5346
5347  // Initialize or return local memory object
5348, memory: function() {
5349    return this._memory || (this._memory = {})
5350  }
5351
5352})
5353// Method for getting an element by id
5354SVG.get = function(id) {
5355  var node = document.getElementById(idFromReference(id) || id)
5356  return SVG.adopt(node)
5357}
5358
5359// Select elements by query string
5360SVG.select = function(query, parent) {
5361  return new SVG.Set(
5362    SVG.utils.map((parent || document).querySelectorAll(query), function(node) {
5363      return SVG.adopt(node)
5364    })
5365  )
5366}
5367
5368SVG.extend(SVG.Parent, {
5369  // Scoped select method
5370  select: function(query) {
5371    return SVG.select(query, this.node)
5372  }
5373
5374})
5375function pathRegReplace(a, b, c, d) {
5376  return c + d.replace(SVG.regex.dots, ' .')
5377}
5378
5379// creates deep clone of array
5380function array_clone(arr){
5381  var clone = arr.slice(0)
5382  for(var i = clone.length; i--;){
5383    if(Array.isArray(clone[i])){
5384      clone[i] = array_clone(clone[i])
5385    }
5386  }
5387  return clone
5388}
5389
5390// tests if a given element is instance of an object
5391function is(el, obj){
5392  return el instanceof obj
5393}
5394
5395// tests if a given selector matches an element
5396function matches(el, selector) {
5397  return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);
5398}
5399
5400// Convert dash-separated-string to camelCase
5401function camelCase(s) {
5402  return s.toLowerCase().replace(/-(.)/g, function(m, g) {
5403    return g.toUpperCase()
5404  })
5405}
5406
5407// Capitalize first letter of a string
5408function capitalize(s) {
5409  return s.charAt(0).toUpperCase() + s.slice(1)
5410}
5411
5412// Ensure to six-based hex
5413function fullHex(hex) {
5414  return hex.length == 4 ?
5415    [ '#',
5416      hex.substring(1, 2), hex.substring(1, 2)
5417    , hex.substring(2, 3), hex.substring(2, 3)
5418    , hex.substring(3, 4), hex.substring(3, 4)
5419    ].join('') : hex
5420}
5421
5422// Component to hex value
5423function compToHex(comp) {
5424  var hex = comp.toString(16)
5425  return hex.length == 1 ? '0' + hex : hex
5426}
5427
5428// Calculate proportional width and height values when necessary
5429function proportionalSize(element, width, height) {
5430  if (width == null || height == null) {
5431    var box = element.bbox()
5432
5433    if (width == null)
5434      width = box.width / box.height * height
5435    else if (height == null)
5436      height = box.height / box.width * width
5437  }
5438
5439  return {
5440    width:  width
5441  , height: height
5442  }
5443}
5444
5445// Delta transform point
5446function deltaTransformPoint(matrix, x, y) {
5447  return {
5448    x: x * matrix.a + y * matrix.c + 0
5449  , y: x * matrix.b + y * matrix.d + 0
5450  }
5451}
5452
5453// Map matrix array to object
5454function arrayToMatrix(a) {
5455  return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] }
5456}
5457
5458// Parse matrix if required
5459function parseMatrix(matrix) {
5460  if (!(matrix instanceof SVG.Matrix))
5461    matrix = new SVG.Matrix(matrix)
5462
5463  return matrix
5464}
5465
5466// Add centre point to transform object
5467function ensureCentre(o, target) {
5468  o.cx = o.cx == null ? target.bbox().cx : o.cx
5469  o.cy = o.cy == null ? target.bbox().cy : o.cy
5470}
5471
5472// PathArray Helpers
5473function arrayToString(a) {
5474  for (var i = 0, il = a.length, s = ''; i < il; i++) {
5475    s += a[i][0]
5476
5477    if (a[i][1] != null) {
5478      s += a[i][1]
5479
5480      if (a[i][2] != null) {
5481        s += ' '
5482        s += a[i][2]
5483
5484        if (a[i][3] != null) {
5485          s += ' '
5486          s += a[i][3]
5487          s += ' '
5488          s += a[i][4]
5489
5490          if (a[i][5] != null) {
5491            s += ' '
5492            s += a[i][5]
5493            s += ' '
5494            s += a[i][6]
5495
5496            if (a[i][7] != null) {
5497              s += ' '
5498              s += a[i][7]
5499            }
5500          }
5501        }
5502      }
5503    }
5504  }
5505
5506  return s + ' '
5507}
5508
5509// Deep new id assignment
5510function assignNewId(node) {
5511  // do the same for SVG child nodes as well
5512  for (var i = node.childNodes.length - 1; i >= 0; i--)
5513    if (node.childNodes[i] instanceof window.SVGElement)
5514      assignNewId(node.childNodes[i])
5515
5516  return SVG.adopt(node).id(SVG.eid(node.nodeName))
5517}
5518
5519// Add more bounding box properties
5520function fullBox(b) {
5521  if (b.x == null) {
5522    b.x      = 0
5523    b.y      = 0
5524    b.width  = 0
5525    b.height = 0
5526  }
5527
5528  b.w  = b.width
5529  b.h  = b.height
5530  b.x2 = b.x + b.width
5531  b.y2 = b.y + b.height
5532  b.cx = b.x + b.width / 2
5533  b.cy = b.y + b.height / 2
5534
5535  return b
5536}
5537
5538// Get id from reference string
5539function idFromReference(url) {
5540  var m = (url || '').toString().match(SVG.regex.reference)
5541
5542  if (m) return m[1]
5543}
5544
5545// If values like 1e-88 are passed, this is not a valid 32 bit float,
5546// but in those cases, we are so close to 0 that 0 works well!
5547function float32String(v) {
5548  return Math.abs(v) > 1e-37 ? v : 0
5549}
5550
5551// Create matrix array for looping
5552var abcdef = 'abcdef'.split('')
5553
5554// Add CustomEvent to IE9 and IE10
5555if (typeof window.CustomEvent !== 'function') {
5556  // Code from: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent
5557  var CustomEventPoly = function(event, options) {
5558    options = options || { bubbles: false, cancelable: false, detail: undefined }
5559    var e = document.createEvent('CustomEvent')
5560    e.initCustomEvent(event, options.bubbles, options.cancelable, options.detail)
5561    return e
5562  }
5563
5564  CustomEventPoly.prototype = window.Event.prototype
5565
5566  SVG.CustomEvent = CustomEventPoly
5567} else {
5568  SVG.CustomEvent = window.CustomEvent
5569}
5570
5571// requestAnimationFrame / cancelAnimationFrame Polyfill with fallback based on Paul Irish
5572(function(w) {
5573  var lastTime = 0
5574  var vendors = ['moz', 'webkit']
5575
5576  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
5577    w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame']
5578    w.cancelAnimationFrame  = w[vendors[x] + 'CancelAnimationFrame'] ||
5579                              w[vendors[x] + 'CancelRequestAnimationFrame']
5580  }
5581
5582  w.requestAnimationFrame = w.requestAnimationFrame ||
5583    function(callback) {
5584      var currTime = new Date().getTime()
5585      var timeToCall = Math.max(0, 16 - (currTime - lastTime))
5586
5587      var id = w.setTimeout(function() {
5588        callback(currTime + timeToCall)
5589      }, timeToCall)
5590
5591      lastTime = currTime + timeToCall
5592      return id
5593    }
5594
5595  w.cancelAnimationFrame = w.cancelAnimationFrame || w.clearTimeout;
5596
5597}(window))
5598
5599return SVG
5600
5601}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.