]> source.dussan.org Git - svg.js.git/commitdiff
Merge branch '875-es6' into 3.0.0
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Wed, 7 Nov 2018 14:08:41 +0000 (15:08 +0100)
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Wed, 7 Nov 2018 14:08:41 +0000 (15:08 +0100)
1  2 
dist/svg.js
spec/spec/element.js

diff --cc dist/svg.js
index b854ee4e4df9270bee797eb7ca03a8e8a8e0cacc,e218c1562f44385eece3250b88022fcefbdd1917..e1dfbef26fc75d34e84b5381aa7b39ebc7e2a3dd
  * @copyright Wout Fierens <wout@mick-wout.com>
  * @license MIT
  *
- * BUILT: Sat Oct 27 2018 12:02:54 GMT+0200 (CEST)
 -* BUILT: Tue Nov 06 2018 13:43:56 GMT+0100 (GMT+01:00)
++* BUILT: Wed Nov 07 2018 15:06:40 GMT+0100 (GMT+01:00)
  */;
+ var SVG = (function () {
+   'use strict';
  
- (function(root, factory) {
-   /* istanbul ignore next */
-   if (typeof define === 'function' && define.amd) {
-     define(function(){
-       return factory(root, root.document)
-     })
-   } else if (typeof exports === 'object') {
-     module.exports = root.document ? factory(root, root.document) : function(w){ return factory(w, w.document) }
-   } else {
-     root.SVG = factory(root, root.document)
-   }
- }(typeof window !== "undefined" ? window : this, function(window, document) {
+   function _typeof(obj) {
+     if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
+       _typeof = function (obj) {
+         return typeof obj;
+       };
+     } else {
+       _typeof = function (obj) {
+         return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+       };
+     }
  
- // Check that our browser supports svg
- var supported = !! document.createElementNS &&
-   !! document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect
+     return _typeof(obj);
+   }
  
- // If we don't support svg, just exit without doing anything
- if (!supported)
-   return {supported: false}
+   function _classCallCheck(instance, Constructor) {
+     if (!(instance instanceof Constructor)) {
+       throw new TypeError("Cannot call a class as a function");
+     }
+   }
  
- // Otherwise, the library will be here
- "use strict";
+   function _defineProperties(target, props) {
+     for (var i = 0; i < props.length; i++) {
+       var descriptor = props[i];
+       descriptor.enumerable = descriptor.enumerable || false;
+       descriptor.configurable = true;
+       if ("value" in descriptor) descriptor.writable = true;
+       Object.defineProperty(target, descriptor.key, descriptor);
+     }
+   }
  
- function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
+   function _createClass(Constructor, protoProps, staticProps) {
+     if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+     if (staticProps) _defineProperties(Constructor, staticProps);
+     return Constructor;
+   }
  
- function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
+   function _defineProperty(obj, key, value) {
+     if (key in obj) {
+       Object.defineProperty(obj, key, {
+         value: value,
+         enumerable: true,
+         configurable: true,
+         writable: true
+       });
+     } else {
+       obj[key] = value;
+     }
  
- function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
+     return obj;
+   }
  
- function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
+   function _objectSpread(target) {
+     for (var i = 1; i < arguments.length; i++) {
+       var source = arguments[i] != null ? arguments[i] : {};
+       var ownKeys = Object.keys(source);
  
- function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+       if (typeof Object.getOwnPropertySymbols === 'function') {
+         ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
+           return Object.getOwnPropertyDescriptor(source, sym).enumerable;
+         }));
+       }
  
- function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
+       ownKeys.forEach(function (key) {
+         _defineProperty(target, key, source[key]);
+       });
+     }
  
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+     return target;
+   }
  
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
+   function _inherits(subClass, superClass) {
+     if (typeof superClass !== "function" && superClass !== null) {
+       throw new TypeError("Super expression must either be null or a function");
+     }
  
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+     subClass.prototype = Object.create(superClass && superClass.prototype, {
+       constructor: {
+         value: subClass,
+         writable: true,
+         configurable: true
+       }
+     });
+     if (superClass) _setPrototypeOf(subClass, superClass);
+   }
  
- function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+   function _getPrototypeOf(o) {
+     _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
+       return o.__proto__ || Object.getPrototypeOf(o);
+     };
+     return _getPrototypeOf(o);
+   }
  
- /* global createElement, capitalize */
+   function _setPrototypeOf(o, p) {
+     _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
+       o.__proto__ = p;
+       return o;
+     };
  
- /* eslint-disable new-cap */
- // The main wrapping element
- var SVG = window.SVG = function (element) {
-   if (SVG.supported) {
-     element = createElement(element);
-     return element;
+     return _setPrototypeOf(o, p);
    }
- }; // Svg must be supported if we reached this stage
  
+   function _assertThisInitialized(self) {
+     if (self === void 0) {
+       throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+     }
  
- SVG.supported = true; // Default namespaces
+     return self;
+   }
  
- SVG.ns = 'http://www.w3.org/2000/svg';
- SVG.xmlns = 'http://www.w3.org/2000/xmlns/';
SVG.xlink = 'http://www.w3.org/1999/xlink';
- SVG.svgjs = 'http://svgjs.com/svgjs'; // Element id sequence
+   function _possibleConstructorReturn(self, call) {
+     if (call && (typeof call === "object" || typeof call === "function")) {
      return call;
+     }
  
- SVG.did = 1000; // Get next named element id
+     return _assertThisInitialized(self);
+   }
  
- SVG.eid = function (name) {
-   return 'Svgjs' + capitalize(name) + SVG.did++;
- }; // Method for element creation
+   function _superPropBase(object, property) {
+     while (!Object.prototype.hasOwnProperty.call(object, property)) {
+       object = _getPrototypeOf(object);
+       if (object === null) break;
+     }
  
+     return object;
+   }
  
- SVG.create = function (name) {
-   // create element
-   return document.createElementNS(this.ns, name);
- }; // Method for extending objects
+   function _get(target, property, receiver) {
+     if (typeof Reflect !== "undefined" && Reflect.get) {
+       _get = Reflect.get;
+     } else {
+       _get = function _get(target, property, receiver) {
+         var base = _superPropBase(target, property);
  
+         if (!base) return;
+         var desc = Object.getOwnPropertyDescriptor(base, property);
  
SVG.extend = function (modules, methods) {
-   var key, i;
-   modules = Array.isArray(modules) ? modules : [modules];
        if (desc.get) {
+           return desc.get.call(receiver);
+         }
  
-   for (i = modules.length - 1; i >= 0; i--) {
-     if (modules[i]) {
-       for (key in methods) {
-         modules[i].prototype[key] = methods[key];
-       }
+         return desc.value;
+       };
      }
-   }
- }; // Invent new element
  
- SVG.invent = function (config) {
-   // Create element initializer
-   var initializer = typeof config.create === 'function' ? config.create : function (node) {
-     config.inherit.call(this, node || SVG.create(config.create));
-   }; // Inherit prototype
-   if (config.inherit) {
-     initializer.prototype = new config.inherit();
-     initializer.prototype.constructor = initializer;
-   } // Extend with methods
+     return _get(target, property, receiver || target);
+   }
  
+   function _slicedToArray(arr, i) {
+     return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
+   }
  
-   if (config.extend) {
-     SVG.extend(initializer, config.extend);
-   } // Attach construct method to parent
+   function _toConsumableArray(arr) {
+     return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
+   }
  
+   function _arrayWithoutHoles(arr) {
+     if (Array.isArray(arr)) {
+       for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
  
-   if (config.construct) {
-     SVG.extend(config.parent || SVG.Container, config.construct);
+       return arr2;
+     }
    }
  
-   return initializer;
- }; // Adopt existing svg elements
+   function _arrayWithHoles(arr) {
+     if (Array.isArray(arr)) return arr;
+   }
  
SVG.adopt = function (node) {
-   // check for presence of node
-   if (!node) return null; // make sure a node isn't already adopted
  function _iterableToArray(iter) {
+     if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
+   }
  
-   if (node.instance instanceof SVG.Element) return node.instance;
+   function _iterableToArrayLimit(arr, i) {
+     var _arr = [];
+     var _n = true;
+     var _d = false;
+     var _e = undefined;
  
-   if (!(node instanceof window.SVGElement)) {
-     return new SVG.HtmlNode(node);
-   } // initialize variables
+     try {
+       for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
+         _arr.push(_s.value);
  
+         if (i && _arr.length === i) break;
+       }
+     } catch (err) {
+       _d = true;
+       _e = err;
+     } finally {
+       try {
+         if (!_n && _i["return"] != null) _i["return"]();
+       } finally {
+         if (_d) throw _e;
+       }
+     }
  
-   var element; // adopt with element-specific settings
+     return _arr;
+   }
  
-   if (node.nodeName === 'svg') {
-     element = new SVG.Doc(node);
-   } else if (node.nodeName === 'linearGradient' || node.nodeName === 'radialGradient') {
-     element = new SVG.Gradient(node);
-   } else if (SVG[capitalize(node.nodeName)]) {
-     element = new SVG[capitalize(node.nodeName)](node);
-   } else {
-     element = new SVG.Parent(node);
+   function _nonIterableSpread() {
+     throw new TypeError("Invalid attempt to spread non-iterable instance");
    }
  
-   return element;
- }; // Storage for regular expressions
+   function _nonIterableRest() {
+     throw new TypeError("Invalid attempt to destructure non-iterable instance");
+   }
  
+   var methods = {};
+   function registerMethods(name, m) {
+     if (Array.isArray(name)) {
+       var _iteratorNormalCompletion = true;
+       var _didIteratorError = false;
+       var _iteratorError = undefined;
  
- SVG.regex = {
-   // Parse unit value
-   numberAndUnit: /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,
-   // Parse hex value
-   hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,
-   // Parse rgb value
-   rgb: /rgb\((\d+),(\d+),(\d+)\)/,
-   // Parse reference id
-   reference: /#([a-z0-9\-_]+)/i,
-   // splits a transformation chain
-   transforms: /\)\s*,?\s*/,
-   // Whitespace
-   whitespace: /\s/g,
-   // Test hex value
-   isHex: /^#[a-f0-9]{3,6}$/i,
-   // Test rgb value
-   isRgb: /^rgb\(/,
-   // Test css declaration
-   isCss: /[^:]+:[^;]+;?/,
-   // Test for blank string
-   isBlank: /^(\s+)?$/,
-   // Test for numeric string
-   isNumber: /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,
-   // Test for percent value
-   isPercent: /^-?[\d.]+%$/,
-   // Test for image url
-   isImage: /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,
-   // split at whitespace and comma
-   delimiter: /[\s,]+/,
-   // The following regex are used to parse the d attribute of a path
-   // Matches all hyphens which are not after an exponent
-   hyphen: /([^e])-/gi,
-   // Replaces and tests for all path letters
-   pathLetters: /[MLHVCSQTAZ]/gi,
-   // yes we need this one, too
-   isPathLetter: /[MLHVCSQTAZ]/i,
-   // matches 0.154.23.45
-   numbersWithDots: /((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi,
-   // matches .
-   dots: /\./g
- };
- SVG.utils = {
-   // Map function
-   map: function map(array, block) {
-     var i;
-     var il = array.length;
-     var result = [];
+       try {
+         for (var _iterator = name[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+           var _name = _step.value;
+           registerMethods(_name, m);
+         }
+       } catch (err) {
+         _didIteratorError = true;
+         _iteratorError = err;
+       } finally {
+         try {
+           if (!_iteratorNormalCompletion && _iterator.return != null) {
+             _iterator.return();
+           }
+         } finally {
+           if (_didIteratorError) {
+             throw _iteratorError;
+           }
+         }
+       }
  
-     for (i = 0; i < il; i++) {
-       result.push(block(array[i]));
+       return;
      }
  
-     return result;
-   },
-   // Filter function
-   filter: function filter(array, block) {
-     var i;
-     var il = array.length;
-     var result = [];
+     if (_typeof(name) === 'object') {
+       var _arr = Object.entries(name);
  
-     for (i = 0; i < il; i++) {
-       if (block(array[i])) {
-         result.push(array[i]);
+       for (var _i = 0; _i < _arr.length; _i++) {
+         var _arr$_i = _slicedToArray(_arr[_i], 2),
+             _name2 = _arr$_i[0],
+             _m = _arr$_i[1];
+         registerMethods(_name2, _m);
        }
+       return;
      }
  
-     return result;
-   },
-   // Degrees to radians
-   radians: function radians(d) {
-     return d % 360 * Math.PI / 180;
-   },
-   // Radians to degrees
-   degrees: function degrees(r) {
-     return r * 180 / Math.PI % 360;
-   },
-   filterSVGElements: function filterSVGElements(nodes) {
-     return this.filter(nodes, function (el) {
-       return el instanceof window.SVGElement;
-     });
+     methods[name] = Object.assign(methods[name] || {}, m);
+   }
+   function getMethodsFor(name) {
+     return methods[name] || {};
    }
- };
  
- SVG.void = function () {};
+   function siblings() {
+     return this.parent().children();
+   } // Get the curent position siblings
  
- SVG.defaults = {
-   // Default animation values
-   timeline: {
-     duration: 400,
-     ease: '>',
-     delay: 0
-   },
-   // Default attribute values
-   attrs: {
-     // fill and stroke
-     'fill-opacity': 1,
-     'stroke-opacity': 1,
-     'stroke-width': 0,
-     'stroke-linejoin': 'miter',
-     'stroke-linecap': 'butt',
-     fill: '#000000',
-     stroke: '#000000',
-     opacity: 1,
-     // position
-     x: 0,
-     y: 0,
-     cx: 0,
-     cy: 0,
-     // size
-     width: 0,
-     height: 0,
-     // radius
-     r: 0,
-     rx: 0,
-     ry: 0,
-     // gradient
-     offset: 0,
-     'stop-opacity': 1,
-     'stop-color': '#000000',
-     // text
-     'font-size': 16,
-     'font-family': 'Helvetica, Arial, sans-serif',
-     'text-anchor': 'start'
-   }
- };
- SVG.Queue = SVG.invent({
-   create: function create() {
-     this._first = null;
-     this._last = null;
-   },
-   extend: {
-     push: function push(value) {
-       // An item stores an id and the provided value
-       var item = value.next ? value : {
-         value: value,
-         next: null,
-         prev: null // Deal with the queue being empty or populated
+   function position() {
+     return this.parent().index(this);
+   } // Get the next element (will return null if there is none)
  
-       };
+   function next() {
+     return this.siblings()[this.position() + 1];
+   } // Get the next element (will return null if there is none)
  
-       if (this._last) {
-         item.prev = this._last;
-         this._last.next = item;
-         this._last = item;
-       } else {
-         this._last = item;
-         this._first = item;
-       } // Update the length and return the current item
+   function prev() {
+     return this.siblings()[this.position() - 1];
+   } // Send given element one step forward
+   function forward() {
+     var i = this.position() + 1;
+     var p = this.parent(); // move node one step forward
  
+     p.removeElement(this).add(this, i); // make sure defs node is always at the top
  
-       return item;
-     },
-     shift: function shift() {
-       // Check if we have a value
-       var remove = this._first;
-       if (!remove) return null; // If we do, remove it and relink things
-       this._first = remove.next;
-       if (this._first) this._first.prev = null;
-       this._last = this._first ? this._last : null;
-       return remove.value;
-     },
-     // Shows us the first item in the list
-     first: function first() {
-       return this._first && this._first.value;
-     },
-     // Shows us the last item in the list
-     last: function last() {
-       return this._last && this._last.value;
-     },
-     // Removes the item that was returned from the push
-     remove: function remove(item) {
-       // Relink the previous item
-       if (item.prev) item.prev.next = item.next;
-       if (item.next) item.next.prev = item.prev;
-       if (item === this._last) this._last = item.prev;
-       if (item === this._first) this._first = item.next; // Invalidate item
-       item.prev = null;
-       item.next = null;
+     if (typeof p.isRoot === 'function' && p.isRoot()) {
+       p.node.appendChild(p.defs().node);
      }
-   }
- });
- /* globals fullHex, compToHex */
- /*
- Color {
-   constructor (a, b, c, space) {
-     space: 'hsl'
-     a: 30
-     b: 20
-     c: 10
-   },
-   toRgb () { return new Color in rgb space }
-   toHsl () { return new Color in hsl space }
-   toLab () { return new Color in lab space }
-   toArray () { [space, a, b, c] }
-   fromArray () { convert it back }
- }
- // Conversions aren't always exact because of monitor profiles etc...
- new Color(h, s, l, 'hsl') !== new Color(r, g, b).hsl()
- new Color(100, 100, 100, [space])
- new Color('hsl(30, 20, 10)')
- // Sugar
- SVG.rgb(30, 20, 50).lab()
- SVG.hsl()
- SVG.lab('rgb(100, 100, 100)')
- */
- // Module for color convertions
- SVG.Color = function (color, g, b) {
-   var match; // initialize defaults
-   this.r = 0;
-   this.g = 0;
-   this.b = 0;
-   if (!color) return; // parse color
-   if (typeof color === 'string') {
-     if (SVG.regex.isRgb.test(color)) {
-       // get rgb values
-       match = SVG.regex.rgb.exec(color.replace(SVG.regex.whitespace, '')); // parse numeric values
-       this.r = parseInt(match[1]);
-       this.g = parseInt(match[2]);
-       this.b = parseInt(match[3]);
-     } else if (SVG.regex.isHex.test(color)) {
-       // get hex values
-       match = SVG.regex.hex.exec(fullHex(color)); // parse numeric values
-       this.r = parseInt(match[1], 16);
-       this.g = parseInt(match[2], 16);
-       this.b = parseInt(match[3], 16);
+     return this;
+   } // Send given element one step backward
+   function backward() {
+     var i = this.position();
+     if (i > 0) {
+       this.parent().removeElement(this).add(this, i - 1);
      }
-   } else if (Array.isArray(color)) {
-     this.r = color[0];
-     this.g = color[1];
-     this.b = color[2];
-   } else if (_typeof(color) === 'object') {
-     this.r = color.r;
-     this.g = color.g;
-     this.b = color.b;
-   } else if (arguments.length === 3) {
-     this.r = color;
-     this.g = g;
-     this.b = b;
-   }
- };
- SVG.extend(SVG.Color, {
-   // Default to hex conversion
-   toString: function toString() {
-     return this.toHex();
-   },
-   toArray: function toArray() {
-     return [this.r, this.g, this.b];
-   },
-   fromArray: function fromArray(a) {
-     return new SVG.Color(a);
-   },
-   // Build hex value
-   toHex: function toHex() {
-     return '#' + compToHex(Math.round(this.r)) + compToHex(Math.round(this.g)) + compToHex(Math.round(this.b));
-   },
-   // Build rgb value
-   toRgb: function toRgb() {
-     return 'rgb(' + [this.r, this.g, this.b].join() + ')';
-   },
-   // Calculate true brightness
-   brightness: function brightness() {
-     return this.r / 255 * 0.30 + this.g / 255 * 0.59 + this.b / 255 * 0.11;
-   },
-   // Make color morphable
-   morph: function morph(color) {
-     this.destination = new SVG.Color(color);
      return this;
-   },
-   // Get morphed color at given position
-   at: function at(pos) {
-     // make sure a destination is defined
-     if (!this.destination) return this; // normalise pos
-     pos = pos < 0 ? 0 : pos > 1 ? 1 : pos; // generate morphed color
-     return new SVG.Color({
-       r: ~~(this.r + (this.destination.r - this.r) * pos),
-       g: ~~(this.g + (this.destination.g - this.g) * pos),
-       b: ~~(this.b + (this.destination.b - this.b) * pos)
-     });
-   }
- }); // Testers
- // Test if given value is a color string
+   } // Send given element all the way to the front
  
- SVG.Color.test = function (color) {
-   color += '';
-   return SVG.regex.isHex.test(color) || SVG.regex.isRgb.test(color);
- }; // Test if given value is a rgb object
+   function front() {
+     var p = this.parent(); // Move node forward
  
+     p.node.appendChild(this.node); // Make sure defs node is always at the top
  
SVG.Color.isRgb = function (color) {
-   return color && typeof color.r === 'number' && typeof color.g === 'number' && typeof color.b === 'number';
- }; // Test if given value is a color
    if (typeof p.isRoot === 'function' && p.isRoot()) {
+       p.node.appendChild(p.defs().node);
+     }
  
+     return this;
+   } // Send given element all the way to the back
  
- SVG.Color.isColor = function (color) {
-   return SVG.Color.isRgb(color) || SVG.Color.test(color);
- };
- /* global arrayClone */
- // Module for array conversion
+   function back() {
+     if (this.position() > 0) {
+       this.parent().removeElement(this).add(this, 0);
+     }
  
+     return this;
+   } // Inserts a given element before the targeted element
  
- SVG.Array = function (array, fallback) {
-   array = (array || []).valueOf(); // if array is empty and fallback is provided, use fallback
+   function before(element) {
+     element.remove();
+     var i = this.position();
+     this.parent().add(element, i);
+     return this;
+   } // Inserts a given element after the targeted element
  
-   if (array.length === 0 && fallback) {
-     array = fallback.valueOf();
-   } // parse array
+   function after(element) {
+     element.remove();
+     var i = this.position();
+     this.parent().add(element, i + 1);
+     return this;
+   }
+   registerMethods('Dom', {
+     siblings: siblings,
+     position: position,
+     next: next,
+     prev: prev,
+     forward: forward,
+     backward: backward,
+     front: front,
+     back: back,
+     before: before,
+     after: after
+   });
  
+   // Parse unit value
+   var numberAndUnit = /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i; // Parse hex value
  
-   this.value = this.parse(array);
- };
+   var hex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; // Parse rgb value
  
- SVG.extend(SVG.Array, {
-   // Make array morphable
-   morph: function morph(array) {
-     this.destination = this.parse(array); // normalize length of arrays
+   var rgb = /rgb\((\d+),(\d+),(\d+)\)/; // Parse reference id
  
-     if (this.value.length !== this.destination.length) {
-       var lastValue = this.value[this.value.length - 1];
-       var lastDestination = this.destination[this.destination.length - 1];
+   var reference = /(#[a-z0-9\-_]+)/i; // splits a transformation chain
  
-       while (this.value.length > this.destination.length) {
-         this.destination.push(lastDestination);
-       }
+   var transforms = /\)\s*,?\s*/; // Whitespace
  
-       while (this.value.length < this.destination.length) {
-         this.value.push(lastValue);
-       }
-     }
+   var whitespace = /\s/g; // Test hex value
  
-     return this;
-   },
-   // Clean up any duplicate points
-   settle: function settle() {
-     // find all unique values
-     for (var i = 0, il = this.value.length, seen = []; i < il; i++) {
-       if (seen.indexOf(this.value[i]) === -1) {
-         seen.push(this.value[i]);
-       }
-     } // set new value
-     this.value = seen;
-     return seen;
-   },
-   // Get morphed array at given position
-   at: function at(pos) {
-     // make sure a destination is defined
-     if (!this.destination) return this; // generate morphed array
-     for (var i = 0, il = this.value.length, array = []; i < il; i++) {
-       array.push(this.value[i] + (this.destination[i] - this.value[i]) * pos);
-     }
+   var isHex = /^#[a-f0-9]{3,6}$/i; // Test rgb value
  
-     return new SVG.Array(array);
-   },
-   toArray: function toArray() {
-     return this.value;
-   },
-   // Convert array to string
-   toString: function toString() {
-     return this.value.join(' ');
-   },
-   // Real value
-   valueOf: function valueOf() {
-     return this.value;
-   },
-   // Parse whitespace separated string
-   parse: function parse(array) {
-     array = array.valueOf(); // if already is an array, no need to parse it
-     if (Array.isArray(array)) return array;
-     return array.trim().split(SVG.regex.delimiter).map(parseFloat);
-   },
-   // Reverse array
-   reverse: function reverse() {
-     this.value.reverse();
-     return this;
-   },
-   clone: function clone() {
-     var clone = new this.constructor();
-     clone.value = arrayClone(this.value);
-     return clone;
-   }
- }); // Poly points array
+   var isRgb = /^rgb\(/; // Test css declaration
  
- SVG.PointArray = function (array, fallback) {
-   SVG.Array.call(this, array, fallback || [[0, 0]]);
- }; // Inherit from SVG.Array
+   var isCss = /[^:]+:[^;]+;?/; // Test for blank string
  
+   var isBlank = /^(\s+)?$/; // Test for numeric string
  
- SVG.PointArray.prototype = new SVG.Array();
- SVG.PointArray.prototype.constructor = SVG.PointArray;
- SVG.extend(SVG.PointArray, {
-   // Convert array to string
-   toString: function toString() {
-     // convert to a poly point string
-     for (var i = 0, il = this.value.length, array = []; i < il; i++) {
-       array.push(this.value[i].join(','));
-     }
+   var isNumber = /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i; // Test for percent value
  
-     return array.join(' ');
-   },
-   toArray: function toArray() {
-     return this.value.reduce(function (prev, curr) {
-       return [].concat.call(prev, curr);
-     }, []);
-   },
-   // Convert array to line object
-   toLine: function toLine() {
-     return {
-       x1: this.value[0][0],
-       y1: this.value[0][1],
-       x2: this.value[1][0],
-       y2: this.value[1][1]
-     };
-   },
-   // Get morphed array at given position
-   at: function at(pos) {
-     // make sure a destination is defined
-     if (!this.destination) return this; // generate morphed point string
-     for (var i = 0, il = this.value.length, array = []; i < il; i++) {
-       array.push([this.value[i][0] + (this.destination[i][0] - this.value[i][0]) * pos, this.value[i][1] + (this.destination[i][1] - this.value[i][1]) * pos]);
-     }
+   var isPercent = /^-?[\d.]+%$/; // Test for image url
  
-     return new SVG.PointArray(array);
-   },
-   // Parse point string and flat array
-   parse: function parse(array) {
-     var points = [];
-     array = array.valueOf(); // if it is an array
+   var isImage = /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i; // split at whitespace and comma
  
-     if (Array.isArray(array)) {
-       // and it is not flat, there is no need to parse it
-       if (Array.isArray(array[0])) {
-         return array;
-       }
-     } else {
-       // Else, it is considered as a string
-       // parse points
-       array = array.trim().split(SVG.regex.delimiter).map(parseFloat);
-     } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
-     // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
+   var delimiter = /[\s,]+/; // The following regex are used to parse the d attribute of a path
+   // Matches all hyphens which are not after an exponent
  
+   var hyphen = /([^e])-/gi; // Replaces and tests for all path letters
+   var pathLetters = /[MLHVCSQTAZ]/gi; // yes we need this one, too
+   var isPathLetter = /[MLHVCSQTAZ]/i; // matches 0.154.23.45
+   var numbersWithDots = /((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi; // matches .
+   var dots = /\./g;
+   var regex = /*#__PURE__*/Object.freeze({
+     numberAndUnit: numberAndUnit,
+     hex: hex,
+     rgb: rgb,
+     reference: reference,
+     transforms: transforms,
+     whitespace: whitespace,
+     isHex: isHex,
+     isRgb: isRgb,
+     isCss: isCss,
+     isBlank: isBlank,
+     isNumber: isNumber,
+     isPercent: isPercent,
+     isImage: isImage,
+     delimiter: delimiter,
+     hyphen: hyphen,
+     pathLetters: pathLetters,
+     isPathLetter: isPathLetter,
+     numbersWithDots: numbersWithDots,
+     dots: dots
+   });
  
-     if (array.length % 2 !== 0) array.pop(); // wrap points in two-tuples and parse points as floats
+   function classes() {
+     var attr = this.attr('class');
+     return attr == null ? [] : attr.trim().split(delimiter);
+   } // Return true if class exists on the node, false otherwise
  
-     for (var i = 0, len = array.length; i < len; i = i + 2) {
-       points.push([array[i], array[i + 1]]);
-     }
  
-     return points;
-   },
-   // Move point string
-   move: function move(x, y) {
-     var box = this.bbox(); // get relative offset
+   function hasClass(name) {
+     return this.classes().indexOf(name) !== -1;
+   } // Add class to the node
  
-     x -= box.x;
-     y -= box.y; // move every point
  
-     if (!isNaN(x) && !isNaN(y)) {
-       for (var i = this.value.length - 1; i >= 0; i--) {
-         this.value[i] = [this.value[i][0] + x, this.value[i][1] + y];
-       }
+   function addClass(name) {
+     if (!this.hasClass(name)) {
+       var array = this.classes();
+       array.push(name);
+       this.attr('class', array.join(' '));
      }
  
      return this;
  
      return this;
    }
- });
- /* global arrayToMatrix getOrigin isMatrixLike */
- SVG.extend(SVG.Element, {
-   // Reset all transformations
-   untransform: function untransform() {
-     return this.attr('transform', null);
-   },
-   // merge the whole transformation chain into one matrix and returns it
-   matrixify: function matrixify() {
-     var matrix = (this.attr('transform') || ''). // split transformations
-     split(SVG.regex.transforms).slice(0, -1).map(function (str) {
-       // generate key => value pairs
-       var kv = str.trim().split('(');
-       return [kv[0], kv[1].split(SVG.regex.delimiter).map(function (str) {
-         return parseFloat(str);
-       })];
-     }).reverse() // merge every transformation into one matrix
-     .reduce(function (matrix, transform) {
-       if (transform[0] === 'matrix') {
-         return matrix.lmultiply(arrayToMatrix(transform[1]));
-       }
  
-       return matrix[transform[0]].apply(matrix, transform[1]);
-     }, new SVG.Matrix());
-     return matrix;
-   },
-   // add an element to another parent without changing the visual representation on the screen
-   toParent: function toParent(parent) {
-     if (this === parent) return this;
-     var ctm = this.screenCTM();
-     var pCtm = parent.screenCTM().inverse();
-     this.addTo(parent).untransform().transform(pCtm.multiply(ctm));
-     return this;
-   },
-   // same as above with parent equals root-svg
-   toDoc: function toDoc() {
-     return this.toParent(this.doc());
-   }
- });
- SVG.extend(SVG.Element, {
-   // Add transformations
-   transform: function transform(o, relative) {
-     // Act as a getter if no object was passed
-     if (o == null || typeof o === 'string') {
-       var decomposed = new SVG.Matrix(this).decompose();
-       return decomposed[o] || decomposed;
-     }
+   var Dom =
+   /*#__PURE__*/
+   function (_EventTarget) {
+     _inherits(Dom, _EventTarget);
  
-     if (!isMatrixLike(o)) {
-       // Set the origin according to the defined transform
-       o = _objectSpread({}, o, {
-         origin: getOrigin(o, this)
-       });
-     } // The user can pass a boolean, an SVG.Element or an SVG.Matrix or nothing
+     function Dom(node) {
+       var _this;
  
+       _classCallCheck(this, Dom);
  
-     var cleanRelative = relative === true ? this : relative || false;
-     var result = new SVG.Matrix(cleanRelative).transform(o);
-     return this.attr('transform', result);
-   }
- });
- /* global camelCase */
+       _this = _possibleConstructorReturn(this, _getPrototypeOf(Dom).call(this, node));
+       _this.node = node;
+       _this.type = node.nodeName;
+       return _this;
+     } // Add given element at a position
  
- SVG.extend(SVG.Element, {
-   // Dynamic style generator
-   css: function css(s, v) {
-     var ret = {};
-     var t, i;
  
-     if (arguments.length === 0) {
-       // get full style as object
-       this.node.style.cssText.split(/\s*;\s*/).filter(function (el) {
-         return !!el.length;
-       }).forEach(function (el) {
-         t = el.split(/\s*:\s*/);
-         ret[t[0]] = t[1];
-       });
-       return ret;
-     }
+     _createClass(Dom, [{
+       key: "add",
+       value: function add(element, i) {
+         element = makeInstance(element);
  
-     if (arguments.length < 2) {
-       // get style properties in the array
-       if (Array.isArray(s)) {
-         for (i = s.length; i--;) {
-           ret[camelCase(s[i])] = this.node.style[camelCase(s[i])];
+         if (i == null) {
+           this.node.appendChild(element.node);
+         } else if (element.node !== this.node.childNodes[i]) {
+           this.node.insertBefore(element.node, this.node.childNodes[i]);
          }
  
-         return ret;
-       } // get style for property
+         return this;
+       } // Add element to given container and return self
  
+     }, {
+       key: "addTo",
+       value: function addTo(parent) {
+         return makeInstance(parent).put(this);
+       } // Returns all child elements
  
-       if (typeof s === 'string') {
-         return this.node.style[camelCase(s)];
-       } // set styles in object
+     }, {
+       key: "children",
+       value: function children() {
+         return map(this.node.children, function (node) {
+           return adopt(node);
+         });
+       } // Remove all elements in this container
  
+     }, {
+       key: "clear",
+       value: function clear() {
+         // remove children
+         while (this.node.hasChildNodes()) {
+           this.node.removeChild(this.node.lastChild);
+         } // remove defs reference
  
-       if (_typeof(s) === 'object') {
-         for (i in s) {
-           // set empty string if null/undefined/'' was given
-           this.node.style[camelCase(i)] = s[i] == null || SVG.regex.isBlank.test(s[i]) ? '' : s[i];
-         }
-       }
-     } // set style for property
  
+         delete this._defs;
+         return this;
+       } // Clone element
  
-     if (arguments.length === 2) {
-       this.node.style[camelCase(s)] = v == null || SVG.regex.isBlank.test(v) ? '' : v;
-     }
+     }, {
+       key: "clone",
+       value: function clone(parent) {
+         // write dom data to the dom so the clone can pickup the data
+         this.writeDataToDom(); // clone element and assign new id
  
-     return this;
-   }
- });
- /* global createElement */
- SVG.Parent = SVG.invent({
-   // Initialize node
-   create: function create(node) {
-     SVG.Element.call(this, node);
-   },
-   // Inherit from
-   inherit: SVG.Element,
-   // Add class methods
-   extend: {
-     // Returns all child elements
-     children: function children() {
-       return SVG.utils.map(this.node.children, function (node) {
-         return SVG.adopt(node);
-       });
-     },
-     // Add given element at a position
-     add: function add(element, i) {
-       element = createElement(element);
+         var clone = assignNewId(this.node.cloneNode(true)); // insert the clone in the given parent or after myself
  
-       if (element.node !== this.node.children[i]) {
-         this.node.insertBefore(element.node, this.node.children[i] || null);
-       }
+         if (parent) parent.add(clone); // FIXME: after might not be available here
+         else this.after(clone);
+         return clone;
+       } // Iterates over all children and invokes a given block
  
-       return this;
-     },
-     // Basically does the same as `add()` but returns the added element instead
-     put: function put(element, i) {
-       this.add(element, i);
-       return element.instance || element;
-     },
-     // Checks if the given element is a child
-     has: function has(element) {
-       return this.index(element) >= 0;
-     },
-     // Gets index of given element
-     index: function index(element) {
-       return [].slice.call(this.node.children).indexOf(element.node);
-     },
-     // Get a element at the given index
-     get: function get(i) {
-       return SVG.adopt(this.node.children[i]);
-     },
-     // Get first child
-     first: function first() {
-       return this.get(0);
-     },
-     // Get the last child
-     last: function last() {
-       return this.get(this.node.children.length - 1);
-     },
-     // Iterates over all children and invokes a given block
-     each: function each(block, deep) {
-       var children = this.children();
-       var i, il;
+     }, {
+       key: "each",
+       value: function each(block, deep) {
+         var children = this.children();
+         var i, il;
  
-       for (i = 0, il = children.length; i < il; i++) {
-         if (children[i] instanceof SVG.Element) {
+         for (i = 0, il = children.length; i < il; i++) {
            block.apply(children[i], [i, children]);
+           if (deep) {
+             children[i].each(block, deep);
+           }
+         }
+         return this;
+       } // Get first child
+     }, {
+       key: "first",
+       value: function first() {
+         return adopt(this.node.firstChild);
+       } // Get a element at the given index
+     }, {
+       key: "get",
+       value: function get(i) {
+         return adopt(this.node.childNodes[i]);
+       }
+     }, {
+       key: "getEventHolder",
+       value: function getEventHolder() {
+         return this.node;
+       }
+     }, {
+       key: "getEventTarget",
+       value: function getEventTarget() {
+         return this.node;
+       } // Checks if the given element is a child
+     }, {
+       key: "has",
+       value: function has(element) {
+         return this.index(element) >= 0;
+       } // Get / set id
+     }, {
+       key: "id",
+       value: function id(_id) {
+         // generate new id if no id set
+         if (typeof _id === 'undefined' && !this.node.id) {
+           this.node.id = eid(this.type);
+         } // dont't set directly width this.node.id to make `null` work correctly
+         return this.attr('id', _id);
+       } // Gets index of given element
+     }, {
+       key: "index",
+       value: function index(element) {
+         return [].slice.call(this.node.childNodes).indexOf(element.node);
+       } // Get the last child
+     }, {
+       key: "last",
+       value: function last() {
+         return adopt(this.node.lastChild);
+       } // matches the element vs a css selector
+     }, {
+       key: "matches",
+       value: function matches(selector) {
+         var el = this.node;
+         return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);
+       } // Returns the svg node to call native svg methods on it
+     }, {
+       key: "native",
+       value: function native() {
+         return this.node;
+       } // Returns the parent element instance
+     }, {
+       key: "parent",
+       value: function parent(type) {
+         var parent = this; // check for parent
+         if (!parent.node.parentNode) return null; // get parent element
+         parent = adopt(parent.node.parentNode);
+         if (!type) return parent; // loop trough ancestors if type is given
+         while (parent && parent.node instanceof window.SVGElement) {
+           if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent;
+           parent = adopt(parent.node.parentNode);
+         }
+       } // Basically does the same as `add()` but returns the added element instead
+     }, {
+       key: "put",
+       value: function put(element, i) {
+         this.add(element, i);
+         return element;
+       } // Add element to given container and return container
+     }, {
+       key: "putIn",
+       value: function putIn(parent) {
+         return makeInstance(parent).add(this);
+       } // Remove element
+     }, {
+       key: "remove",
+       value: function remove() {
+         if (this.parent()) {
+           this.parent().removeElement(this);
          }
  
-         if (deep && children[i] instanceof SVG.Parent) {
-           children[i].each(block, deep);
+         return this;
+       } // Remove a given child
+     }, {
+       key: "removeElement",
+       value: function removeElement(element) {
+         this.node.removeChild(element.node);
+         return this;
+       } // Replace element
+     }, {
+       key: "replace",
+       value: function replace(element) {
+         // FIXME: after() might not be available here
+         this.after(element).remove();
+         return element;
+       } // Return id on string conversion
+     }, {
+       key: "toString",
+       value: function toString() {
+         return this.id();
+       } // Import raw svg
+     }, {
+       key: "svg",
+       value: function svg(_svg) {
+         var well, len; // act as a setter if svg is given
+         if (_svg) {
+           // create temporary holder
+           well = document.createElementNS(ns, 'svg'); // dump raw svg
+           well.innerHTML = _svg; // transplant nodes
+           for (len = well.children.length; len--;) {
+             this.node.appendChild(well.firstElementChild);
+           } // otherwise act as a getter
+         } else {
+           // write svgjs data to the dom
+           this.writeDataToDom();
+           return this.node.outerHTML;
          }
+         return this;
+       } // write svgjs data to the dom
+     }, {
+       key: "writeDataToDom",
+       value: function writeDataToDom() {
+         // dump variables recursively
+         this.each(function () {
+           this.writeDataToDom();
+         });
+         return this;
        }
+     }]);
  
-       return this;
-     },
-     // Remove a given child
-     removeElement: function removeElement(element) {
-       this.node.removeChild(element.node);
-       return this;
-     },
-     // Remove all elements in this container
-     clear: function clear() {
-       // remove children
-       while (this.node.hasChildNodes()) {
-         this.node.removeChild(this.node.lastChild);
-       } // remove defs reference
+     return Dom;
+   }(EventTarget);
+   extend(Dom, {
+     attr: attr
+   });
  
+   var Doc = getClass(root);
  
-       delete this._defs;
-       return this;
-     }
-   }
- });
- SVG.extend(SVG.Parent, {
-   flatten: function flatten(parent) {
-     // flattens is only possible for nested svgs and groups
-     if (!(this instanceof SVG.G || this instanceof SVG.Doc)) {
-       return this;
-     }
+   var Element =
+   /*#__PURE__*/
+   function (_Dom) {
+     _inherits(Element, _Dom);
  
-     parent = parent || (this instanceof SVG.Doc && this.isRoot() ? this : this.parent(SVG.Parent));
-     this.each(function () {
-       if (this instanceof SVG.Defs) return this;
-       if (this instanceof SVG.Parent) return this.flatten(parent);
-       return this.toParent(parent);
-     }); // we need this so that SVG.Doc does not get removed
+     function Element(node) {
+       var _this;
  
-     this.node.firstElementChild || this.remove();
-     return this;
-   },
-   ungroup: function ungroup(parent) {
-     // ungroup is only possible for nested svgs and groups
-     if (!(this instanceof SVG.G || this instanceof SVG.Doc && !this.isRoot())) {
-       return this;
-     }
+       _classCallCheck(this, Element);
  
-     parent = parent || this.parent(SVG.Parent);
-     this.each(function () {
-       return this.toParent(parent);
-     }); // we need this so that SVG.Doc does not get removed
+       _this = _possibleConstructorReturn(this, _getPrototypeOf(Element).call(this, node)); // initialize data object
  
-     this.remove();
-     return this;
-   }
- });
- SVG.Container = SVG.invent({
-   // Initialize node
-   create: function create(node) {
-     SVG.Element.call(this, node);
-   },
-   // Inherit from
-   inherit: SVG.Parent
- });
- SVG.Defs = SVG.invent({
-   // Initialize node
-   create: 'defs',
-   // Inherit from
-   inherit: SVG.Container
- });
- SVG.G = SVG.invent({
-   // Initialize node
-   create: 'g',
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {},
-   // Add parent method
-   construct: {
-     // Create a group element
-     group: function group() {
-       return this.put(new SVG.G());
-     }
-   }
- }); // ### This module adds backward / forward functionality to elements.
- //
+       _this.dom = {}; // create circular reference
  
- SVG.extend(SVG.Element, {
-   // Get all siblings, including myself
-   siblings: function siblings() {
-     return this.parent().children();
-   },
-   // Get the curent position siblings
-   position: function position() {
-     return this.parent().index(this);
-   },
-   // Get the next element (will return null if there is none)
-   next: function next() {
-     return this.siblings()[this.position() + 1];
-   },
-   // Get the next element (will return null if there is none)
-   prev: function prev() {
-     return this.siblings()[this.position() - 1];
-   },
-   // Send given element one step forward
-   forward: function forward() {
-     var i = this.position() + 1;
-     var p = this.parent(); // move node one step forward
+       _this.node.instance = _assertThisInitialized(_assertThisInitialized(_this));
  
-     p.removeElement(this).add(this, i); // make sure defs node is always at the top
+       if (node.hasAttribute('svgjs:data')) {
+         // pull svgjs data from the dom (getAttributeNS doesn't work in html5)
+         _this.setData(JSON.parse(node.getAttribute('svgjs:data')) || {});
+       }
  
-     if (p instanceof SVG.Doc) {
-       p.node.appendChild(p.defs().node);
-     }
+       return _this;
+     } // Move element by its center
+     _createClass(Element, [{
+       key: "center",
+       value: function center(x, y) {
+         return this.cx(x).cy(y);
+       } // Move by center over x-axis
+     }, {
+       key: "cx",
+       value: function cx(x) {
+         return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2);
+       } // Move by center over y-axis
+     }, {
+       key: "cy",
+       value: function cy(y) {
+         return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2);
+       } // Get defs
+     }, {
+       key: "defs",
+       value: function defs() {
+         return this.doc().defs();
+       } // Get parent document
+     }, {
+       key: "doc",
+       value: function doc() {
+         var p = this.parent(Doc);
+         return p && p.doc();
+       }
+     }, {
+       key: "getEventHolder",
+       value: function getEventHolder() {
+         return this;
+       } // Set height of element
+     }, {
+       key: "height",
+       value: function height(_height) {
+         return this.attr('height', _height);
+       } // Checks whether the given point inside the bounding box of the element
+     }, {
+       key: "inside",
+       value: function inside(x, y) {
+         var box = this.bbox();
+         return x > box.x && y > box.y && x < box.x + box.width && y < box.y + box.height;
+       } // Move element to given x and y values
+     }, {
+       key: "move",
+       value: function move(x, y) {
+         return this.x(x).y(y);
+       } // return array of all ancestors of given type up to the root svg
+     }, {
+       key: "parents",
+       value: function parents(type) {
+         var parents = [];
+         var parent = this;
+         do {
+           parent = parent.parent(type);
+           if (!parent || parent instanceof getClass('HtmlNode')) break;
+           parents.push(parent);
+         } while (parent.parent);
+         return parents;
+       } // Get referenced element form attribute value
+     }, {
+       key: "reference",
+       value: function reference$$1(attr) {
+         attr = this.attr(attr);
+         if (!attr) return null;
+         var m = attr.match(reference);
+         return m ? makeInstance(m[1]) : null;
+       } // set given data to the elements data property
+     }, {
+       key: "setData",
+       value: function setData(o) {
+         this.dom = o;
+         return this;
+       } // Set element size to given width and height
+     }, {
+       key: "size",
+       value: function size(width, height) {
+         var p = proportionalSize(this, width, height);
+         return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height));
+       } // Set width of element
+     }, {
+       key: "width",
+       value: function width(_width) {
+         return this.attr('width', _width);
+       } // write svgjs data to the dom
+     }, {
+       key: "writeDataToDom",
+       value: function writeDataToDom() {
+         // remove previously set data
+         this.node.removeAttribute('svgjs:data');
+         if (Object.keys(this.dom).length) {
+           this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428
+         }
  
-     return this;
-   },
-   // Send given element one step backward
-   backward: function backward() {
-     var i = this.position();
+         return _get(_getPrototypeOf(Element.prototype), "writeDataToDom", this).call(this);
+       } // Move over x-axis
  
-     if (i > 0) {
-       this.parent().removeElement(this).add(this, i - 1);
-     }
+     }, {
+       key: "x",
+       value: function x(_x) {
+         return this.attr('x', _x);
+       } // Move over y-axis
  
-     return this;
-   },
-   // Send given element all the way to the front
-   front: function front() {
-     var p = this.parent(); // Move node forward
+     }, {
+       key: "y",
+       value: function y(_y) {
+         return this.attr('y', _y);
+       }
+     }]);
  
-     p.node.appendChild(this.node); // Make sure defs node is always at the top
+     return Element;
+   }(Dom);
  
-     if (p instanceof SVG.Doc) {
-       p.node.appendChild(p.defs().node);
+   var Container =
+   /*#__PURE__*/
+   function (_Element) {
+     _inherits(Container, _Element);
+     function Container() {
+       _classCallCheck(this, Container);
+       return _possibleConstructorReturn(this, _getPrototypeOf(Container).apply(this, arguments));
      }
  
-     return this;
-   },
-   // Send given element all the way to the back
-   back: function back() {
-     if (this.position() > 0) {
-       this.parent().removeElement(this).add(this, 0);
+     _createClass(Container, [{
+       key: "flatten",
+       value: function flatten(parent) {
+         this.each(function () {
+           if (this instanceof Container) return this.flatten(parent).ungroup(parent);
+           return this.toParent(parent);
+         }); // we need this so that Doc does not get removed
+         this.node.firstElementChild || this.remove();
+         return this;
+       }
+     }, {
+       key: "ungroup",
+       value: function ungroup(parent) {
+         parent = parent || this.parent();
+         this.each(function () {
+           return this.toParent(parent);
+         });
+         this.remove();
+         return this;
+       }
+     }]);
+     return Container;
+   }(Element);
+   var Defs =
+   /*#__PURE__*/
+   function (_Container) {
+     _inherits(Defs, _Container);
+     function Defs(node) {
+       _classCallCheck(this, Defs);
+       return _possibleConstructorReturn(this, _getPrototypeOf(Defs).call(this, nodeOrNew('defs', node), Defs));
      }
  
-     return this;
-   },
-   // Inserts a given element before the targeted element
-   before: function before(element) {
-     element.remove();
-     var i = this.position();
-     this.parent().add(element, i);
-     return this;
-   },
-   // Insters a given element after the targeted element
-   after: function after(element) {
-     element.remove();
-     var i = this.position();
-     this.parent().add(element, i + 1);
-     return this;
-   }
- });
- SVG.Mask = SVG.invent({
-   // Initialize node
-   create: 'mask',
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {
-     // Unmask all masked elements and remove itself
-     remove: function remove() {
-       // unmask all targets
-       this.targets().forEach(function (el) {
-         el.unmask();
-       }); // remove mask from parent
-       return SVG.Element.prototype.remove.call(this);
-     },
-     targets: function targets() {
-       return SVG.select('svg [mask*="' + this.id() + '"]');
+     _createClass(Defs, [{
+       key: "flatten",
+       value: function flatten() {
+         return this;
+       }
+     }, {
+       key: "ungroup",
+       value: function ungroup() {
+         return this;
+       }
+     }]);
+     return Defs;
+   }(Container);
+   register(Defs);
+   var Doc$1 =
+   /*#__PURE__*/
+   function (_Container) {
+     _inherits(Doc, _Container);
+     function Doc(node) {
+       var _this;
+       _classCallCheck(this, Doc);
+       _this = _possibleConstructorReturn(this, _getPrototypeOf(Doc).call(this, nodeOrNew('svg', node), Doc));
+       _this.namespace();
+       return _this;
      }
-   },
-   // Add parent method
-   construct: {
-     // Create masking element
-     mask: function mask() {
-       return this.defs().put(new SVG.Mask());
+     _createClass(Doc, [{
+       key: "isRoot",
+       value: function isRoot() {
+         return !this.node.parentNode || !(this.node.parentNode instanceof window.SVGElement) || this.node.parentNode.nodeName === '#document';
+       } // Check if this is a root svg
+       // If not, call docs from this element
+     }, {
+       key: "doc",
+       value: function doc() {
+         if (this.isRoot()) return this;
+         return _get(_getPrototypeOf(Doc.prototype), "doc", this).call(this);
+       } // Add namespaces
+     }, {
+       key: "namespace",
+       value: function namespace() {
+         if (!this.isRoot()) return this.doc().namespace();
+         return this.attr({
+           xmlns: ns,
+           version: '1.1'
+         }).attr('xmlns:xlink', xlink, xmlns).attr('xmlns:svgjs', svgjs, xmlns);
+       } // Creates and returns defs element
+     }, {
+       key: "defs",
+       value: function defs() {
+         if (!this.isRoot()) return this.doc().defs();
+         return adopt(this.node.getElementsByTagName('defs')[0]) || this.put(new Defs());
+       } // custom parent method
+     }, {
+       key: "parent",
+       value: function parent(type) {
+         if (this.isRoot()) {
+           return this.node.parentNode.nodeName === '#document' ? null : adopt(this.node.parentNode);
+         }
+         return _get(_getPrototypeOf(Doc.prototype), "parent", this).call(this, type);
+       }
+     }, {
+       key: "clear",
+       value: function clear() {
+         // remove children
+         while (this.node.hasChildNodes()) {
+           this.node.removeChild(this.node.lastChild);
+         }
+         return this;
+       }
+     }]);
+     return Doc;
+   }(Container);
+   registerMethods({
+     Container: {
+       // Create nested svg document
+       nested: function nested() {
+         return this.put(new Doc$1());
+       }
      }
-   }
- });
- SVG.extend(SVG.Element, {
-   // Distribute mask to svg element
-   maskWith: function maskWith(element) {
-     // use given mask or create a new one
-     var masker = element instanceof SVG.Mask ? element : this.parent().mask().add(element); // apply mask
-     return this.attr('mask', 'url("#' + masker.id() + '")');
-   },
-   // Unmask element
-   unmask: function unmask() {
-     return this.attr('mask', null);
-   },
-   masker: function masker() {
-     return this.reference('mask');
-   }
- });
- SVG.ClipPath = SVG.invent({
-   // Initialize node
-   create: 'clipPath',
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {
-     // Unclip all clipped elements and remove itself
-     remove: function remove() {
-       // unclip all targets
-       this.targets().forEach(function (el) {
-         el.unclip();
-       }); // remove clipPath from parent
-       return SVG.Element.prototype.remove.call(this);
-     },
-     targets: function targets() {
-       return SVG.select('svg [clip-path*="' + this.id() + '"]');
+   });
+   register(Doc$1, 'Doc', true);
+   function parser() {
+     // Reuse cached element if possible
+     if (!parser.nodes) {
+       var svg = new Doc$1().size(2, 0);
+       svg.node.cssText = ['opacity: 0', 'position: absolute', 'left: -100%', 'top: -100%', 'overflow: hidden'].join(';');
+       var path = svg.path().node;
+       parser.nodes = {
+         svg: svg,
+         path: path
+       };
      }
-   },
-   // Add parent method
-   construct: {
-     // Create clipping element
-     clip: function clip() {
-       return this.defs().put(new SVG.ClipPath());
+     if (!parser.nodes.svg.node.parentNode) {
+       var b = document.body || document.documentElement;
+       parser.nodes.svg.addTo(b);
      }
+     return parser.nodes;
    }
- }); //
- SVG.extend(SVG.Element, {
-   // Distribute clipPath to svg element
-   clipWith: function clipWith(element) {
-     // use given clip or create a new one
-     var clipper = element instanceof SVG.ClipPath ? element : this.parent().clip().add(element); // apply mask
-     return this.attr('clip-path', 'url("#' + clipper.id() + '")');
-   },
-   // Unclip element
-   unclip: function unclip() {
-     return this.attr('clip-path', null);
-   },
-   clipper: function clipper() {
-     return this.reference('clip-path');
+   var Point =
+   /*#__PURE__*/
+   function () {
+     // Initialize
+     function Point(x, y, base) {
+       _classCallCheck(this, Point);
+       var source;
+       base = base || {
+         x: 0,
+         y: 0 // ensure source as object
+       };
+       source = Array.isArray(x) ? {
+         x: x[0],
+         y: x[1]
+       } : _typeof(x) === 'object' ? {
+         x: x.x,
+         y: x.y
+       } : {
+         x: x,
+         y: y // merge source
+       };
+       this.x = source.x == null ? base.x : source.x;
+       this.y = source.y == null ? base.y : source.y;
+     } // Clone point
+     _createClass(Point, [{
+       key: "clone",
+       value: function clone() {
+         return new Point(this);
+       } // Convert to native SVGPoint
+     }, {
+       key: "native",
+       value: function native() {
+         // create new point
+         var point = parser().svg.node.createSVGPoint(); // update with current values
+         point.x = this.x;
+         point.y = this.y;
+         return point;
+       } // transform point with matrix
+     }, {
+       key: "transform",
+       value: function transform(m) {
+         // Perform the matrix multiplication
+         var x = m.a * this.x + m.c * this.y + m.e;
+         var y = m.b * this.x + m.d * this.y + m.f; // Return the required point
+         return new Point(x, y);
+       }
+     }]);
+     return Point;
+   }();
+   registerMethods({
+     Element: {
+       // Get point
+       point: function point(x, y) {
+         return new Point(x, y).transform(this.screenCTM().inverse());
+       }
+     }
+   });
+   var abcdef = 'abcdef'.split('');
+   function closeEnough(a, b, threshold) {
+     return Math.abs(b - a) < (threshold || 1e-6);
    }
- });
- SVG.Gradient = SVG.invent({
-   // Initialize node
-   create: function create(type) {
-     SVG.Element.call(this, _typeof(type) === 'object' ? type : SVG.create(type + 'Gradient'));
-   },
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {
-     // Add a color stop
-     stop: function stop(offset, color, opacity) {
-       return this.put(new SVG.Stop()).update(offset, color, opacity);
-     },
-     // Update gradient
-     update: function update(block) {
-       // remove all stops
-       this.clear(); // invoke passed block
  
-       if (typeof block === 'function') {
-         block.call(this, this);
+   var Matrix =
+   /*#__PURE__*/
+   function () {
+     function Matrix() {
+       _classCallCheck(this, Matrix);
+       this.init.apply(this, arguments);
+     } // Initialize
+     _createClass(Matrix, [{
+       key: "init",
+       value: function init(source) {
+         var base = Matrix.fromArray([1, 0, 0, 1, 0, 0]); // ensure source as object
+         source = source instanceof Element ? source.matrixify() : typeof source === 'string' ? Matrix.fromArray(source.split(delimiter).map(parseFloat)) : Array.isArray(source) ? Matrix.fromArray(source) : _typeof(source) === 'object' && Matrix.isMatrixLike(source) ? source : _typeof(source) === 'object' ? new Matrix().transform(source) : arguments.length === 6 ? Matrix.fromArray([].slice.call(arguments)) : base; // Merge the source matrix with the base matrix
+         this.a = source.a != null ? source.a : base.a;
+         this.b = source.b != null ? source.b : base.b;
+         this.c = source.c != null ? source.c : base.c;
+         this.d = source.d != null ? source.d : base.d;
+         this.e = source.e != null ? source.e : base.e;
+         this.f = source.f != null ? source.f : base.f;
+       } // Clones this matrix
+     }, {
+       key: "clone",
+       value: function clone() {
+         return new Matrix(this);
+       } // Transform a matrix into another matrix by manipulating the space
+     }, {
+       key: "transform",
+       value: function transform(o) {
+         // Check if o is a matrix and then left multiply it directly
+         if (Matrix.isMatrixLike(o)) {
+           var matrix = new Matrix(o);
+           return matrix.multiplyO(this);
+         } // Get the proposed transformations and the current transformations
+         var t = Matrix.formatTransforms(o);
+         var current = this;
+         var _transform = new Point(t.ox, t.oy).transform(current),
+             ox = _transform.x,
+             oy = _transform.y; // Construct the resulting matrix
+         var transformer = new Matrix().translateO(t.rx, t.ry).lmultiplyO(current).translateO(-ox, -oy).scaleO(t.scaleX, t.scaleY).skewO(t.skewX, t.skewY).shearO(t.shear).rotateO(t.theta).translateO(ox, oy); // If we want the origin at a particular place, we force it there
+         if (isFinite(t.px) || isFinite(t.py)) {
+           var origin = new Point(ox, oy).transform(transformer); // TODO: Replace t.px with isFinite(t.px)
+           var dx = t.px ? t.px - origin.x : 0;
+           var dy = t.py ? t.py - origin.y : 0;
+           transformer.translateO(dx, dy);
+         } // Translate now after positioning
+         transformer.translateO(t.tx, t.ty);
+         return transformer;
+       } // Applies a matrix defined by its affine parameters
+     }, {
+       key: "compose",
+       value: function compose(o) {
+         if (o.origin) {
+           o.originX = o.origin[0];
+           o.originY = o.origin[1];
+         } // Get the parameters
+         var ox = o.originX || 0;
+         var oy = o.originY || 0;
+         var sx = o.scaleX || 1;
+         var sy = o.scaleY || 1;
+         var lam = o.shear || 0;
+         var theta = o.rotate || 0;
+         var tx = o.translateX || 0;
+         var ty = o.translateY || 0; // Apply the standard matrix
+         var result = new Matrix().translateO(-ox, -oy).scaleO(sx, sy).shearO(lam).rotateO(theta).translateO(tx, ty).lmultiplyO(this).translateO(ox, oy);
+         return result;
+       } // Decomposes this matrix into its affine parameters
+     }, {
+       key: "decompose",
+       value: function decompose() {
+         var cx = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
+         var cy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
+         // Get the parameters from the matrix
+         var a = this.a;
+         var b = this.b;
+         var c = this.c;
+         var d = this.d;
+         var e = this.e;
+         var f = this.f; // Figure out if the winding direction is clockwise or counterclockwise
+         var determinant = a * d - b * c;
+         var ccw = determinant > 0 ? 1 : -1; // Since we only shear in x, we can use the x basis to get the x scale
+         // and the rotation of the resulting matrix
+         var sx = ccw * Math.sqrt(a * a + b * b);
+         var thetaRad = Math.atan2(ccw * b, ccw * a);
+         var theta = 180 / Math.PI * thetaRad;
+         var ct = Math.cos(thetaRad);
+         var st = Math.sin(thetaRad); // We can then solve the y basis vector simultaneously to get the other
+         // two affine parameters directly from these parameters
+         var lam = (a * c + b * d) / determinant;
+         var sy = c * sx / (lam * a - b) || d * sx / (lam * b + a); // Use the translations
+         var tx = e - cx + cx * ct * sx + cy * (lam * ct * sx - st * sy);
+         var ty = f - cy + cx * st * sx + cy * (lam * st * sx + ct * sy); // Construct the decomposition and return it
+         return {
+           // Return the affine parameters
+           scaleX: sx,
+           scaleY: sy,
+           shear: lam,
+           rotate: theta,
+           translateX: tx,
+           translateY: ty,
+           originX: cx,
+           originY: cy,
+           // Return the matrix parameters
+           a: this.a,
+           b: this.b,
+           c: this.c,
+           d: this.d,
+           e: this.e,
+           f: this.f
+         };
+       } // Left multiplies by the given matrix
+     }, {
+       key: "multiply",
+       value: function multiply(matrix) {
+         return this.clone().multiplyO(matrix);
+       }
+     }, {
+       key: "multiplyO",
+       value: function multiplyO(matrix) {
+         // Get the matrices
+         var l = this;
+         var r = matrix instanceof Matrix ? matrix : new Matrix(matrix);
+         return Matrix.matrixMultiply(l, r, this);
+       }
+     }, {
+       key: "lmultiply",
+       value: function lmultiply(matrix) {
+         return this.clone().lmultiplyO(matrix);
+       }
+     }, {
+       key: "lmultiplyO",
+       value: function lmultiplyO(matrix) {
+         var r = this;
+         var l = matrix instanceof Matrix ? matrix : new Matrix(matrix);
+         return Matrix.matrixMultiply(l, r, this);
+       } // Inverses matrix
+     }, {
+       key: "inverseO",
+       value: function inverseO() {
+         // Get the current parameters out of the matrix
+         var a = this.a;
+         var b = this.b;
+         var c = this.c;
+         var d = this.d;
+         var e = this.e;
+         var f = this.f; // Invert the 2x2 matrix in the top left
+         var det = a * d - b * c;
+         if (!det) throw new Error('Cannot invert ' + this); // Calculate the top 2x2 matrix
+         var na = d / det;
+         var nb = -b / det;
+         var nc = -c / det;
+         var nd = a / det; // Apply the inverted matrix to the top right
+         var ne = -(na * e + nc * f);
+         var nf = -(nb * e + nd * f); // Construct the inverted matrix
+         this.a = na;
+         this.b = nb;
+         this.c = nc;
+         this.d = nd;
+         this.e = ne;
+         this.f = nf;
+         return this;
+       }
+     }, {
+       key: "inverse",
+       value: function inverse() {
+         return this.clone().inverseO();
+       } // Translate matrix
+     }, {
+       key: "translate",
+       value: function translate(x, y) {
+         return this.clone().translateO(x, y);
+       }
+     }, {
+       key: "translateO",
+       value: function translateO(x, y) {
+         this.e += x || 0;
+         this.f += y || 0;
+         return this;
+       } // Scale matrix
+     }, {
+       key: "scale",
+       value: function scale(x, y, cx, cy) {
+         var _this$clone;
+         return (_this$clone = this.clone()).scaleO.apply(_this$clone, arguments);
+       }
+     }, {
+       key: "scaleO",
+       value: function scaleO(x) {
+         var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
+         var cx = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
+         var cy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
+         // Support uniform scaling
+         if (arguments.length === 3) {
+           cy = cx;
+           cx = y;
+           y = x;
+         }
+         var a = this.a,
+             b = this.b,
+             c = this.c,
+             d = this.d,
+             e = this.e,
+             f = this.f;
+         this.a = a * x;
+         this.b = b * y;
+         this.c = c * x;
+         this.d = d * y;
+         this.e = e * x - cx * x + cx;
+         this.f = f * y - cy * y + cy;
+         return this;
+       } // Rotate matrix
+     }, {
+       key: "rotate",
+       value: function rotate(r, cx, cy) {
+         return this.clone().rotateO(r, cx, cy);
+       }
+     }, {
+       key: "rotateO",
+       value: function rotateO(r) {
+         var cx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
+         var cy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
+         // Convert degrees to radians
+         r = radians(r);
+         var cos = Math.cos(r);
+         var sin = Math.sin(r);
+         var a = this.a,
+             b = this.b,
+             c = this.c,
+             d = this.d,
+             e = this.e,
+             f = this.f;
+         this.a = a * cos - b * sin;
+         this.b = b * cos + a * sin;
+         this.c = c * cos - d * sin;
+         this.d = d * cos + c * sin;
+         this.e = e * cos - f * sin + cy * sin - cx * cos + cx;
+         this.f = f * cos + e * sin - cx * sin - cy * cos + cy;
+         return this;
+       } // Flip matrix on x or y, at a given offset
+     }, {
+       key: "flip",
+       value: function flip(axis, around) {
+         return this.clone().flipO(axis, around);
+       }
+     }, {
+       key: "flipO",
+       value: function flipO(axis, around) {
+         return axis === 'x' ? this.scaleO(-1, 1, around, 0) : axis === 'y' ? this.scaleO(1, -1, 0, around) : this.scaleO(-1, -1, axis, around || axis); // Define an x, y flip point
+       } // Shear matrix
+     }, {
+       key: "shear",
+       value: function shear(a, cx, cy) {
+         return this.clone().shearO(a, cx, cy);
+       }
+     }, {
+       key: "shearO",
+       value: function shearO(lx) {
+         var cy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
+         var a = this.a,
+             b = this.b,
+             c = this.c,
+             d = this.d,
+             e = this.e,
+             f = this.f;
+         this.a = a + b * lx;
+         this.c = c + d * lx;
+         this.e = e + f * lx - cy * lx;
+         return this;
+       } // Skew Matrix
+     }, {
+       key: "skew",
+       value: function skew(x, y, cx, cy) {
+         var _this$clone2;
+         return (_this$clone2 = this.clone()).skewO.apply(_this$clone2, arguments);
+       }
+     }, {
+       key: "skewO",
+       value: function skewO(x) {
+         var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
+         var cx = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
+         var cy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
+         // support uniformal skew
+         if (arguments.length === 3) {
+           cy = cx;
+           cx = y;
+           y = x;
+         } // Convert degrees to radians
+         x = radians(x);
+         y = radians(y);
+         var lx = Math.tan(x);
+         var ly = Math.tan(y);
+         var a = this.a,
+             b = this.b,
+             c = this.c,
+             d = this.d,
+             e = this.e,
+             f = this.f;
+         this.a = a + b * lx;
+         this.b = b + a * ly;
+         this.c = c + d * lx;
+         this.d = d + c * ly;
+         this.e = e + f * lx - cy * lx;
+         this.f = f + e * ly - cx * ly;
+         return this;
+       } // SkewX
+     }, {
+       key: "skewX",
+       value: function skewX(x, cx, cy) {
+         return this.skew(x, 0, cx, cy);
+       }
+     }, {
+       key: "skewXO",
+       value: function skewXO(x, cx, cy) {
+         return this.skewO(x, 0, cx, cy);
+       } // SkewY
+     }, {
+       key: "skewY",
+       value: function skewY(y, cx, cy) {
+         return this.skew(0, y, cx, cy);
+       }
+     }, {
+       key: "skewYO",
+       value: function skewYO(y, cx, cy) {
+         return this.skewO(0, y, cx, cy);
+       } // Transform around a center point
+     }, {
+       key: "aroundO",
+       value: function aroundO(cx, cy, matrix) {
+         var dx = cx || 0;
+         var dy = cy || 0;
+         return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy);
+       }
+     }, {
+       key: "around",
+       value: function around(cx, cy, matrix) {
+         return this.clone().aroundO(cx, cy, matrix);
+       } // Convert to native SVGMatrix
+     }, {
+       key: "native",
+       value: function native() {
+         // create new matrix
+         var matrix = parser().svg.node.createSVGMatrix(); // update with current values
+         for (var i = abcdef.length - 1; i >= 0; i--) {
+           matrix[abcdef[i]] = this[abcdef[i]];
+         }
+         return matrix;
+       } // Check if two matrices are equal
+     }, {
+       key: "equals",
+       value: function equals(other) {
+         var comp = new Matrix(other);
+         return closeEnough(this.a, comp.a) && closeEnough(this.b, comp.b) && closeEnough(this.c, comp.c) && closeEnough(this.d, comp.d) && closeEnough(this.e, comp.e) && closeEnough(this.f, comp.f);
+       } // Convert matrix to string
+     }, {
+       key: "toString",
+       value: function toString() {
+         return 'matrix(' + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ')';
+       }
+     }, {
+       key: "toArray",
+       value: function toArray() {
+         return [this.a, this.b, this.c, this.d, this.e, this.f];
+       }
+     }, {
+       key: "valueOf",
+       value: function valueOf() {
+         return {
+           a: this.a,
+           b: this.b,
+           c: this.c,
+           d: this.d,
+           e: this.e,
+           f: this.f
+         };
+       }
+     }], [{
+       key: "fromArray",
+       value: function fromArray(a) {
+         return {
+           a: a[0],
+           b: a[1],
+           c: a[2],
+           d: a[3],
+           e: a[4],
+           f: a[5]
+         };
+       }
+     }, {
+       key: "isMatrixLike",
+       value: function isMatrixLike(o) {
+         return o.a != null || o.b != null || o.c != null || o.d != null || o.e != null || o.f != null;
+       }
+     }, {
+       key: "formatTransforms",
+       value: function formatTransforms(o) {
+         // Get all of the parameters required to form the matrix
+         var flipBoth = o.flip === 'both' || o.flip === true;
+         var flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1;
+         var flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1;
+         var skewX = o.skew && o.skew.length ? o.skew[0] : isFinite(o.skew) ? o.skew : isFinite(o.skewX) ? o.skewX : 0;
+         var skewY = o.skew && o.skew.length ? o.skew[1] : isFinite(o.skew) ? o.skew : isFinite(o.skewY) ? o.skewY : 0;
+         var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX : isFinite(o.scale) ? o.scale * flipX : isFinite(o.scaleX) ? o.scaleX * flipX : flipX;
+         var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY : isFinite(o.scale) ? o.scale * flipY : isFinite(o.scaleY) ? o.scaleY * flipY : flipY;
+         var shear = o.shear || 0;
+         var theta = o.rotate || o.theta || 0;
+         var origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY);
+         var ox = origin.x;
+         var oy = origin.y;
+         var position = new Point(o.position || o.px || o.positionX, o.py || o.positionY);
+         var px = position.x;
+         var py = position.y;
+         var translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY);
+         var tx = translate.x;
+         var ty = translate.y;
+         var relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY);
+         var rx = relative.x;
+         var ry = relative.y; // Populate all of the values
+         return {
+           scaleX: scaleX,
+           scaleY: scaleY,
+           skewX: skewX,
+           skewY: skewY,
+           shear: shear,
+           theta: theta,
+           rx: rx,
+           ry: ry,
+           tx: tx,
+           ty: ty,
+           ox: ox,
+           oy: oy,
+           px: px,
+           py: py
+         };
+       } // left matrix, right matrix, target matrix which is overwritten
+     }, {
+       key: "matrixMultiply",
+       value: function matrixMultiply(l, r, o) {
+         // Work out the product directly
+         var a = l.a * r.a + l.c * r.b;
+         var b = l.b * r.a + l.d * r.b;
+         var c = l.a * r.c + l.c * r.d;
+         var d = l.b * r.c + l.d * r.d;
+         var e = l.e + l.a * r.e + l.c * r.f;
+         var f = l.f + l.b * r.e + l.d * r.f; // make sure to use local variables because l/r and o could be the same
+         o.a = a;
+         o.b = b;
+         o.c = c;
+         o.d = d;
+         o.e = e;
+         o.f = f;
+         return o;
+       }
+     }]);
+     return Matrix;
+   }();
+   registerMethods({
+     Element: {
+       // Get current matrix
+       ctm: function ctm() {
+         return new Matrix(this.node.getCTM());
+       },
+       // Get current screen matrix
+       screenCTM: function screenCTM() {
 -        /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537\r
 -           This is needed because FF does not return the transformation matrix\r
 -           for the inner coordinate system when getScreenCTM() is called on nested svgs.\r
++        /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537
++           This is needed because FF does not return the transformation matrix
++           for the inner coordinate system when getScreenCTM() is called on nested svgs.
+            However all other Browsers do that */
+         if (typeof this.isRoot === 'function' && !this.isRoot()) {
+           var rect = this.rect(1, 1);
+           var m = rect.node.getScreenCTM();
+           rect.remove();
+           return new Matrix(m);
+         }
+         return new Matrix(this.node.getScreenCTM());
        }
+     }
+   });
  
 -  /***\r
 -  Base Class\r
 -  ==========\r
 -  The base stepper class that will be\r
++  /***
++  Base Class
++  ==========
++  The base stepper class that will be
+   ***/
+   function makeSetterGetter(k, f) {
+     return function (v) {
+       if (v == null) return this[v];
+       this[k] = v;
+       if (f) f.call(this);
        return this;
+     };
+   }
+   var easing = {
+     '-': function _(pos) {
+       return pos;
      },
-     // Return the fill id
-     url: function url() {
-       return 'url(#' + this.id() + ')';
+     '<>': function _(pos) {
+       return -Math.cos(pos * Math.PI) / 2 + 0.5;
      },
-     // Alias string convertion to fill
-     toString: function toString() {
-       return this.url();
+     '>': function _(pos) {
+       return Math.sin(pos * Math.PI / 2);
+     },
+     '<': function _(pos) {
+       return -Math.cos(pos * Math.PI / 2) + 1;
      },
-     // custom attr to handle transform
-     attr: function attr(a, b, c) {
-       if (a === 'transform') a = 'gradientTransform';
-       return SVG.Container.prototype.attr.call(this, a, b, c);
+     bezier: function bezier(t0, x0, t1, x1) {
+       return function (t) {// TODO: FINISH
+       };
      }
-   },
-   // Add parent method
-   construct: {
-     // Create gradient element in defs
-     gradient: function gradient(type, block) {
-       return this.defs().gradient(type, block);
+   };
+   var Stepper =
+   /*#__PURE__*/
+   function () {
+     function Stepper() {
+       _classCallCheck(this, Stepper);
      }
-   }
- }); // Add animatable methods to both gradient and fx module
- SVG.extend([SVG.Gradient, SVG.Timeline], {
-   // From position
-   from: function from(x, y) {
-     return (this._target || this).type === 'radialGradient' ? this.attr({
-       fx: new SVG.Number(x),
-       fy: new SVG.Number(y)
-     }) : this.attr({
-       x1: new SVG.Number(x),
-       y1: new SVG.Number(y)
-     });
-   },
-   // To position
-   to: function to(x, y) {
-     return (this._target || this).type === 'radialGradient' ? this.attr({
-       cx: new SVG.Number(x),
-       cy: new SVG.Number(y)
-     }) : this.attr({
-       x2: new SVG.Number(x),
-       y2: new SVG.Number(y)
-     });
-   }
- }); // Base gradient generation
  
- SVG.extend(SVG.Defs, {
-   // define gradient
-   gradient: function gradient(type, block) {
-     return this.put(new SVG.Gradient(type)).update(block);
-   }
- });
- SVG.Stop = SVG.invent({
-   // Initialize node
-   create: 'stop',
-   // Inherit from
-   inherit: SVG.Element,
-   // Add class methods
-   extend: {
-     // add color stops
-     update: function update(o) {
-       if (typeof o === 'number' || o instanceof SVG.Number) {
-         o = {
-           offset: arguments[0],
-           color: arguments[1],
-           opacity: arguments[2]
-         };
-       } // set attributes
+     _createClass(Stepper, [{
+       key: "done",
+       value: function done() {
+         return false;
+       }
+     }]);
+     return Stepper;
+   }();
 -  /***\r
 -  Easing Functions\r
 -  ================\r
++  /***
++  Easing Functions
++  ================
+   ***/
  
+   var Ease =
+   /*#__PURE__*/
+   function (_Stepper) {
+     _inherits(Ease, _Stepper);
  
-       if (o.opacity != null) this.attr('stop-opacity', o.opacity);
-       if (o.color != null) this.attr('stop-color', o.color);
-       if (o.offset != null) this.attr('offset', new SVG.Number(o.offset));
-       return this;
+     function Ease(fn) {
+       var _this;
+       _classCallCheck(this, Ease);
+       _this = _possibleConstructorReturn(this, _getPrototypeOf(Ease).call(this));
+       _this.ease = easing[fn || timeline.ease] || fn;
+       return _this;
      }
 -  /***\r
 -  Controller Types\r
 -  ================\r
+     _createClass(Ease, [{
+       key: "step",
+       value: function step(from, to, pos) {
+         if (typeof from !== 'number') {
+           return pos < 1 ? from : to;
+         }
+         return from + (to - from) * this.ease(pos);
+       }
+     }]);
+     return Ease;
+   }(Stepper);
++  /***
++  Controller Types
++  ================
+   ***/
+   var Controller =
+   /*#__PURE__*/
+   function (_Stepper2) {
+     _inherits(Controller, _Stepper2);
+     function Controller(fn) {
+       var _this2;
+       _classCallCheck(this, Controller);
+       _this2 = _possibleConstructorReturn(this, _getPrototypeOf(Controller).call(this));
+       _this2.stepper = fn;
+       return _this2;
+     }
+     _createClass(Controller, [{
+       key: "step",
+       value: function step(current, target, dt, c) {
+         return this.stepper(current, target, dt, c);
+       }
+     }, {
+       key: "done",
+       value: function done(c) {
+         return c.done;
+       }
+     }]);
+     return Controller;
+   }(Stepper);
+   function recalculate() {
+     // Apply the default parameters
+     var duration = (this._duration || 500) / 1000;
+     var overshoot = this._overshoot || 0; // Calculate the PID natural response
+     var eps = 1e-10;
+     var pi = Math.PI;
+     var os = Math.log(overshoot / 100 + eps);
+     var zeta = -os / Math.sqrt(pi * pi + os * os);
+     var wn = 3.9 / (zeta * duration); // Calculate the Spring values
+     this.d = 2 * zeta * wn;
+     this.k = wn * wn;
    }
- });
- SVG.Pattern = SVG.invent({
-   // Initialize node
-   create: 'pattern',
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {
-     // Return the fill id
-     url: function url() {
-       return 'url(#' + this.id() + ')';
+   var Spring =
+   /*#__PURE__*/
+   function (_Controller) {
+     _inherits(Spring, _Controller);
+     function Spring(duration, overshoot) {
+       var _this3;
+       _classCallCheck(this, Spring);
+       _this3 = _possibleConstructorReturn(this, _getPrototypeOf(Spring).call(this));
+       _this3.duration(duration || 500).overshoot(overshoot || 0);
+       return _this3;
+     }
+     _createClass(Spring, [{
+       key: "step",
+       value: function step(current, target, dt, c) {
+         if (typeof current === 'string') return current;
+         c.done = dt === Infinity;
+         if (dt === Infinity) return target;
+         if (dt === 0) return current;
+         if (dt > 100) dt = 16;
+         dt /= 1000; // Get the previous velocity
+         var velocity = c.velocity || 0; // Apply the control to get the new position and store it
+         var acceleration = -this.d * velocity - this.k * (current - target);
+         var newPosition = current + velocity * dt + acceleration * dt * dt / 2; // Store the velocity
+         c.velocity = velocity + acceleration * dt; // Figure out if we have converged, and if so, pass the value
+         c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 0.002;
+         return c.done ? target : newPosition;
+       }
+     }]);
+     return Spring;
+   }(Controller);
+   extend(Spring, {
+     duration: makeSetterGetter('_duration', recalculate),
+     overshoot: makeSetterGetter('_overshoot', recalculate)
+   });
+   var PID =
+   /*#__PURE__*/
+   function (_Controller2) {
+     _inherits(PID, _Controller2);
+     function PID(p, i, d, windup) {
+       var _this4;
+       _classCallCheck(this, PID);
+       _this4 = _possibleConstructorReturn(this, _getPrototypeOf(PID).call(this));
+       p = p == null ? 0.1 : p;
+       i = i == null ? 0.01 : i;
+       d = d == null ? 0 : d;
+       windup = windup == null ? 1000 : windup;
+       _this4.p(p).i(i).d(d).windup(windup);
+       return _this4;
+     }
+     _createClass(PID, [{
+       key: "step",
+       value: function step(current, target, dt, c) {
+         if (typeof current === 'string') return current;
+         c.done = dt === Infinity;
+         if (dt === Infinity) return target;
+         if (dt === 0) return current;
+         var p = target - current;
+         var i = (c.integral || 0) + p * dt;
+         var d = (p - (c.error || 0)) / dt;
+         var windup = this.windup; // antiwindup
+         if (windup !== false) {
+           i = Math.max(-windup, Math.min(i, windup));
+         }
+         c.error = p;
+         c.integral = i;
+         c.done = Math.abs(p) < 0.001;
+         return c.done ? target : current + (this.P * p + this.I * i + this.D * d);
+       }
+     }]);
+     return PID;
+   }(Controller);
+   extend(PID, {
+     windup: makeSetterGetter('windup'),
+     p: makeSetterGetter('P'),
+     i: makeSetterGetter('I'),
+     d: makeSetterGetter('D')
+   });
+   var Queue =
+   /*#__PURE__*/
+   function () {
+     function Queue() {
+       _classCallCheck(this, Queue);
+       this._first = null;
+       this._last = null;
+     }
+     _createClass(Queue, [{
+       key: "push",
+       value: function push(value) {
+         // An item stores an id and the provided value
+         var item = value.next ? value : {
+           value: value,
+           next: null,
+           prev: null // Deal with the queue being empty or populated
+         };
+         if (this._last) {
+           item.prev = this._last;
+           this._last.next = item;
+           this._last = item;
+         } else {
+           this._last = item;
+           this._first = item;
+         } // Update the length and return the current item
+         return item;
+       }
+     }, {
+       key: "shift",
+       value: function shift() {
+         // Check if we have a value
+         var remove = this._first;
+         if (!remove) return null; // If we do, remove it and relink things
+         this._first = remove.next;
+         if (this._first) this._first.prev = null;
+         this._last = this._first ? this._last : null;
+         return remove.value;
+       } // Shows us the first item in the list
+     }, {
+       key: "first",
+       value: function first() {
+         return this._first && this._first.value;
+       } // Shows us the last item in the list
+     }, {
+       key: "last",
+       value: function last() {
+         return this._last && this._last.value;
+       } // Removes the item that was returned from the push
+     }, {
+       key: "remove",
+       value: function remove(item) {
+         // Relink the previous item
+         if (item.prev) item.prev.next = item.next;
+         if (item.next) item.next.prev = item.prev;
+         if (item === this._last) this._last = item.prev;
+         if (item === this._first) this._first = item.next; // Invalidate item
+         item.prev = null;
+         item.next = null;
+       }
+     }]);
+     return Queue;
+   }();
+   var Animator = {
+     nextDraw: null,
+     frames: new Queue(),
+     timeouts: new Queue(),
+     timer: window.performance || window.Date,
+     transforms: [],
+     frame: function frame(fn) {
+       // Store the node
+       var node = Animator.frames.push({
+         run: fn
+       }); // Request an animation frame if we don't have one
+       if (Animator.nextDraw === null) {
+         Animator.nextDraw = window.requestAnimationFrame(Animator._draw);
+       } // Return the node so we can remove it easily
+       return node;
+     },
+     transform_frame: function transform_frame(fn, id) {
+       Animator.transforms[id] = fn;
      },
-     // Update pattern by rebuilding
-     update: function update(block) {
-       // remove content
-       this.clear(); // invoke passed block
+     timeout: function timeout(fn, delay) {
+       delay = delay || 0; // Work out when the event should fire
  
-       if (typeof block === 'function') {
-         block.call(this, this);
+       var time = Animator.timer.now() + delay; // Add the timeout to the end of the queue
+       var node = Animator.timeouts.push({
+         run: fn,
+         time: time
+       }); // Request another animation frame if we need one
+       if (Animator.nextDraw === null) {
+         Animator.nextDraw = window.requestAnimationFrame(Animator._draw);
        }
  
-       return this;
+       return node;
      },
-     // Alias string convertion to fill
-     toString: function toString() {
-       return this.url();
+     cancelFrame: function cancelFrame(node) {
+       Animator.frames.remove(node);
      },
-     // custom attr to handle transform
-     attr: function attr(a, b, c) {
-       if (a === 'transform') a = 'patternTransform';
-       return SVG.Container.prototype.attr.call(this, a, b, c);
-     }
-   },
-   // Add parent method
-   construct: {
-     // Create pattern element in defs
-     pattern: function pattern(width, height, block) {
-       return this.defs().pattern(width, height, block);
+     clearTimeout: function clearTimeout(node) {
+       Animator.timeouts.remove(node);
+     },
+     _draw: function _draw(now) {
+       // Run all the timeouts we can run, if they are not ready yet, add them
+       // to the end of the queue immediately! (bad timeouts!!! [sarcasm])
+       var nextTimeout = null;
+       var lastTimeout = Animator.timeouts.last();
+       while (nextTimeout = Animator.timeouts.shift()) {
+         // Run the timeout if its time, or push it to the end
+         if (now >= nextTimeout.time) {
+           nextTimeout.run();
+         } else {
+           Animator.timeouts.push(nextTimeout);
+         } // If we hit the last item, we should stop shifting out more items
+         if (nextTimeout === lastTimeout) break;
+       } // Run all of the animation frames
+       var nextFrame = null;
+       var lastFrame = Animator.frames.last();
+       while (nextFrame !== lastFrame && (nextFrame = Animator.frames.shift())) {
+         nextFrame.run();
+       }
+       Animator.transforms.forEach(function (el) {
+         el();
+       }); // If we have remaining timeouts or frames, draw until we don't anymore
+       Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first() ? window.requestAnimationFrame(Animator._draw) : null;
      }
+   };
+   function isNulledBox(box) {
+     return !box.w && !box.h && !box.x && !box.y;
    }
- });
- SVG.extend(SVG.Defs, {
-   // Define gradient
-   pattern: function pattern(width, height, block) {
-     return this.put(new SVG.Pattern()).update(block).attr({
-       x: 0,
-       y: 0,
-       width: width,
-       height: height,
-       patternUnits: 'userSpaceOnUse'
-     });
+   function domContains(node) {
+     return (document.documentElement.contains || function (node) {
+       // This is IE - it does not support contains() for top-level SVGs
+       while (node.parentNode) {
+         node = node.parentNode;
+       }
+       return node === document;
+     }).call(document.documentElement, node);
    }
- });
- SVG.Doc = SVG.invent({
-   // Initialize node
-   create: function create(node) {
-     SVG.Element.call(this, node || SVG.create('svg')); // set svg element attributes and ensure defs node
-     this.namespace();
-   },
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {
-     isRoot: function isRoot() {
-       return !this.node.parentNode || !(this.node.parentNode instanceof window.SVGElement) || this.node.parentNode.nodeName === '#document';
-     },
-     // Check if this is a root svg. If not, call docs from this element
-     doc: function doc() {
-       if (this.isRoot()) return this;
-       return SVG.Element.prototype.doc.call(this);
-     },
-     // Add namespaces
-     namespace: function namespace() {
-       if (!this.isRoot()) return this.doc().namespace();
-       return this.attr({
-         xmlns: SVG.ns,
-         version: '1.1'
-       }).attr('xmlns:xlink', SVG.xlink, SVG.xmlns).attr('xmlns:svgjs', SVG.svgjs, SVG.xmlns);
-     },
-     // Creates and returns defs element
-     defs: function defs() {
-       if (!this.isRoot()) return this.doc().defs();
-       return SVG.adopt(this.node.getElementsByTagName('defs')[0]) || this.put(new SVG.Defs());
+   var Box =
+   /*#__PURE__*/
+   function () {
+     function Box() {
+       _classCallCheck(this, Box);
+       this.init.apply(this, arguments);
+     }
+     _createClass(Box, [{
+       key: "init",
+       value: function init(source) {
+         var base = [0, 0, 0, 0];
+         source = typeof source === 'string' ? source.split(delimiter).map(parseFloat) : Array.isArray(source) ? source : _typeof(source) === 'object' ? [source.left != null ? source.left : source.x, source.top != null ? source.top : source.y, source.width, source.height] : arguments.length === 4 ? [].slice.call(arguments) : base;
+         this.x = source[0] || 0;
+         this.y = source[1] || 0;
+         this.width = this.w = source[2] || 0;
+         this.height = this.h = source[3] || 0; // Add more bounding box properties
+         this.x2 = this.x + this.w;
+         this.y2 = this.y + this.h;
+         this.cx = this.x + this.w / 2;
+         this.cy = this.y + this.h / 2;
+       } // Merge rect box with another, return a new instance
+     }, {
+       key: "merge",
+       value: function merge(box) {
+         var x = Math.min(this.x, box.x);
+         var y = Math.min(this.y, box.y);
+         var width = Math.max(this.x + this.width, box.x + box.width) - x;
+         var height = Math.max(this.y + this.height, box.y + box.height) - y;
+         return new Box(x, y, width, height);
+       }
+     }, {
+       key: "transform",
+       value: function transform(m) {
+         var xMin = Infinity;
+         var xMax = -Infinity;
+         var yMin = Infinity;
+         var yMax = -Infinity;
+         var pts = [new Point(this.x, this.y), new Point(this.x2, this.y), new Point(this.x, this.y2), new Point(this.x2, this.y2)];
+         pts.forEach(function (p) {
+           p = p.transform(m);
+           xMin = Math.min(xMin, p.x);
+           xMax = Math.max(xMax, p.x);
+           yMin = Math.min(yMin, p.y);
+           yMax = Math.max(yMax, p.y);
+         });
+         return new Box(xMin, yMin, xMax - xMin, yMax - yMin);
+       }
+     }, {
+       key: "addOffset",
+       value: function addOffset() {
+         // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
+         this.x += window.pageXOffset;
+         this.y += window.pageYOffset;
+         return this;
+       }
+     }, {
+       key: "toString",
+       value: function toString() {
+         return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height;
+       }
+     }, {
+       key: "toArray",
+       value: function toArray() {
+         return [this.x, this.y, this.width, this.height];
+       }
+     }, {
+       key: "isNulled",
+       value: function isNulled() {
+         return isNulledBox(this);
+       }
+     }]);
+     return Box;
+   }();
+   function getBox(cb) {
+     var box;
+     try {
+       box = cb(this.node);
+       if (isNulledBox(box) && !domContains(this.node)) {
+         throw new Error('Element not in the dom');
+       }
+     } catch (e) {
+       try {
+         var clone = this.clone(parser().svg).show();
+         box = cb(clone.node);
+         clone.remove();
+       } catch (e) {
+         console.warn('Getting a bounding box of this element is not possible');
+       }
+     }
+     return box;
+   }
+   registerMethods({
+     Element: {
+       // Get bounding box
+       bbox: function bbox() {
+         return new Box(getBox.call(this, function (node) {
+           return node.getBBox();
+         }));
+       },
+       rbox: function rbox(el) {
+         var box = new Box(getBox.call(this, function (node) {
+           return node.getBoundingClientRect();
+         }));
+         if (el) return box.transform(el.screenCTM().inverse());
+         return box.addOffset();
+       }
      },
-     // custom parent method
-     parent: function parent(type) {
-       if (this.isRoot()) {
-         return this.node.parentNode.nodeName === '#document' ? null : this.node.parentNode;
+     viewbox: {
+       viewbox: function viewbox(x, y, width, height) {
+         // act as getter
+         if (x == null) return new Box(this.attr('viewBox')); // act as setter
+         return this.attr('viewBox', new Box(x, y, width, height));
        }
 -      /**\r
 -       *\r
+     }
+   });
+   var PathArray = subClassArray('PathArray', SVGArray);
+   function pathRegReplace(a, b, c, d) {
+     return c + d.replace(dots, ' .');
+   }
+   function arrayToString(a) {
+     for (var i = 0, il = a.length, s = ''; i < il; i++) {
+       s += a[i][0];
+       if (a[i][1] != null) {
+         s += a[i][1];
+         if (a[i][2] != null) {
+           s += ' ';
+           s += a[i][2];
+           if (a[i][3] != null) {
+             s += ' ';
+             s += a[i][3];
+             s += ' ';
+             s += a[i][4];
+             if (a[i][5] != null) {
+               s += ' ';
+               s += a[i][5];
+               s += ' ';
+               s += a[i][6];
+               if (a[i][7] != null) {
+                 s += ' ';
+                 s += a[i][7];
+               }
+             }
+           }
+         }
+       }
+     }
+     return s + ' ';
+   }
+   var pathHandlers = {
+     M: function M(c, p, p0) {
+       p.x = p0.x = c[0];
+       p.y = p0.y = c[1];
+       return ['M', p.x, p.y];
+     },
+     L: function L(c, p) {
+       p.x = c[0];
+       p.y = c[1];
+       return ['L', c[0], c[1]];
+     },
+     H: function H(c, p) {
+       p.x = c[0];
+       return ['H', c[0]];
+     },
+     V: function V(c, p) {
+       p.y = c[0];
+       return ['V', c[0]];
+     },
+     C: function C(c, p) {
+       p.x = c[4];
+       p.y = c[5];
+       return ['C', c[0], c[1], c[2], c[3], c[4], c[5]];
+     },
+     S: function S(c, p) {
+       p.x = c[2];
+       p.y = c[3];
+       return ['S', c[0], c[1], c[2], c[3]];
+     },
+     Q: function Q(c, p) {
+       p.x = c[2];
+       p.y = c[3];
+       return ['Q', c[0], c[1], c[2], c[3]];
+     },
+     T: function T(c, p) {
+       p.x = c[0];
+       p.y = c[1];
+       return ['T', c[0], c[1]];
+     },
+     Z: function Z(c, p, p0) {
+       p.x = p0.x;
+       p.y = p0.y;
+       return ['Z'];
+     },
+     A: function A(c, p) {
+       p.x = c[5];
+       p.y = c[6];
+       return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]];
+     }
+   };
+   var mlhvqtcsaz = 'mlhvqtcsaz'.split('');
+   for (var i = 0, il = mlhvqtcsaz.length; i < il; ++i) {
+     pathHandlers[mlhvqtcsaz[i]] = function (i) {
+       return function (c, p, p0) {
+         if (i === 'H') c[0] = c[0] + p.x;else if (i === 'V') c[0] = c[0] + p.y;else if (i === 'A') {
+           c[5] = c[5] + p.x;
+           c[6] = c[6] + p.y;
+         } else {
+           for (var j = 0, jl = c.length; j < jl; ++j) {
+             c[j] = c[j] + (j % 2 ? p.y : p.x);
+           }
+         }
+         return pathHandlers[i](c, p, p0);
+       };
+     }(mlhvqtcsaz[i].toUpperCase());
+   }
+   extend(PathArray, {
+     // Convert array to string
+     toString: function toString() {
+       return arrayToString(this);
+     },
+     // Move path string
+     move: function move(x, y) {
+       // get bounding box of current situation
+       var box = this.bbox(); // get relative offset
+       x -= box.x;
+       y -= box.y;
+       if (!isNaN(x) && !isNaN(y)) {
+         // move every point
+         for (var l, i = this.length - 1; i >= 0; i--) {
+           l = this[i][0];
+           if (l === 'M' || l === 'L' || l === 'T') {
+             this[i][1] += x;
+             this[i][2] += y;
+           } else if (l === 'H') {
+             this[i][1] += x;
+           } else if (l === 'V') {
+             this[i][1] += y;
+           } else if (l === 'C' || l === 'S' || l === 'Q') {
+             this[i][1] += x;
+             this[i][2] += y;
+             this[i][3] += x;
+             this[i][4] += y;
+             if (l === 'C') {
+               this[i][5] += x;
+               this[i][6] += y;
+             }
+           } else if (l === 'A') {
+             this[i][6] += x;
+             this[i][7] += y;
+           }
+         }
+       }
+       return this;
+     },
+     // Resize path string
+     size: function size(width, height) {
+       // get bounding box of current situation
+       var box = this.bbox();
+       var i, l; // recalculate position of all points according to new size
+       for (i = this.length - 1; i >= 0; i--) {
+         l = this[i][0];
+         if (l === 'M' || l === 'L' || l === 'T') {
+           this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
+           this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
+         } else if (l === 'H') {
+           this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
+         } else if (l === 'V') {
+           this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
+         } else if (l === 'C' || l === 'S' || l === 'Q') {
+           this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
+           this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
+           this[i][3] = (this[i][3] - box.x) * width / box.width + box.x;
+           this[i][4] = (this[i][4] - box.y) * height / box.height + box.y;
+           if (l === 'C') {
+             this[i][5] = (this[i][5] - box.x) * width / box.width + box.x;
+             this[i][6] = (this[i][6] - box.y) * height / box.height + box.y;
+           }
+         } else if (l === 'A') {
+           // resize radii
+           this[i][1] = this[i][1] * width / box.width;
+           this[i][2] = this[i][2] * height / box.height; // move position values
+           this[i][6] = (this[i][6] - box.x) * width / box.width + box.x;
+           this[i][7] = (this[i][7] - box.y) * height / box.height + box.y;
+         }
+       }
+       return this;
+     },
+     // Test if the passed path array use the same path data commands as this path array
+     equalCommands: function equalCommands(pathArray) {
+       var i, il, equalCommands;
+       pathArray = new PathArray(pathArray);
+       equalCommands = this.length === pathArray.length;
+       for (i = 0, il = this.length; equalCommands && i < il; i++) {
+         equalCommands = this[i][0] === pathArray[i][0];
+       }
+       return equalCommands;
+     },
+     // Make path array morphable
+     morph: function morph(pathArray) {
+       pathArray = new PathArray(pathArray);
+       if (this.equalCommands(pathArray)) {
+         this.destination = pathArray;
+       } else {
+         this.destination = null;
+       }
+       return this;
+     },
+     // Get morphed path array at given position
+     at: function at(pos) {
+       // make sure a destination is defined
+       if (!this.destination) return this;
+       var sourceArray = this;
+       var destinationArray = this.destination.value;
+       var array = [];
+       var pathArray = new PathArray();
+       var i, il, j, jl; // Animate has specified in the SVG spec
+       // See: https://www.w3.org/TR/SVG11/paths.html#PathElement
+       for (i = 0, il = sourceArray.length; i < il; i++) {
+         array[i] = [sourceArray[i][0]];
+         for (j = 1, jl = sourceArray[i].length; j < jl; j++) {
+           array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos;
+         } // For the two flags of the elliptical arc command, the SVG spec say:
+         // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true
+         // Elliptical arc command as an array followed by corresponding indexes:
+         // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
+         //   0    1   2        3                 4             5      6  7
+         if (array[i][0] === 'A') {
+           array[i][4] = +(array[i][4] !== 0);
+           array[i][5] = +(array[i][5] !== 0);
+         }
+       } // Directly modify the value of a path array, this is done this way for performance
+       pathArray.value = array;
+       return pathArray;
+     },
+     // Absolutize and parse path to array
+     parse: function parse() {
+       var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [['M', 0, 0]];
+       // if it's already a patharray, no need to parse it
+       if (array instanceof PathArray) return array; // prepare for parsing
+       var s;
+       var paramCnt = {
+         'M': 2,
+         'L': 2,
+         'H': 1,
+         'V': 1,
+         'C': 6,
+         'S': 4,
+         'Q': 4,
+         'T': 2,
+         'A': 7,
+         'Z': 0
+       };
+       if (typeof array === 'string') {
+         array = array.replace(numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
+         .replace(pathLetters, ' $& ') // put some room between letters and numbers
+         .replace(hyphen, '$1 -') // add space before hyphen
+         .trim() // trim
+         .split(delimiter); // split into array
+       } else {
+         array = array.reduce(function (prev, curr) {
+           return [].concat.call(prev, curr);
+         }, []);
+       } // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...]
+       var result = [];
+       var p = new Point();
+       var p0 = new Point();
+       var index = 0;
+       var len = array.length;
+       do {
+         // Test if we have a path letter
+         if (isPathLetter.test(array[index])) {
+           s = array[index];
+           ++index; // If last letter was a move command and we got no new, it defaults to [L]ine
+         } else if (s === 'M') {
+           s = 'L';
+         } else if (s === 'm') {
+           s = 'l';
+         }
+         result.push(pathHandlers[s].call(null, array.slice(index, index = index + paramCnt[s.toUpperCase()]).map(parseFloat), p, p0));
+       } while (len > index);
+       return result;
+     },
+     // Get bounding box of path
+     bbox: function bbox() {
+       parser().path.setAttribute('d', this.toString());
+       return parser.nodes.path.getBBox();
+     }
+   });
+   var Morphable =
+   /*#__PURE__*/
+   function () {
+     function Morphable(stepper) {
+       _classCallCheck(this, Morphable);
+       // FIXME: the default stepper does not know about easing
+       this._stepper = stepper || new Ease('-');
+       this._from = null;
+       this._to = null;
+       this._type = null;
+       this._context = null;
+       this._morphObj = null;
+     }
+     _createClass(Morphable, [{
+       key: "from",
+       value: function from(val) {
+         if (val == null) {
+           return this._from;
+         }
+         this._from = this._set(val);
+         return this;
+       }
+     }, {
+       key: "to",
+       value: function to(val) {
+         if (val == null) {
+           return this._to;
+         }
+         this._to = this._set(val);
+         return this;
+       }
+     }, {
+       key: "type",
+       value: function type(_type) {
+         // getter
+         if (_type == null) {
+           return this._type;
+         } // setter
+         this._type = _type;
+         return this;
+       }
+     }, {
+       key: "_set",
+       value: function _set$$1(value) {
+         if (!this._type) {
+           var type = _typeof(value);
+           if (type === 'number') {
+             this.type(SVGNumber);
+           } else if (type === 'string') {
+             if (Color.isColor(value)) {
+               this.type(Color);
+             } else if (delimiter.test(value)) {
+               this.type(pathLetters.test(value) ? PathArray : SVGArray);
+             } else if (numberAndUnit.test(value)) {
+               this.type(SVGNumber);
+             } else {
+               this.type(NonMorphable);
+             }
+           } else if (morphableTypes.indexOf(value.constructor) > -1) {
+             this.type(value.constructor);
+           } else if (Array.isArray(value)) {
+             this.type(SVGArray);
+           } else if (type === 'object') {
+             this.type(ObjectBag);
+           } else {
+             this.type(NonMorphable);
+           }
+         }
+         var result = new this._type(value).toArray();
+         this._morphObj = this._morphObj || new this._type();
+         this._context = this._context || Array.apply(null, Array(result.length)).map(Object);
+         return result;
+       }
+     }, {
+       key: "stepper",
+       value: function stepper(_stepper) {
+         if (_stepper == null) return this._stepper;
+         this._stepper = _stepper;
+         return this;
+       }
+     }, {
+       key: "done",
+       value: function done() {
+         var complete = this._context.map(this._stepper.done).reduce(function (last, curr) {
+           return last && curr;
+         }, true);
+         return complete;
+       }
+     }, {
+       key: "at",
+       value: function at(pos) {
+         var _this = this;
+         return this._morphObj.fromArray(this._from.map(function (i, index) {
+           return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context);
+         }));
+       }
+     }]);
+     return Morphable;
+   }();
+   var NonMorphable =
+   /*#__PURE__*/
+   function () {
+     function NonMorphable() {
+       _classCallCheck(this, NonMorphable);
+       this.init.apply(this, arguments);
+     }
+     _createClass(NonMorphable, [{
+       key: "init",
+       value: function init(val) {
+         val = Array.isArray(val) ? val[0] : val;
+         this.value = val;
+       }
+     }, {
+       key: "valueOf",
+       value: function valueOf() {
+         return this.value;
+       }
+     }, {
+       key: "toArray",
+       value: function toArray() {
+         return [this.value];
+       }
+     }]);
+     return NonMorphable;
+   }();
+   var TransformBag =
+   /*#__PURE__*/
+   function () {
+     function TransformBag() {
+       _classCallCheck(this, TransformBag);
+       this.init.apply(this, arguments);
+     }
+     _createClass(TransformBag, [{
+       key: "init",
+       value: function init(obj) {
+         if (Array.isArray(obj)) {
+           obj = {
+             scaleX: obj[0],
+             scaleY: obj[1],
+             shear: obj[2],
+             rotate: obj[3],
+             translateX: obj[4],
+             translateY: obj[5],
+             originX: obj[6],
+             originY: obj[7]
+           };
+         }
+         Object.assign(this, TransformBag.defaults, obj);
+       }
+     }, {
+       key: "toArray",
+       value: function toArray() {
+         var v = this;
+         return [v.scaleX, v.scaleY, v.shear, v.rotate, v.translateX, v.translateY, v.originX, v.originY];
+       }
+     }]);
+     return TransformBag;
+   }();
+   TransformBag.defaults = {
+     scaleX: 1,
+     scaleY: 1,
+     shear: 0,
+     rotate: 0,
+     translateX: 0,
+     translateY: 0,
+     originX: 0,
+     originY: 0
+   };
+   var ObjectBag =
+   /*#__PURE__*/
+   function () {
+     function ObjectBag() {
+       _classCallCheck(this, ObjectBag);
+       this.init.apply(this, arguments);
+     }
+     _createClass(ObjectBag, [{
+       key: "init",
+       value: function init(objOrArr) {
+         this.values = [];
+         if (Array.isArray(objOrArr)) {
+           this.values = objOrArr;
+           return;
+         }
+         var entries = Object.entries(objOrArr || {}).sort(function (a, b) {
+           return a[0] - b[0];
+         });
+         this.values = entries.reduce(function (last, curr) {
+           return last.concat(curr);
+         }, []);
+       }
+     }, {
+       key: "valueOf",
+       value: function valueOf() {
+         var obj = {};
+         var arr = this.values;
+         for (var i = 0, len = arr.length; i < len; i += 2) {
+           obj[arr[i]] = arr[i + 1];
+         }
+         return obj;
+       }
+     }, {
+       key: "toArray",
+       value: function toArray() {
+         return this.values;
+       }
+     }]);
+     return ObjectBag;
+   }();
+   var morphableTypes = [NonMorphable, TransformBag, ObjectBag];
+   function registerMorphableType() {
+     var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+     morphableTypes.push.apply(morphableTypes, _toConsumableArray([].concat(type)));
+   }
+   function makeMorphable() {
+     extend(morphableTypes, {
+       to: function to(val, args) {
+         return new Morphable().type(this.constructor).from(this.valueOf()).to(val, args);
+       },
+       fromArray: function fromArray(arr) {
+         this.init(arr);
+         return this;
+       }
+     });
+   }
+   var time = window.performance || Date;
+   var makeSchedule = function makeSchedule(runnerInfo) {
+     var start = runnerInfo.start;
+     var duration = runnerInfo.runner.duration();
+     var end = start + duration;
+     return {
+       start: start,
+       duration: duration,
+       end: end,
+       runner: runnerInfo.runner
+     };
+   };
+   var Timeline =
+   /*#__PURE__*/
+   function () {
+     // Construct a new timeline on the given element
+     function Timeline() {
+       _classCallCheck(this, Timeline);
+       this._timeSource = function () {
+         return time.now();
+       };
+       this._dispatcher = document.createElement('div'); // Store the timing variables
+       this._startTime = 0;
+       this._speed = 1.0; // Play control variables control how the animation proceeds
+       this._reverse = false;
+       this._persist = 0; // Keep track of the running animations and their starting parameters
+       this._nextFrame = null;
+       this._paused = false;
+       this._runners = [];
+       this._order = [];
+       this._time = 0;
+       this._lastSourceTime = 0;
+       this._lastStepTime = 0;
+     }
+     _createClass(Timeline, [{
+       key: "getEventTarget",
+       value: function getEventTarget() {
+         return this._dispatcher;
+       }
 -    /*\r
 -    Runner Definitions\r
 -    ==================\r
 -    These methods help us define the runtime behaviour of the Runner or they\r
 -    help us make new runners from the current runner\r
++      /**
++       *
+        */
+       // schedules a runner on the timeline
+     }, {
+       key: "schedule",
+       value: function schedule(runner, delay, when) {
+         if (runner == null) {
+           return this._runners.map(makeSchedule).sort(function (a, b) {
+             return a.start - b.start || a.duration - b.duration;
+           });
+         }
+         if (!this.active()) {
+           this._step();
+           if (when == null) {
+             when = 'now';
+           }
+         } // The start time for the next animation can either be given explicitly,
+         // derived from the current timeline time or it can be relative to the
+         // last start time to chain animations direclty
+         var absoluteStartTime = 0;
+         delay = delay || 0; // Work out when to start the animation
+         if (when == null || when === 'last' || when === 'after') {
+           // Take the last time and increment
+           absoluteStartTime = this._startTime;
+         } else if (when === 'absolute' || when === 'start') {
+           absoluteStartTime = delay;
+           delay = 0;
+         } else if (when === 'now') {
+           absoluteStartTime = this._time;
+         } else if (when === 'relative') {
+           var runnerInfo = this._runners[runner.id];
+           if (runnerInfo) {
+             absoluteStartTime = runnerInfo.start + delay;
+             delay = 0;
+           }
+         } else {
+           throw new Error('Invalid value for the "when" parameter');
+         } // Manage runner
+         runner.unschedule();
+         runner.timeline(this);
+         runner.time(-delay); // Save startTime for next runner
+         this._startTime = absoluteStartTime + runner.duration() + delay; // Save runnerInfo
+         this._runners[runner.id] = {
+           persist: this.persist(),
+           runner: runner,
+           start: absoluteStartTime // Save order and continue
+         };
+         this._order.push(runner.id);
+         this._continue();
+         return this;
+       } // Remove the runner from this timeline
+     }, {
+       key: "unschedule",
+       value: function unschedule(runner) {
+         var index = this._order.indexOf(runner.id);
+         if (index < 0) return this;
+         delete this._runners[runner.id];
+         this._order.splice(index, 1);
+         runner.timeline(null);
+         return this;
+       }
+     }, {
+       key: "play",
+       value: function play() {
+         // Now make sure we are not paused and continue the animation
+         this._paused = false;
+         return this._continue();
+       }
+     }, {
+       key: "pause",
+       value: function pause() {
+         // Cancel the next animation frame and pause
+         this._nextFrame = null;
+         this._paused = true;
+         return this;
+       }
+     }, {
+       key: "stop",
+       value: function stop() {
+         // Cancel the next animation frame and go to start
+         this.seek(-this._time);
+         return this.pause();
+       }
+     }, {
+       key: "finish",
+       value: function finish() {
+         this.seek(Infinity);
+         return this.pause();
+       }
+     }, {
+       key: "speed",
+       value: function speed(_speed) {
+         if (_speed == null) return this._speed;
+         this._speed = _speed;
+         return this;
+       }
+     }, {
+       key: "reverse",
+       value: function reverse(yes) {
+         var currentSpeed = this.speed();
+         if (yes == null) return this.speed(-currentSpeed);
+         var positive = Math.abs(currentSpeed);
+         return this.speed(yes ? positive : -positive);
+       }
+     }, {
+       key: "seek",
+       value: function seek(dt) {
+         this._time += dt;
+         return this._continue();
+       }
+     }, {
+       key: "time",
+       value: function time(_time) {
+         if (_time == null) return this._time;
+         this._time = _time;
+         return this;
+       }
+     }, {
+       key: "persist",
+       value: function persist(dtOrForever) {
+         if (dtOrForever == null) return this._persist;
+         this._persist = dtOrForever;
+         return this;
+       }
+     }, {
+       key: "source",
+       value: function source(fn) {
+         if (fn == null) return this._timeSource;
+         this._timeSource = fn;
+         return this;
+       }
+     }, {
+       key: "_step",
+       value: function _step() {
+         // If the timeline is paused, just do nothing
+         if (this._paused) return; // Get the time delta from the last time and update the time
+         // TODO: Deal with window.blur window.focus to pause animations
+         var time = this._timeSource();
+         var dtSource = time - this._lastSourceTime;
+         var dtTime = this._speed * dtSource + (this._time - this._lastStepTime);
+         this._lastSourceTime = time; // Update the time
+         this._time += dtTime;
+         this._lastStepTime = this._time; // this.fire('time', this._time)
+         // Run all of the runners directly
+         var runnersLeft = false;
+         for (var i = 0, len = this._order.length; i < len; i++) {
+           // Get and run the current runner and ignore it if its inactive
+           var runnerInfo = this._runners[this._order[i]];
+           var runner = runnerInfo.runner;
+           var dt = dtTime; // Make sure that we give the actual difference
+           // between runner start time and now
+           var dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet
+           if (dtToStart < 0) {
+             runnersLeft = true;
+             continue;
+           } else if (dtToStart < dt) {
+             // Adjust dt to make sure that animation is on point
+             dt = dtToStart;
+           }
+           if (!runner.active()) continue; // If this runner is still going, signal that we need another animation
+           // frame, otherwise, remove the completed runner
+           var finished = runner.step(dt).done;
+           if (!finished) {
+             runnersLeft = true; // continue
+           } else if (runnerInfo.persist !== true) {
+             // runner is finished. And runner might get removed
+             // TODO: Figure out end time of runner
+             var endTime = runner.duration() - runner.time() + this._time;
+             if (endTime + this._persist < this._time) {
+               // Delete runner and correct index
+               delete this._runners[this._order[i]];
+               this._order.splice(i--, 1) && --len;
+               runner.timeline(null);
+             }
+           }
+         } // Get the next animation frame to keep the simulation going
+         if (runnersLeft) {
+           this._nextFrame = Animator.frame(this._step.bind(this));
+         } else {
+           this._nextFrame = null;
+         }
+         return this;
+       } // Checks if we are running and continues the animation
+     }, {
+       key: "_continue",
+       value: function _continue() {
+         if (this._paused) return this;
+         if (!this._nextFrame) {
+           this._nextFrame = Animator.frame(this._step.bind(this));
+         }
+         return this;
+       }
+     }, {
+       key: "active",
+       value: function active() {
+         return !!this._nextFrame;
+       }
+     }]);
+     return Timeline;
+   }();
+   registerMethods({
+     Element: {
+       timeline: function timeline() {
+         this._timeline = this._timeline || new Timeline();
+         return this._timeline;
+       }
+     }
+   });
+   var Runner =
+   /*#__PURE__*/
+   function (_EventTarget) {
+     _inherits(Runner, _EventTarget);
+     function Runner(options) {
+       var _this;
+       _classCallCheck(this, Runner);
+       _this = _possibleConstructorReturn(this, _getPrototypeOf(Runner).call(this)); // Store a unique id on the runner, so that we can identify it later
+       _this.id = Runner.id++; // Ensure a default value
+       options = options == null ? timeline.duration : options; // Ensure that we get a controller
+       options = typeof options === 'function' ? new Controller(options) : options; // Declare all of the variables
+       _this._element = null;
+       _this._timeline = null;
+       _this.done = false;
+       _this._queue = []; // Work out the stepper and the duration
+       _this._duration = typeof options === 'number' && options;
+       _this._isDeclarative = options instanceof Controller;
+       _this._stepper = _this._isDeclarative ? options : new Ease(); // We copy the current values from the timeline because they can change
+       _this._history = {}; // Store the state of the runner
+       _this.enabled = true;
+       _this._time = 0;
+       _this._last = 0; // Save transforms applied to this runner
+       _this.transforms = new Matrix();
+       _this.transformId = 1; // Looping variables
+       _this._haveReversed = false;
+       _this._reverse = false;
+       _this._loopsDone = 0;
+       _this._swing = false;
+       _this._wait = 0;
+       _this._times = 1;
+       return _this;
+     }
 -      /*\r
 -      Basic Functionality\r
 -      ===================\r
 -      These methods allow us to attach basic functions to the runner directly\r
++    /*
++    Runner Definitions
++    ==================
++    These methods help us define the runtime behaviour of the Runner or they
++    help us make new runners from the current runner
+     */
+     _createClass(Runner, [{
+       key: "element",
+       value: function element(_element) {
+         if (_element == null) return this._element;
+         this._element = _element;
+         _element._prepareRunner();
+         return this;
+       }
+     }, {
+       key: "timeline",
+       value: function timeline$$1(_timeline) {
+         // check explicitly for undefined so we can set the timeline to null
+         if (typeof _timeline === 'undefined') return this._timeline;
+         this._timeline = _timeline;
+         return this;
+       }
+     }, {
+       key: "animate",
+       value: function animate(duration, delay, when) {
+         var o = Runner.sanitise(duration, delay, when);
+         var runner = new Runner(o.duration);
+         if (this._timeline) runner.timeline(this._timeline);
+         if (this._element) runner.element(this._element);
+         return runner.loop(o).schedule(delay, when);
+       }
+     }, {
+       key: "schedule",
+       value: function schedule(timeline$$1, delay, when) {
+         // The user doesn't need to pass a timeline if we already have one
+         if (!(timeline$$1 instanceof Timeline)) {
+           when = delay;
+           delay = timeline$$1;
+           timeline$$1 = this.timeline();
+         } // If there is no timeline, yell at the user...
+         if (!timeline$$1) {
+           throw Error('Runner cannot be scheduled without timeline');
+         } // Schedule the runner on the timeline provided
+         timeline$$1.schedule(this, delay, when);
+         return this;
+       }
+     }, {
+       key: "unschedule",
+       value: function unschedule() {
+         var timeline$$1 = this.timeline();
+         timeline$$1 && timeline$$1.unschedule(this);
+         return this;
+       }
+     }, {
+       key: "loop",
+       value: function loop(times, swing, wait) {
+         // Deal with the user passing in an object
+         if (_typeof(times) === 'object') {
+           swing = times.swing;
+           wait = times.wait;
+           times = times.times;
+         } // Sanitise the values and store them
+         this._times = times || Infinity;
+         this._swing = swing || false;
+         this._wait = wait || 0;
+         return this;
+       }
+     }, {
+       key: "delay",
+       value: function delay(_delay) {
+         return this.animate(0, _delay);
+       }
 -      /*\r
 -      Runner animation methods\r
 -      ========================\r
 -      Control how the animation plays\r
++      /*
++      Basic Functionality
++      ===================
++      These methods allow us to attach basic functions to the runner directly
+       */
+     }, {
+       key: "queue",
+       value: function queue(initFn, runFn, isTransform) {
+         this._queue.push({
+           initialiser: initFn || noop,
+           runner: runFn || noop,
+           isTransform: isTransform,
+           initialised: false,
+           finished: false
+         });
+         var timeline$$1 = this.timeline();
+         timeline$$1 && this.timeline()._continue();
+         return this;
+       }
+     }, {
+       key: "during",
+       value: function during(fn) {
+         return this.queue(null, fn);
+       }
+     }, {
+       key: "after",
+       value: function after(fn) {
+         return this.on('finish', fn);
+       }
 -          /*\r
 -          This function converts a time to a position in the range [0, 1]\r
 -          The full explanation can be found in this desmos demonstration\r
 -            https://www.desmos.com/calculator/u4fbavgche\r
 -          The logic is slightly simplified here because we can use booleans\r
++      /*
++      Runner animation methods
++      ========================
++      Control how the animation plays
+       */
+     }, {
+       key: "time",
+       value: function time(_time) {
+         if (_time == null) {
+           return this._time;
+         }
+         var dt = _time - this._time;
+         this.step(dt);
+         return this;
+       }
+     }, {
+       key: "duration",
+       value: function duration() {
+         return this._times * (this._wait + this._duration) - this._wait;
+       }
+     }, {
+       key: "loops",
+       value: function loops(p) {
+         var loopDuration = this._duration + this._wait;
+         if (p == null) {
+           var loopsDone = Math.floor(this._time / loopDuration);
+           var relativeTime = this._time - loopsDone * loopDuration;
+           var position = relativeTime / this._duration;
+           return Math.min(loopsDone + position, this._times);
+         }
+         var whole = Math.floor(p);
+         var partial = p % 1;
+         var time = loopDuration * whole + this._duration * partial;
+         return this.time(time);
+       }
+     }, {
+       key: "position",
+       value: function position(p) {
+         // Get all of the variables we need
+         var x = this._time;
+         var d = this._duration;
+         var w = this._wait;
+         var t = this._times;
+         var s = this._swing;
+         var r = this._reverse;
+         var position;
+         if (p == null) {
 -      /*\r
 -      Private Methods\r
 -      ===============\r
 -      Methods that shouldn't be used externally\r
++          /*
++          This function converts a time to a position in the range [0, 1]
++          The full explanation can be found in this desmos demonstration
++            https://www.desmos.com/calculator/u4fbavgche
++          The logic is slightly simplified here because we can use booleans
+           */
+           // Figure out the value without thinking about the start or end time
+           var f = function f(x) {
+             var swinging = s * Math.floor(x % (2 * (w + d)) / (w + d));
+             var backwards = swinging && !r || !swinging && r;
+             var uncliped = Math.pow(-1, backwards) * (x % (w + d)) / d + backwards;
+             var clipped = Math.max(Math.min(uncliped, 1), 0);
+             return clipped;
+           }; // Figure out the value by incorporating the start time
+           var endTime = t * (w + d) - w;
+           position = x <= 0 ? Math.round(f(1e-5)) : x < endTime ? f(x) : Math.round(f(endTime - 1e-5));
+           return position;
+         } // Work out the loops done and add the position to the loops done
+         var loopsDone = Math.floor(this.loops());
+         var swingForward = s && loopsDone % 2 === 0;
+         var forwards = swingForward && !r || r && swingForward;
+         position = loopsDone + (forwards ? p : 1 - p);
+         return this.loops(position);
+       }
+     }, {
+       key: "progress",
+       value: function progress(p) {
+         if (p == null) {
+           return Math.min(1, this._time / this.duration());
+         }
+         return this.time(p * this.duration());
+       }
+     }, {
+       key: "step",
+       value: function step(dt) {
+         // If we are inactive, this stepper just gets skipped
+         if (!this.enabled) return this; // Update the time and get the new position
+         dt = dt == null ? 16 : dt;
+         this._time += dt;
+         var position = this.position(); // Figure out if we need to run the stepper in this frame
+         var running = this._lastPosition !== position && this._time >= 0;
+         this._lastPosition = position; // Figure out if we just started
+         var duration = this.duration();
+         var justStarted = this._lastTime < 0 && this._time > 0;
+         var justFinished = this._lastTime < this._time && this.time > duration;
+         this._lastTime = this._time;
+         if (justStarted) {
+           this.fire('start', this);
+         } // Work out if the runner is finished set the done flag here so animations
+         // know, that they are running in the last step (this is good for
+         // transformations which can be merged)
+         var declarative = this._isDeclarative;
+         this.done = !declarative && !justFinished && this._time >= duration; // Call initialise and the run function
+         if (running || declarative) {
+           this._initialise(running); // clear the transforms on this runner so they dont get added again and again
+           this.transforms = new Matrix();
+           var converged = this._run(declarative ? dt : position);
+           this.fire('step', this);
+         } // correct the done flag here
+         // declaritive animations itself know when they converged
+         this.done = this.done || converged && declarative;
+         if (this.done) {
+           this.fire('finish', this);
+         }
+         return this;
+       }
+     }, {
+       key: "finish",
+       value: function finish() {
+         return this.step(Infinity);
+       }
+     }, {
+       key: "reverse",
+       value: function reverse(_reverse) {
+         this._reverse = _reverse == null ? !this._reverse : _reverse;
+         return this;
+       }
+     }, {
+       key: "ease",
+       value: function ease(fn) {
+         this._stepper = new Ease(fn);
+         return this;
+       }
+     }, {
+       key: "active",
+       value: function active(enabled) {
+         if (enabled == null) return this.enabled;
+         this.enabled = enabled;
+         return this;
+       }
++      /*
++      Private Methods
++      ===============
++      Methods that shouldn't be used externally
+       */
+       // Save a morpher to the morpher list so that we can retarget it later
+     }, {
+       key: "_rememberMorpher",
+       value: function _rememberMorpher(method, morpher) {
+         this._history[method] = {
+           morpher: morpher,
+           caller: this._queue[this._queue.length - 1]
+         };
+       } // Try to set the target for a morpher if the morpher exists, otherwise
+       // do nothing and return false
+     }, {
+       key: "_tryRetarget",
+       value: function _tryRetarget(method, target) {
+         if (this._history[method]) {
+           // if the last method wasnt even initialised, throw it away
+           if (!this._history[method].caller.initialised) {
+             var index = this._queue.indexOf(this._history[method].caller);
+             this._queue.splice(index, 1);
+             return false;
+           } // for the case of transformations, we use the special retarget function
+           // which has access to the outer scope
+           if (this._history[method].caller.isTransform) {
+             this._history[method].caller.isTransform(target); // for everything else a simple morpher change is sufficient
+           } else {
+             this._history[method].morpher.to(target);
+           }
+           this._history[method].caller.finished = false;
+           var timeline$$1 = this.timeline();
+           timeline$$1 && timeline$$1._continue();
+           return true;
+         }
+         return false;
+       } // Run each initialise function in the runner if required
+     }, {
+       key: "_initialise",
+       value: function _initialise(running) {
+         // If we aren't running, we shouldn't initialise when not declarative
+         if (!running && !this._isDeclarative) return; // Loop through all of the initialisers
+         for (var i = 0, len = this._queue.length; i < len; ++i) {
+           // Get the current initialiser
+           var current = this._queue[i]; // Determine whether we need to initialise
+           var needsIt = this._isDeclarative || !current.initialised && running;
+           running = !current.finished; // Call the initialiser if we need to
+           if (needsIt && running) {
+             current.initialiser.call(this);
+             current.initialised = true;
+           }
+         }
+       } // Run each run function for the position or dt given
+     }, {
+       key: "_run",
+       value: function _run(positionOrDt) {
+         // Run all of the _queue directly
+         var allfinished = true;
+         for (var i = 0, len = this._queue.length; i < len; ++i) {
+           // Get the current function to run
+           var current = this._queue[i]; // Run the function if its not finished, we keep track of the finished
+           // flag for the sake of declarative _queue
+           var converged = current.runner.call(this, positionOrDt);
+           current.finished = current.finished || converged === true;
+           allfinished = allfinished && current.finished;
+         } // We report when all of the constructors are finished
+         return allfinished;
+       }
+     }, {
+       key: "addTransform",
+       value: function addTransform(transform, index) {
+         this.transforms.lmultiplyO(transform);
+         return this;
+       }
+     }, {
+       key: "clearTransform",
+       value: function clearTransform() {
+         this.transforms = new Matrix();
+         return this;
+       }
+     }], [{
+       key: "sanitise",
+       value: function sanitise(duration, delay, when) {
+         // Initialise the default parameters
+         var times = 1;
+         var swing = false;
+         var wait = 0;
+         duration = duration || timeline.duration;
+         delay = delay || timeline.delay;
+         when = when || 'last'; // If we have an object, unpack the values
+         if (_typeof(duration) === 'object' && !(duration instanceof Stepper)) {
+           delay = duration.delay || delay;
+           when = duration.when || when;
+           swing = duration.swing || swing;
+           times = duration.times || times;
+           wait = duration.wait || wait;
+           duration = duration.duration || timeline.duration;
+         }
+         return {
+           duration: duration,
+           delay: delay,
+           swing: swing,
+           times: times,
+           wait: wait,
+           when: when
+         };
+       }
+     }]);
+     return Runner;
+   }(EventTarget);
+   Runner.id = 0;
+   var FakeRunner = function FakeRunner() {
+     var transforms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Matrix();
+     var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
+     var done = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
+     _classCallCheck(this, FakeRunner);
+     this.transforms = transforms;
+     this.id = id;
+     this.done = done;
+   };
+   extend([Runner, FakeRunner], {
+     mergeWith: function mergeWith(runner) {
+       return new FakeRunner(runner.transforms.lmultiply(this.transforms), runner.id);
+     }
+   }); // FakeRunner.emptyRunner = new FakeRunner()
+   var lmultiply = function lmultiply(last, curr) {
+     return last.lmultiplyO(curr);
+   };
  
-       return SVG.Element.prototype.parent.call(this, type);
-     },
-     // Removes the doc from the DOM
-     remove: function remove() {
-       if (!this.isRoot()) {
-         return SVG.Element.prototype.remove.call(this);
-       }
+   var getRunnerTransform = function getRunnerTransform(runner) {
+     return runner.transforms;
+   };
  
-       if (this.parent()) {
-         this.parent().removeChild(this.node);
-       }
+   function mergeTransforms() {
+     // Find the matrix to apply to the element and apply it
+     var runners = this._transformationRunners.runners;
+     var netTransform = runners.map(getRunnerTransform).reduce(lmultiply, new Matrix());
+     this.transform(netTransform);
  
-       return this;
-     },
-     clear: function clear() {
-       // remove children
-       while (this.node.hasChildNodes()) {
-         this.node.removeChild(this.node.lastChild);
-       }
+     this._transformationRunners.merge();
  
-       return this;
-     }
-   },
-   construct: {
-     // Create nested svg document
-     nested: function nested() {
-       return this.put(new SVG.Doc());
-     }
-   }
- });
- SVG.Shape = SVG.invent({
-   // Initialize node
-   create: function create(node) {
-     SVG.Element.call(this, node);
-   },
-   // Inherit from
-   inherit: SVG.Element
- });
- SVG.Bare = SVG.invent({
-   // Initialize
-   create: function create(element, inherit) {
-     // construct element
-     SVG.Element.call(this, SVG.create(element)); // inherit custom methods
-     if (inherit) {
-       for (var method in inherit.prototype) {
-         if (typeof inherit.prototype[method] === 'function') {
-           this[method] = inherit.prototype[method];
-         }
-       }
-     }
-   },
-   // Inherit from
-   inherit: SVG.Element,
-   // Add methods
-   extend: {
-     // Insert some plain text
-     words: function words(text) {
-       // remove contents
-       while (this.node.hasChildNodes()) {
-         this.node.removeChild(this.node.lastChild);
-       } // create text node
-       this.node.appendChild(document.createTextNode(text));
-       return this;
-     }
-   }
- });
- SVG.extend(SVG.Parent, {
-   // Create an element that is not described by SVG.js
-   element: function element(_element, inherit) {
-     return this.put(new SVG.Bare(_element, inherit));
-   }
- });
- SVG.Symbol = SVG.invent({
-   // Initialize node
-   create: 'symbol',
-   // Inherit from
-   inherit: SVG.Container,
-   construct: {
-     // create symbol
-     symbol: function symbol() {
-       return this.put(new SVG.Symbol());
-     }
-   }
- });
- SVG.Use = SVG.invent({
-   // Initialize node
-   create: 'use',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add class methods
-   extend: {
-     // Use element as a reference
-     element: function element(_element2, file) {
-       // Set lined element
-       return this.attr('href', (file || '') + '#' + _element2, SVG.xlink);
-     }
-   },
-   // Add parent method
-   construct: {
-     // Create a use element
-     use: function use(element, file) {
-       return this.put(new SVG.Use()).element(element, file);
-     }
-   }
- });
- SVG.Rect = SVG.invent({
-   // Initialize node
-   create: 'rect',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add parent method
-   construct: {
-     // Create a rect element
-     rect: function rect(width, height) {
-       return this.put(new SVG.Rect()).size(width, height);
-     }
-   }
- });
- /* global proportionalSize */
- SVG.Circle = SVG.invent({
-   // Initialize node
-   create: 'circle',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add parent method
-   construct: {
-     // Create circle element, based on ellipse
-     circle: function circle(size) {
-       return this.put(new SVG.Circle()).rx(new SVG.Number(size).divide(2)).move(0, 0);
+     if (this._transformationRunners.length() === 1) {
+       this._frameId = null;
      }
    }
- });
- SVG.extend([SVG.Circle, SVG.Timeline], {
-   // Radius x value
-   rx: function rx(_rx) {
-     return this.attr('r', _rx);
-   },
-   // Alias radius x value
-   ry: function ry(_ry) {
-     return this.rx(_ry);
-   }
- });
- SVG.Ellipse = SVG.invent({
-   // Initialize node
-   create: 'ellipse',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add parent method
-   construct: {
-     // Create an ellipse
-     ellipse: function ellipse(width, height) {
-       return this.put(new SVG.Ellipse()).size(width, height).move(0, 0);
+   var RunnerArray =
+   /*#__PURE__*/
+   function () {
+     function RunnerArray() {
+       _classCallCheck(this, RunnerArray);
+       this.runners = [];
+       this.ids = [];
      }
-   }
- });
- SVG.extend([SVG.Ellipse, SVG.Rect, SVG.Timeline], {
-   // Radius x value
-   rx: function rx(_rx2) {
-     return this.attr('rx', _rx2);
-   },
-   // Radius y value
-   ry: function ry(_ry2) {
-     return this.attr('ry', _ry2);
-   }
- }); // Add common method
- SVG.extend([SVG.Circle, SVG.Ellipse], {
-   // Move over x-axis
-   x: function x(_x2) {
-     return _x2 == null ? this.cx() - this.rx() : this.cx(_x2 + this.rx());
-   },
-   // Move over y-axis
-   y: function y(_y2) {
-     return _y2 == null ? this.cy() - this.ry() : this.cy(_y2 + this.ry());
-   },
-   // Move by center over x-axis
-   cx: function cx(x) {
-     return x == null ? this.attr('cx') : this.attr('cx', x);
-   },
-   // Move by center over y-axis
-   cy: function cy(y) {
-     return y == null ? this.attr('cy') : this.attr('cy', y);
-   },
-   // Set width of element
-   width: function width(_width2) {
-     return _width2 == null ? this.rx() * 2 : this.rx(new SVG.Number(_width2).divide(2));
-   },
-   // Set height of element
-   height: function height(_height2) {
-     return _height2 == null ? this.ry() * 2 : this.ry(new SVG.Number(_height2).divide(2));
-   },
-   // Custom size function
-   size: function size(width, height) {
-     var p = proportionalSize(this, width, height);
-     return this.rx(new SVG.Number(p.width).divide(2)).ry(new SVG.Number(p.height).divide(2));
-   }
- });
- /* global proportionalSize */
- SVG.Line = SVG.invent({
-   // Initialize node
-   create: 'line',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add class methods
-   extend: {
-     // Get array
-     array: function array() {
-       return new SVG.PointArray([[this.attr('x1'), this.attr('y1')], [this.attr('x2'), this.attr('y2')]]);
-     },
-     // Overwrite native plot() method
-     plot: function plot(x1, y1, x2, y2) {
-       if (x1 == null) {
-         return this.array();
-       } else if (typeof y1 !== 'undefined') {
-         x1 = {
-           x1: x1,
-           y1: y1,
-           x2: x2,
-           y2: y2
-         };
-       } else {
-         x1 = new SVG.PointArray(x1).toLine();
+     _createClass(RunnerArray, [{
+       key: "add",
+       value: function add(runner) {
+         if (this.runners.includes(runner)) return;
+         var id = runner.id + 1;
+         var leftSibling = this.ids.reduce(function (last, curr) {
+           if (curr > last && curr < id) return curr;
+           return last;
+         }, 0);
+         var index = this.ids.indexOf(leftSibling) + 1;
+         this.ids.splice(index, 0, id);
+         this.runners.splice(index, 0, runner);
+         return this;
+       }
+     }, {
+       key: "getByID",
+       value: function getByID(id) {
+         return this.runners[this.ids.indexOf(id + 1)];
        }
+     }, {
+       key: "remove",
+       value: function remove(id) {
+         var index = this.ids.indexOf(id + 1);
+         this.ids.splice(index, 1);
+         this.runners.splice(index, 1);
+         return this;
+       }
+     }, {
+       key: "merge",
+       value: function merge() {
+         var _this2 = this;
  
-       return this.attr(x1);
-     },
-     // Move by left top corner
-     move: function move(x, y) {
-       return this.attr(this.array().move(x, y).toLine());
-     },
-     // Set element size to given width and height
-     size: function size(width, height) {
-       var p = proportionalSize(this, width, height);
-       return this.attr(this.array().size(p.width, p.height).toLine());
-     }
-   },
-   // Add parent method
-   construct: {
-     // Create a line element
-     line: function line(x1, y1, x2, y2) {
-       // make sure plot is called as a setter
-       // x1 is not necessarily a number, it can also be an array, a string and a SVG.PointArray
-       return SVG.Line.prototype.plot.apply(this.put(new SVG.Line()), x1 != null ? [x1, y1, x2, y2] : [0, 0, 0, 0]);
-     }
-   }
- });
- /* global proportionalSize */
- SVG.Polyline = SVG.invent({
-   // Initialize node
-   create: 'polyline',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add parent method
-   construct: {
-     // Create a wrapped polyline element
-     polyline: function polyline(p) {
-       // make sure plot is called as a setter
-       return this.put(new SVG.Polyline()).plot(p || new SVG.PointArray());
-     }
-   }
- });
- SVG.Polygon = SVG.invent({
-   // Initialize node
-   create: 'polygon',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add parent method
-   construct: {
-     // Create a wrapped polygon element
-     polygon: function polygon(p) {
-       // make sure plot is called as a setter
-       return this.put(new SVG.Polygon()).plot(p || new SVG.PointArray());
-     }
-   }
- }); // Add polygon-specific functions
- SVG.extend([SVG.Polyline, SVG.Polygon], {
-   // Get array
-   array: function array() {
-     return this._array || (this._array = new SVG.PointArray(this.attr('points')));
-   },
-   // Plot new path
-   plot: function plot(p) {
-     return p == null ? this.array() : this.clear().attr('points', typeof p === 'string' ? p : this._array = new SVG.PointArray(p));
-   },
-   // Clear array cache
-   clear: function clear() {
-     delete this._array;
-     return this;
-   },
-   // Move by left top corner
-   move: function move(x, y) {
-     return this.attr('points', this.array().move(x, y));
-   },
-   // Set element size to given width and height
-   size: function size(width, height) {
-     var p = proportionalSize(this, width, height);
-     return this.attr('points', this.array().size(p.width, p.height));
-   }
- }); // unify all point to point elements
- SVG.extend([SVG.Line, SVG.Polyline, SVG.Polygon], {
-   // Define morphable array
-   MorphArray: SVG.PointArray,
-   // Move by left top corner over x-axis
-   x: function x(_x3) {
-     return _x3 == null ? this.bbox().x : this.move(_x3, this.bbox().y);
-   },
-   // Move by left top corner over y-axis
-   y: function y(_y3) {
-     return _y3 == null ? this.bbox().y : this.move(this.bbox().x, _y3);
-   },
-   // Set width of element
-   width: function width(_width3) {
-     var b = this.bbox();
-     return _width3 == null ? b.width : this.size(_width3, b.height);
-   },
-   // Set height of element
-   height: function height(_height3) {
-     var b = this.bbox();
-     return _height3 == null ? b.height : this.size(b.width, _height3);
-   }
- });
- /* global proportionalSize */
- SVG.Path = SVG.invent({
-   // Initialize node
-   create: 'path',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add class methods
-   extend: {
-     // Define morphable array
-     MorphArray: SVG.PathArray,
-     // Get array
-     array: function array() {
-       return this._array || (this._array = new SVG.PathArray(this.attr('d')));
-     },
-     // Plot new path
-     plot: function plot(d) {
-       return d == null ? this.array() : this.clear().attr('d', typeof d === 'string' ? d : this._array = new SVG.PathArray(d));
-     },
-     // Clear array cache
-     clear: function clear() {
-       delete this._array;
-       return this;
-     },
-     // Move by left top corner
-     move: function move(x, y) {
-       return this.attr('d', this.array().move(x, y));
-     },
-     // Move by left top corner over x-axis
-     x: function x(_x4) {
-       return _x4 == null ? this.bbox().x : this.move(_x4, this.bbox().y);
-     },
-     // Move by left top corner over y-axis
-     y: function y(_y4) {
-       return _y4 == null ? this.bbox().y : this.move(this.bbox().x, _y4);
-     },
-     // Set element size to given width and height
-     size: function size(width, height) {
-       var p = proportionalSize(this, width, height);
-       return this.attr('d', this.array().size(p.width, p.height));
-     },
-     // Set width of element
-     width: function width(_width4) {
-       return _width4 == null ? this.bbox().width : this.size(_width4, this.bbox().height);
-     },
-     // Set height of element
-     height: function height(_height4) {
-       return _height4 == null ? this.bbox().height : this.size(this.bbox().width, _height4);
-     }
-   },
-   // Add parent method
-   construct: {
-     // Create a wrapped path element
-     path: function path(d) {
-       // make sure plot is called as a setter
-       return this.put(new SVG.Path()).plot(d || new SVG.PathArray());
-     }
-   }
- });
- SVG.Image = SVG.invent({
-   // Initialize node
-   create: 'image',
-   // Inherit from
-   inherit: SVG.Shape,
-   // Add class methods
-   extend: {
-     // (re)load image
-     load: function load(url, callback) {
-       if (!url) return this;
-       var img = new window.Image();
-       SVG.on(img, 'load', function (e) {
-         var p = this.parent(SVG.Pattern); // ensure image size
-         if (this.width() === 0 && this.height() === 0) {
-           this.size(img.width, img.height);
-         }
+         var lastRunner = null;
+         this.runners.forEach(function (runner, i) {
+           if (lastRunner && runner.done && lastRunner.done) {
+             _this2.remove(runner.id);
  
-         if (p instanceof SVG.Pattern) {
-           // ensure pattern size if not set
-           if (p.width() === 0 && p.height() === 0) {
-             p.size(this.width(), this.height());
+             _this2.edit(lastRunner.id, runner.mergeWith(lastRunner));
            }
-         }
  
-         if (typeof callback === 'function') {
-           callback.call(this, {
-             width: img.width,
-             height: img.height,
-             ratio: img.width / img.height,
-             url: url
-           });
+           lastRunner = runner;
+         });
+         return this;
+       }
+     }, {
+       key: "edit",
+       value: function edit(id, newRunner) {
+         var index = this.ids.indexOf(id + 1);
+         this.ids.splice(index, 1, id);
+         this.runners.splice(index, 1, newRunner);
+         return this;
+       }
+     }, {
+       key: "length",
+       value: function length() {
+         return this.ids.length;
+       }
+     }, {
+       key: "clearBefore",
+       value: function clearBefore(id) {
+         var deleteCnt = this.ids.indexOf(id + 1) || 1;
+         this.ids.splice(0, deleteCnt, 0);
+         this.runners.splice(0, deleteCnt, new FakeRunner());
+         return this;
+       }
+     }]);
+     return RunnerArray;
+   }();
+   var frameId = 0;
+   registerMethods({
+     Element: {
+       animate: function animate(duration, delay, when) {
+         var o = Runner.sanitise(duration, delay, when);
+         var timeline$$1 = this.timeline();
+         return new Runner(o.duration).loop(o).element(this).timeline(timeline$$1).schedule(delay, when);
+       },
+       delay: function delay(by, when) {
+         return this.animate(0, by, when);
+       },
+       // this function searches for all runners on the element and deletes the ones
+       // which run before the current one. This is because absolute transformations
+       // overwfrite anything anyway so there is no need to waste time computing
+       // other runners
+       _clearTransformRunnersBefore: function _clearTransformRunnersBefore(currentRunner) {
+         this._transformationRunners.clearBefore(currentRunner.id);
+       },
+       _currentTransform: function _currentTransform(current) {
+         return this._transformationRunners.runners // we need the equal sign here to make sure, that also transformations
+         // on the same runner which execute before the current transformation are
+         // taken into account
+         .filter(function (runner) {
+           return runner.id <= current.id;
+         }).map(getRunnerTransform).reduce(lmultiply, new Matrix());
+       },
+       addRunner: function addRunner(runner) {
+         this._transformationRunners.add(runner);
+         Animator.transform_frame(mergeTransforms.bind(this), this._frameId);
+       },
+       _prepareRunner: function _prepareRunner() {
+         if (this._frameId == null) {
+           this._transformationRunners = new RunnerArray().add(new FakeRunner(new Matrix(this)));
+           this._frameId = frameId++;
          }
-       }, this);
-       SVG.on(img, 'load error', function () {
-         // dont forget to unbind memory leaking events
-         SVG.off(img);
-       });
-       return this.attr('href', img.src = url, SVG.xlink);
-     }
-   },
-   // Add parent method
-   construct: {
-     // create image element, load image and set its size
-     image: function image(source, callback) {
-       return this.put(new SVG.Image()).size(0, 0).load(source, callback);
+       }
      }
-   }
- });
- SVG.Text = SVG.invent({
-   // Initialize node
-   create: function create(node) {
-     SVG.Element.call(this, node || SVG.create('text'));
-     this.dom.leading = new SVG.Number(1.3); // store leading value for rebuilding
-     this._rebuild = true; // enable automatic updating of dy values
-     this._build = false; // disable build mode for adding multiple lines
-     // set default font
-     this.attr('font-family', SVG.defaults.attrs['font-family']);
-   },
-   // Inherit from
-   inherit: SVG.Parent,
-   // Add class methods
-   extend: {
-     // Move over x-axis
-     x: function x(_x5) {
-       // act as getter
-       if (_x5 == null) {
-         return this.attr('x');
-       }
-       return this.attr('x', _x5);
-     },
-     // Move over y-axis
-     y: function y(_y5) {
-       var oy = this.attr('y');
-       var o = typeof oy === 'number' ? oy - this.bbox().y : 0; // act as getter
-       if (_y5 == null) {
-         return typeof oy === 'number' ? oy - o : oy;
+   });
+   extend(Runner, {
+     attr: function attr(a, v) {
+       return this.styleAttr('attr', a, v);
+     },
+     // Add animatable styles
+     css: function css(s, v) {
+       return this.styleAttr('css', s, v);
+     },
+     styleAttr: function styleAttr(type, name, val) {
+       // apply attributes individually
+       if (_typeof(name) === 'object') {
+         for (var key in val) {
+           this.styleAttr(type, key, val[key]);
+         }
        }
  
-       return this.attr('y', typeof _y5 === 'number' ? _y5 + o : _y5);
-     },
-     // Move center over x-axis
-     cx: function cx(x) {
-       return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2);
+       var morpher = new Morphable(this._stepper).to(val);
+       this.queue(function () {
+         morpher = morpher.from(this.element()[type](name));
+       }, function (pos) {
+         this.element()[type](name, morpher.at(pos));
+         return morpher.done();
+       });
+       return this;
      },
-     // Move center over y-axis
-     cy: function cy(y) {
-       return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2);
+     zoom: function zoom(level, point) {
+       var morpher = new Morphable(this._stepper).to(new SVGNumber(level));
+       this.queue(function () {
+         morpher = morpher.from(this.zoom());
+       }, function (pos) {
+         this.element().zoom(morpher.at(pos), point);
+         return morpher.done();
+       });
+       return this;
      },
-     // Set the text content
-     text: function text(_text) {
-       // act as getter
-       if (_text === undefined) {
-         var children = this.node.childNodes;
-         var firstLine = 0;
-         _text = '';
-         for (var i = 0, len = children.length; i < len; ++i) {
-           // skip textPaths - they are no lines
-           if (children[i].nodeName === 'textPath') {
-             if (i === 0) firstLine = 1;
-             continue;
-           } // add newline if its not the first child and newLined is set to true
  
 -    /**\r
 -     ** absolute transformations\r
++    /**
++     ** absolute transformations
+      **/
+     //
+     // M v -----|-----(D M v = F v)------|----->  T v
+     //
+     // 1. define the final state (T) and decompose it (once)
+     //    t = [tx, ty, the, lam, sy, sx]
+     // 2. on every frame: pull the current state of all previous transforms
+     //    (M - m can change)
+     //   and then write this as m = [tx0, ty0, the0, lam0, sy0, sx0]
+     // 3. Find the interpolated matrix F(pos) = m + pos * (t - m)
+     //   - Note F(0) = M
+     //   - Note F(1) = T
+     // 4. Now you get the delta matrix as a result: D = F * inv(M)
+     transform: function transform(transforms, relative, affine) {
+       // If we have a declarative function, we should retarget it if possible
+       relative = transforms.relative || relative;
+       if (this._isDeclarative && !relative && this._tryRetarget('transform', transforms)) {
+         return this;
+       } // Parse the parameters
+       var isMatrix = Matrix.isMatrixLike(transforms);
+       affine = transforms.affine != null ? transforms.affine : affine != null ? affine : !isMatrix; // Create a morepher and set its type
+       var morpher = new Morphable().type(affine ? TransformBag : Matrix).stepper(this._stepper);
+       var origin;
+       var element;
+       var current;
+       var currentAngle;
+       var startTransform;
+       function setup() {
+         // make sure element and origin is defined
+         element = element || this.element();
+         origin = origin || getOrigin(transforms, element);
+         startTransform = new Matrix(relative ? undefined : element); // add the runner to the element so it can merge transformations
+         element.addRunner(this); // Deactivate all transforms that have run so far if we are absolute
+         if (!relative) {
+           element._clearTransformRunnersBefore(this);
+         }
+       }
  
-           if (i !== firstLine && children[i].nodeType !== 3 && SVG.adopt(children[i]).dom.newLined === true) {
-             _text += '\n';
-           } // add content of this node
+       function run(pos) {
+         // clear all other transforms before this in case something is saved
+         // on this runner. We are absolute. We dont need these!
+         if (!relative) this.clearTransform();
  
+         var _transform = new Point(origin).transform(element._currentTransform(this)),
+             x = _transform.x,
+             y = _transform.y;
  
-           _text += children[i].textContent;
-         }
+         var target = new Matrix(_objectSpread({}, transforms, {
+           origin: [x, y]
+         }));
+         var start = this._isDeclarative && current ? current : startTransform;
  
-         return _text;
-       } // remove existing content
+         if (affine) {
+           target = target.decompose(x, y);
+           start = start.decompose(x, y); // Get the current and target angle as it was set
  
+           var rTarget = target.rotate;
+           var rCurrent = start.rotate; // Figure out the shortest path to rotate directly
  
-       this.clear().build(true);
+           var possibilities = [rTarget - 360, rTarget, rTarget + 360];
+           var distances = possibilities.map(function (a) {
+             return Math.abs(a - rCurrent);
+           });
+           var shortest = Math.min.apply(Math, _toConsumableArray(distances));
+           var index = distances.indexOf(shortest);
+           target.rotate = possibilities[index];
+         }
  
-       if (typeof _text === 'function') {
-         // call block
-         _text.call(this, this);
-       } else {
-         // store text and make sure text is not blank
-         _text = _text.split('\n'); // build new lines
+         if (relative) {
+           // we have to be careful here not to overwrite the rotation
+           // with the rotate method of Matrix
+           if (!isMatrix) {
+             target.rotate = transforms.rotate || 0;
+           }
  
-         for (var j = 0, jl = _text.length; j < jl; j++) {
-           this.tspan(_text[j]).newLine();
+           if (this._isDeclarative && currentAngle) {
+             start.rotate = currentAngle;
+           }
          }
-       } // disable build mode and rebuild lines
  
+         morpher.from(start);
+         morpher.to(target);
+         var affineParameters = morpher.at(pos);
+         currentAngle = affineParameters.rotate;
+         current = new Matrix(affineParameters);
+         this.addTransform(current);
+         return morpher.done();
+       }
  
-       return this.build(false).rebuild();
-     },
-     // Set / get leading
-     leading: function leading(value) {
-       // act as getter
-       if (value == null) {
-         return this.dom.leading;
-       } // act as setter
+       function retarget(newTransforms) {
+         // only get a new origin if it changed since the last call
+         if ((newTransforms.origin || 'center').toString() !== (transforms.origin || 'center').toString()) {
+           origin = getOrigin(transforms, element);
+         } // overwrite the old transformations with the new ones
  
-       this.dom.leading = new SVG.Number(value);
-       return this.rebuild();
-     },
-     // Rebuild appearance type
-     rebuild: function rebuild(_rebuild) {
-       // store new rebuild flag if given
-       if (typeof _rebuild === 'boolean') {
-         this._rebuild = _rebuild;
-       } // define position of all lines
-       if (this._rebuild) {
-         var self = this;
-         var blankLineOffset = 0;
-         var dy = this.dom.leading * new SVG.Number(this.attr('font-size'));
-         this.each(function () {
-           if (this.dom.newLined) {
-             this.attr('x', self.attr('x'));
  
-             if (this.text() === '\n') {
-               blankLineOffset += dy;
-             } else {
-               this.attr('dy', dy + blankLineOffset);
-               blankLineOffset = 0;
-             }
-           }
+         transforms = _objectSpread({}, newTransforms, {
+           origin: origin
          });
-         this.fire('rebuild');
        }
  
+       this.queue(setup, run, retarget);
+       this._isDeclarative && this._rememberMorpher('transform', morpher);
        return this;
      },
-     // Enable / disable build mode
-     build: function build(_build) {
-       this._build = !!_build;
-       return this;
-     },
-     // overwrite method from parent to set data properly
-     setData: function setData(o) {
-       this.dom = o;
-       this.dom.leading = new SVG.Number(o.leading || 1.3);
-       return this;
-     }
-   },
-   // Add parent method
-   construct: {
-     // Create text element
-     text: function text(_text2) {
-       return this.put(new SVG.Text()).text(_text2);
+     // Animatable x-axis
+     x: function x(_x, relative) {
+       return this._queueNumber('x', _x);
      },
-     // Create plain text element
-     plain: function plain(text) {
-       return this.put(new SVG.Text()).plain(text);
-     }
-   }
- });
- SVG.Tspan = SVG.invent({
-   // Initialize node
-   create: 'tspan',
-   // Inherit from
-   inherit: SVG.Parent,
-   // Add class methods
-   extend: {
-     // Set text content
-     text: function text(_text3) {
-       if (_text3 == null) return this.node.textContent + (this.dom.newLined ? '\n' : '');
-       typeof _text3 === 'function' ? _text3.call(this, this) : this.plain(_text3);
-       return this;
+     // Animatable y-axis
+     y: function y(_y) {
+       return this._queueNumber('y', _y);
      },
-     // Shortcut dx
-     dx: function dx(_dx) {
-       return this.attr('dx', _dx);
+     dx: function dx(x) {
+       return this._queueNumberDelta('dx', x);
      },
-     // Shortcut dy
-     dy: function dy(_dy) {
-       return this.attr('dy', _dy);
+     dy: function dy(y) {
+       return this._queueNumberDelta('dy', y);
      },
-     // Create new line
-     newLine: function newLine() {
-       // fetch text parent
-       var t = this.parent(SVG.Text); // mark new line
+     _queueNumberDelta: function _queueNumberDelta(method, to) {
+       to = new SVGNumber(to); // Try to change the target if we have this method already registerd
  
-       this.dom.newLined = true; // apply new position
+       if (this._tryRetargetDelta(method, to)) return this; // Make a morpher and queue the animation
  
-       return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x());
-     }
-   }
- });
- SVG.extend([SVG.Text, SVG.Tspan], {
-   // Create plain text node
-   plain: function plain(text) {
-     // clear if build mode is disabled
-     if (this._build === false) {
-       this.clear();
-     } // create text node
+       var morpher = new Morphable(this._stepper).to(to);
+       this.queue(function () {
+         var from = this.element()[method]();
+         morpher.from(from);
+         morpher.to(from + to);
+       }, function (pos) {
+         this.element()[method](morpher.at(pos));
+         return morpher.done();
+       }); // Register the morpher so that if it is changed again, we can retarget it
  
+       this._rememberMorpher(method, morpher);
  
-     this.node.appendChild(document.createTextNode(text));
-     return this;
-   },
-   // Create a tspan
-   tspan: function tspan(text) {
-     var tspan = new SVG.Tspan(); // clear if build mode is disabled
+       return this;
+     },
+     _queueObject: function _queueObject(method, to) {
+       // Try to change the target if we have this method already registerd
+       if (this._tryRetarget(method, to)) return this; // Make a morpher and queue the animation
  
-     if (!this._build) {
-       this.clear();
-     } // add new tspan
+       var morpher = new Morphable(this._stepper).to(to);
+       this.queue(function () {
+         morpher.from(this.element()[method]());
+       }, function (pos) {
+         this.element()[method](morpher.at(pos));
+         return morpher.done();
+       }); // Register the morpher so that if it is changed again, we can retarget it
  
+       this._rememberMorpher(method, morpher);
  
-     this.node.appendChild(tspan.node);
-     return tspan.text(text);
-   },
-   // FIXME: Does this also work for textpath?
-   // Get length of text element
-   length: function length() {
-     return this.node.getComputedTextLength();
-   }
- });
- SVG.TextPath = SVG.invent({
-   // Initialize node
-   create: 'textPath',
-   // Inherit from
-   inherit: SVG.Text,
-   // Define parent class
-   parent: SVG.Parent,
-   // Add parent method
-   extend: {
-     MorphArray: SVG.PathArray,
-     // return the array of the path track element
-     array: function array() {
-       var track = this.track();
-       return track ? track.array() : null;
+       return this;
+     },
+     _queueNumber: function _queueNumber(method, value) {
+       return this._queueObject(method, new SVGNumber(value));
+     },
+     // Animatable center x-axis
+     cx: function cx(x) {
+       return this._queueNumber('cx', x);
      },
-     // Plot path if any
-     plot: function plot(d) {
-       var track = this.track();
-       var pathArray = null;
+     // Animatable center y-axis
+     cy: function cy(y) {
+       return this._queueNumber('cy', y);
+     },
+     // Add animatable move
+     move: function move(x, y) {
+       return this.x(x).y(y);
+     },
+     // Add animatable center
+     center: function center(x, y) {
+       return this.cx(x).cy(y);
+     },
+     // Add animatable size
+     size: function size(width, height) {
+       // animate bbox based size for all other elements
+       var box;
  
-       if (track) {
-         pathArray = track.plot(d);
+       if (!width || !height) {
+         box = this._element.bbox();
        }
  
-       return d == null ? pathArray : this;
-     },
-     // Get the path element
-     track: function track() {
-       return this.reference('href');
-     }
-   },
-   construct: {
-     textPath: function textPath(text, path) {
-       return this.defs().path(path).text(text).addTo(this);
-     }
-   }
- });
- SVG.extend([SVG.Text], {
-   // Create path for text to run on
-   path: function path(track) {
-     var path = new SVG.TextPath(); // if d is a path, reuse it
-     if (!(track instanceof SVG.Path)) {
-       // create path element
-       track = this.doc().defs().path(track);
-     } // link textPath to path and add content
-     path.attr('href', '#' + track, SVG.xlink); // add textPath element as child node and return textPath
-     return this.put(path);
-   },
-   // Todo: make this plural?
-   // Get the textPath children
-   textPath: function textPath() {
-     return this.select('textPath');
-   }
- });
- SVG.extend([SVG.Path], {
-   // creates a textPath from this path
-   text: function text(_text4) {
-     if (_text4 instanceof SVG.Text) {
-       var txt = _text4.text();
-       return _text4.clear().path(this).text(txt);
-     }
+       if (!width) {
+         width = box.width / box.height * height;
+       }
  
-     return this.parent().put(new SVG.Text()).path(this).text(_text4);
-   } // TODO: Maybe add `targets` to get all textPaths associated with this path
- });
- SVG.A = SVG.invent({
-   // Initialize node
-   create: 'a',
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {
-     // Link url
-     to: function to(url) {
-       return this.attr('href', url, SVG.xlink);
-     },
-     // Link target attribute
-     target: function target(_target) {
-       return this.attr('target', _target);
-     }
-   },
-   // Add parent method
-   construct: {
-     // Create a hyperlink element
-     link: function link(url) {
-       return this.put(new SVG.A()).to(url);
-     }
-   }
- });
- SVG.extend(SVG.Element, {
-   // Create a hyperlink element
-   linkTo: function linkTo(url) {
-     var link = new SVG.A();
-     if (typeof url === 'function') {
-       url.call(link, link);
-     } else {
-       link.to(url);
-     }
+       if (!height) {
+         height = box.height / box.width * width;
+       }
  
-     return this.parent().put(link).put(this);
-   }
- });
- SVG.Marker = SVG.invent({
-   // Initialize node
-   create: 'marker',
-   // Inherit from
-   inherit: SVG.Container,
-   // Add class methods
-   extend: {
-     // Set width of element
-     width: function width(_width5) {
-       return this.attr('markerWidth', _width5);
+       return this.width(width).height(height);
+     },
+     // Add animatable width
+     width: function width(_width) {
+       return this._queueNumber('width', _width);
      },
-     // Set height of element
-     height: function height(_height5) {
-       return this.attr('markerHeight', _height5);
+     // Add animatable height
+     height: function height(_height) {
+       return this._queueNumber('height', _height);
+     },
+     // Add animatable plot
+     plot: function plot(a, b, c, d) {
+       // Lines can be plotted with 4 arguments
+       if (arguments.length === 4) {
+         return this.plot([a, b, c, d]);
+       } // FIXME: this needs to be rewritten such that the element is only accesed
+       // in the init function
+       return this._queueObject('plot', new this._element.MorphArray(a));
 -      /*\r
 -      var morpher = this._element.morphArray().to(a)\r
 -        this.queue(function () {\r
 -        morpher.from(this._element.array())\r
 -      }, function (pos) {\r
 -        this._element.plot(morpher.at(pos))\r
 -      })\r
 -        return this\r
++      /*
++      var morpher = this._element.morphArray().to(a)
++        this.queue(function () {
++        morpher.from(this._element.array())
++      }, function (pos) {
++        this._element.plot(morpher.at(pos))
++      })
++        return this
+       */
+     },
+     // Add leading method
+     leading: function leading(value) {
+       return this._queueNumber('leading', value);
      },
-     // Set marker refX and refY
-     ref: function ref(x, y) {
-       return this.attr('refX', x).attr('refY', y);
+     // Add animatable viewbox
+     viewbox: function viewbox(x, y, width, height) {
+       return this._queueObject('viewbox', new Box(x, y, width, height));
      },
-     // Update marker
-     update: function update(block) {
-       // remove all content
-       this.clear(); // invoke passed block
-       if (typeof block === 'function') {
-         block.call(this, this);
+     update: function update(o) {
+       if (_typeof(o) !== 'object') {
+         return this.update({
+           offset: arguments[0],
+           color: arguments[1],
+           opacity: arguments[2]
+         });
        }
  
+       if (o.opacity != null) this.attr('stop-opacity', o.opacity);
+       if (o.color != null) this.attr('stop-color', o.color);
+       if (o.offset != null) this.attr('offset', o.offset);
        return this;
-     },
-     // Return the fill id
-     toString: function toString() {
-       return 'url(#' + this.id() + ')';
-     }
-   },
-   // Add parent method
-   construct: {
-     marker: function marker(width, height, block) {
-       // Create marker element in defs
-       return this.defs().marker(width, height, block);
      }
-   }
- });
- SVG.extend(SVG.Defs, {
-   // Create marker
-   marker: function marker(width, height, block) {
-     // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
-     return this.put(new SVG.Marker()).size(width, height).ref(width / 2, height / 2).viewbox(0, 0, width, height).attr('orient', 'auto').update(block);
-   }
- });
- SVG.extend([SVG.Line, SVG.Polyline, SVG.Polygon, SVG.Path], {
-   // Create and attach markers
-   marker: function marker(_marker, width, height, block) {
-     var attr = ['marker']; // Build attribute name
+   });
  
-     if (_marker !== 'all') attr.push(_marker);
-     attr = attr.join('-'); // Set marker attribute
+   var sugar = {
+     stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],
+     fill: ['color', 'opacity', 'rule'],
+     prefix: function prefix(t, a) {
+       return a === 'color' ? t : t + '-' + a;
+     } // Add sugar for fill and stroke
  
-     _marker = arguments[1] instanceof SVG.Marker ? arguments[1] : this.doc().marker(width, height, block);
-     return this.attr(attr, _marker);
-   }
- }); // Define list of available attributes for stroke and fill
- var sugar = {
-   stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],
-   fill: ['color', 'opacity', 'rule'],
-   prefix: function prefix(t, a) {
-     return a === 'color' ? t : t + '-' + a;
-   } // Add sugar for fill and stroke
- };
- ['fill', 'stroke'].forEach(function (m) {
-   var extension = {};
-   var i;
-   extension[m] = function (o) {
-     if (typeof o === 'undefined') {
-       return this;
-     }
+   };
+   ['fill', 'stroke'].forEach(function (m) {
+     var extension = {};
+     var i;
  
-     if (typeof o === 'string' || SVG.Color.isRgb(o) || o && typeof o.fill === 'function') {
-       this.attr(m, o);
-     } else {
-       // set all attributes from sugar.fill and sugar.stroke list
-       for (i = sugar[m].length - 1; i >= 0; i--) {
-         if (o[sugar[m][i]] != null) {
-           this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);
+     extension[m] = function (o) {
+       if (typeof o === 'undefined') {
+         return this;
+       }
+       if (typeof o === 'string' || Color.isRgb(o) || o instanceof Element) {
+         this.attr(m, o);
+       } else {
+         // set all attributes from sugar.fill and sugar.stroke list
+         for (i = sugar[m].length - 1; i >= 0; i--) {
+           if (o[sugar[m][i]] != null) {
+             this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);
+           }
          }
        }
-     }
  
-     return this;
-   };
+       return this;
+     };
  
-   SVG.extend([SVG.Element, SVG.Timeline], extension);
- });
- SVG.extend([SVG.Element, SVG.Timeline], {
-   // Let the user set the matrix directly
-   matrix: function matrix(mat, b, c, d, e, f) {
-     // Act as a getter
-     if (mat == null) {
-       return new SVG.Matrix(this);
-     } // Act as a setter, the user can pass a matrix or a set of numbers
-     return this.attr('transform', new SVG.Matrix(mat, b, c, d, e, f));
-   },
-   // Map rotation to transform
-   rotate: function rotate(angle, cx, cy) {
-     return this.transform({
-       rotate: angle,
-       ox: cx,
-       oy: cy
-     }, true);
-   },
-   // Map skew to transform
-   skew: function skew(x, y, cx, cy) {
-     return arguments.length === 1 || arguments.length === 3 ? this.transform({
-       skew: x,
-       ox: y,
-       oy: cx
-     }, true) : this.transform({
-       skew: [x, y],
-       ox: cx,
-       oy: cy
-     }, true);
-   },
-   shear: function shear(lam, cx, cy) {
-     return this.transform({
-       shear: lam,
-       ox: cx,
-       oy: cy
-     }, true);
-   },
-   // Map scale to transform
-   scale: function scale(x, y, cx, cy) {
-     return arguments.length === 1 || arguments.length === 3 ? this.transform({
-       scale: x,
-       ox: y,
-       oy: cx
-     }, true) : this.transform({
-       scale: [x, y],
-       ox: cx,
-       oy: cy
-     }, true);
-   },
-   // Map translate to transform
-   translate: function translate(x, y) {
-     return this.transform({
-       translate: [x, y]
-     }, true);
-   },
-   // Map relative translations to transform
-   relative: function relative(x, y) {
-     return this.transform({
-       relative: [x, y]
-     }, true);
-   },
-   // Map flip to transform
-   flip: function flip(direction, around) {
-     var directionString = typeof direction === 'string' ? direction : isFinite(direction) ? 'both' : 'both';
-     var origin = direction === 'both' && isFinite(around) ? [around, around] : direction === 'x' ? [around, 0] : direction === 'y' ? [0, around] : isFinite(direction) ? [direction, direction] : [0, 0];
-     this.transform({
-       flip: directionString,
-       origin: origin
-     }, true);
-   },
-   // Opacity
-   opacity: function opacity(value) {
-     return this.attr('opacity', value);
-   },
-   // Relative move over x axis
-   dx: function dx(x) {
-     return this.x(new SVG.Number(x).plus(this instanceof SVG.Timeline ? 0 : this.x()), true);
-   },
-   // Relative move over y axis
-   dy: function dy(y) {
-     return this.y(new SVG.Number(y).plus(this instanceof SVG.Timeline ? 0 : this.y()), true);
-   },
-   // Relative move over x and y axes
-   dmove: function dmove(x, y) {
-     return this.dx(x).dy(y);
-   }
- });
- SVG.extend([SVG.Rect, SVG.Ellipse, SVG.Circle, SVG.Gradient, SVG.Timeline], {
-   // Add x and y radius
-   radius: function radius(x, y) {
-     var type = (this._target || this).type;
-     return type === 'radialGradient' || type === 'radialGradient' ? this.attr('r', new SVG.Number(x)) : this.rx(x).ry(y == null ? x : y);
-   }
- });
- SVG.extend(SVG.Path, {
-   // Get path length
-   length: function length() {
-     return this.node.getTotalLength();
-   },
-   // Get point at length
-   pointAt: function pointAt(length) {
-     return new SVG.Point(this.node.getPointAtLength(length));
-   }
- });
- SVG.extend([SVG.Parent, SVG.Text, SVG.Tspan, SVG.Timeline], {
-   // Set font
-   font: function font(a, v) {
-     if (_typeof(a) === 'object') {
-       for (v in a) {
-         this.font(v, a[v]);
-       }
+     registerMethods(['Shape', 'Runner'], extension);
+   });
+   registerMethods(['Element', 'Runner'], {
+     // Let the user set the matrix directly
+     matrix: function matrix(mat, b, c, d, e, f) {
+       // Act as a getter
+       if (mat == null) {
+         return new Matrix(this);
+       } // Act as a setter, the user can pass a matrix or a set of numbers
+       return this.attr('transform', new Matrix(mat, b, c, d, e, f));
+     },
+     // Map rotation to transform
+     rotate: function rotate(angle, cx, cy) {
+       return this.transform({
+         rotate: angle,
+         ox: cx,
+         oy: cy
+       }, true);
+     },
+     // Map skew to transform
+     skew: function skew(x, y, cx, cy) {
+       return arguments.length === 1 || arguments.length === 3 ? this.transform({
+         skew: x,
+         ox: y,
+         oy: cx
+       }, true) : this.transform({
+         skew: [x, y],
+         ox: cx,
+         oy: cy
+       }, true);
+     },
+     shear: function shear(lam, cx, cy) {
+       return this.transform({
+         shear: lam,
+         ox: cx,
+         oy: cy
+       }, true);
+     },
+     // Map scale to transform
+     scale: function scale(x, y, cx, cy) {
+       return arguments.length === 1 || arguments.length === 3 ? this.transform({
+         scale: x,
+         ox: y,
+         oy: cx
+       }, true) : this.transform({
+         scale: [x, y],
+         ox: cx,
+         oy: cy
+       }, true);
+     },
+     // Map translate to transform
+     translate: function translate(x, y) {
+       return this.transform({
+         translate: [x, y]
+       }, true);
+     },
+     // Map relative translations to transform
+     relative: function relative(x, y) {
+       return this.transform({
+         relative: [x, y]
+       }, true);
+     },
+     // Map flip to transform
+     flip: function flip(direction, around) {
+       var directionString = typeof direction === 'string' ? direction : isFinite(direction) ? 'both' : 'both';
+       var origin = direction === 'both' && isFinite(around) ? [around, around] : direction === 'x' ? [around, 0] : direction === 'y' ? [0, around] : isFinite(direction) ? [direction, direction] : [0, 0];
+       this.transform({
+         flip: directionString,
+         origin: origin
+       }, true);
+     },
+     // Opacity
+     opacity: function opacity(value) {
+       return this.attr('opacity', value);
+     },
+     // Relative move over x axis
+     dx: function dx(x) {
+       return this.x(new SVGNumber(x).plus(this instanceof Runner ? 0 : this.x()), true);
+     },
+     // Relative move over y axis
+     dy: function dy(y) {
+       return this.y(new SVGNumber(y).plus(this instanceof Runner ? 0 : this.y()), true);
+     },
+     // Relative move over x and y axes
+     dmove: function dmove(x, y) {
+       return this.dx(x).dy(y);
      }
-     return a === 'leading' ? this.leading(v) : a === 'anchor' ? this.attr('text-anchor', v) : a === 'size' || a === 'family' || a === 'weight' || a === 'stretch' || a === 'variant' || a === 'style' ? this.attr('font-' + a, v) : this.attr(a, v);
-   }
- });
- SVG.extend(SVG.Element, {
-   // Store data values on svg nodes
-   data: function data(a, v, r) {
-     if (_typeof(a) === 'object') {
-       for (v in a) {
-         this.data(v, a[v]);
-       }
-     } else if (arguments.length < 2) {
-       try {
-         return JSON.parse(this.attr('data-' + a));
-       } catch (e) {
-         return this.attr('data-' + a);
-       }
-     } else {
-       this.attr('data-' + a, v === null ? null : r === true || typeof v === 'string' || typeof v === 'number' ? v : JSON.stringify(v));
+   });
+   registerMethods('radius', {
+     // Add x and y radius
+     radius: function radius(x, y) {
+       var type = (this._element || this).type;
+       return type === 'radialGradient' || type === 'radialGradient' ? this.attr('r', new SVGNumber(x)) : this.rx(x).ry(y == null ? x : y);
      }
-     return this;
-   }
- });
- SVG.extend(SVG.Element, {
-   // Remember arbitrary data
-   remember: function remember(k, v) {
-     // remember every item in an object individually
-     if (_typeof(arguments[0]) === 'object') {
-       for (var key in k) {
-         this.remember(key, k[key]);
+   });
+   registerMethods('Path', {
+     // Get path length
+     length: function length() {
+       return this.node.getTotalLength();
+     },
+     // Get point at length
+     pointAt: function pointAt(length) {
+       return new Point(this.node.getPointAtLength(length));
+     }
+   });
+   registerMethods(['Element', 'Runner'], {
+     // Set font
+     font: function font(a, v) {
+       if (_typeof(a) === 'object') {
+         for (v in a) {
+           this.font(v, a[v]);
+         }
        }
-     } else if (arguments.length === 1) {
-       // retrieve memory
-       return this.memory()[k];
-     } else {
-       // store memory
-       this.memory()[k] = v;
+       return a === 'leading' ? this.leading(v) : a === 'anchor' ? this.attr('text-anchor', v) : a === 'size' || a === 'family' || a === 'weight' || a === 'stretch' || a === 'variant' || a === 'style' ? this.attr('font-' + a, v) : this.attr(a, v);
      }
+   });
  
-     return this;
-   },
-   // Erase a given memory
-   forget: function forget() {
-     if (arguments.length === 0) {
-       this._memory = {};
-     } else {
-       for (var i = arguments.length - 1; i >= 0; i--) {
-         delete this.memory()[arguments[i]];
+   function untransform() {
+     return this.attr('transform', null);
+   } // merge the whole transformation chain into one matrix and returns it
+   function matrixify() {
+     var matrix = (this.attr('transform') || ''). // split transformations
+     split(transforms).slice(0, -1).map(function (str) {
+       // generate key => value pairs
+       var kv = str.trim().split('(');
+       return [kv[0], kv[1].split(delimiter).map(function (str) {
+         return parseFloat(str);
+       })];
+     }).reverse() // merge every transformation into one matrix
+     .reduce(function (matrix, transform) {
+       if (transform[0] === 'matrix') {
+         return matrix.lmultiply(Matrix.fromArray(transform[1]));
        }
-     }
  
-     return this;
-   },
-   // Initialize or return local memory object
-   memory: function memory() {
-     return this._memory || (this._memory = {});
-   }
- });
- /* global idFromReference */
- // Method for getting an element by id
+       return matrix[transform[0]].apply(matrix, transform[1]);
+     }, new Matrix());
+     return matrix;
+   } // add an element to another parent without changing the visual representation on the screen
  
- SVG.get = function (id) {
-   var node = document.getElementById(idFromReference(id) || id);
-   return SVG.adopt(node);
- }; // Select elements by query string
+   function toParent(parent) {
+     if (this === parent) return this;
+     var ctm = this.screenCTM();
+     var pCtm = parent.screenCTM().inverse();
+     this.addTo(parent).untransform().transform(pCtm.multiply(ctm));
+     return this;
+   } // same as above with parent equals root-svg
  
+   function toDoc() {
+     return this.toParent(this.doc());
+   } // Add transformations
  
- SVG.select = function (query, parent) {
-   return SVG.utils.map((parent || document).querySelectorAll(query), function (node) {
-     return SVG.adopt(node);
-   });
- };
+   function transform(o, relative) {
+     // Act as a getter if no object was passed
+     if (o == null || typeof o === 'string') {
+       var decomposed = new Matrix(this).decompose();
+       return decomposed[o] || decomposed;
+     }
  
- SVG.$$ = function (query, parent) {
-   return SVG.utils.map((parent || document).querySelectorAll(query), function (node) {
-     return SVG.adopt(node);
-   });
- };
+     if (!Matrix.isMatrixLike(o)) {
+       // Set the origin according to the defined transform
+       o = _objectSpread({}, o, {
+         origin: getOrigin(o, this)
+       });
+     } // The user can pass a boolean, an Element or an Matrix or nothing
  
- SVG.$ = function (query, parent) {
-   return SVG.adopt((parent || document).querySelector(query));
- };
  
- SVG.extend(SVG.Parent, {
-   // Scoped select method
-   select: function select(query) {
-     return SVG.select(query, this.node);
+     var cleanRelative = relative === true ? this : relative || false;
+     var result = new Matrix(cleanRelative).transform(o);
+     return this.attr('transform', result);
    }
- });
- /* eslint no-unused-vars: 0 */
+   registerMethods('Element', {
+     untransform: untransform,
+     matrixify: matrixify,
+     toParent: toParent,
+     toDoc: toDoc,
+     transform: transform
+   });
  
- function createElement(element, makeNested) {
-   if (element instanceof SVG.Element) return element;
+   // FIXME: import this to runner
  
-   if (_typeof(element) === 'object') {
-     return SVG.adopt(element);
-   }
+   function rx(rx) {
+     return this.attr('rx', rx);
+   } // Radius y value
  
-   if (element == null) {
-     return new SVG.Doc();
-   }
+   function ry(ry) {
+     return this.attr('ry', ry);
+   } // Move over x-axis
  
-   if (typeof element === 'string' && element.charAt(0) !== '<') {
-     return SVG.adopt(document.querySelector(element));
-   }
+   function x(x) {
+     return x == null ? this.cx() - this.rx() : this.cx(x + this.rx());
+   } // Move over y-axis
  
-   var node = SVG.create('svg');
-   node.innerHTML = element;
-   element = SVG.adopt(node.firstElementChild);
-   return element;
- }
- function isNulledBox(box) {
-   return !box.w && !box.h && !box.x && !box.y;
- }
- function domContains(node) {
-   return (document.documentElement.contains || function (node) {
-     // This is IE - it does not support contains() for top-level SVGs
-     while (node.parentNode) {
-       node = node.parentNode;
-     }
+   function y(y) {
+     return y == null ? this.cy() - this.ry() : this.cy(y + this.ry());
+   } // Move by center over x-axis
  
-     return node === document;
-   }).call(document.documentElement, node);
- }
+   function cx(x) {
+     return x == null ? this.attr('cx') : this.attr('cx', x);
+   } // Move by center over y-axis
  
function pathRegReplace(a, b, c, d) {
-   return c + d.replace(SVG.regex.dots, ' .');
- } // creates deep clone of array
  function cy(y) {
+     return y == null ? this.attr('cy') : this.attr('cy', y);
+   } // Set width of element
  
+   function width(width) {
+     return width == null ? this.rx() * 2 : this.rx(new SVGNumber(width).divide(2));
+   } // Set height of element
  
- function arrayClone(arr) {
-   var clone = arr.slice(0);
+   function height(height) {
+     return height == null ? this.ry() * 2 : this.ry(new SVGNumber(height).divide(2));
+   } // Custom size function
  
-   for (var i = clone.length; i--;) {
-     if (Array.isArray(clone[i])) {
-       clone[i] = arrayClone(clone[i]);
-     }
+   function size(width, height) {
+     var p = proportionalSize(this, width, height);
+     return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2));
    }
  
-   return clone;
- } // tests if a given element is instance of an object
+   var circled = /*#__PURE__*/Object.freeze({
+     rx: rx,
+     ry: ry,
+     x: x,
+     y: y,
+     cx: cx,
+     cy: cy,
+     width: width,
+     height: height,
+     size: size
+   });
+   var Shape =
+   /*#__PURE__*/
+   function (_Element) {
+     _inherits(Shape, _Element);
+     function Shape() {
+       _classCallCheck(this, Shape);
  
+       return _possibleConstructorReturn(this, _getPrototypeOf(Shape).apply(this, arguments));
+     }
  
- function _is(el, obj) {
-   return el instanceof obj;
- } // tests if a given selector matches an element
+     return Shape;
+   }(Element);
  
+   var Circle =
+   /*#__PURE__*/
+   function (_Shape) {
+     _inherits(Circle, _Shape);
  
- function _matches(el, selector) {
-   return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);
- } // Convert dash-separated-string to camelCase
+     function Circle(node) {
+       _classCallCheck(this, Circle);
  
+       return _possibleConstructorReturn(this, _getPrototypeOf(Circle).call(this, nodeOrNew('circle', node), Circle));
+     }
  
- function camelCase(s) {
-   return s.toLowerCase().replace(/-(.)/g, function (m, g) {
-     return g.toUpperCase();
+     _createClass(Circle, [{
+       key: "radius",
+       value: function radius(r) {
+         return this.attr('r', r);
+       } // Radius x value
+     }, {
+       key: "rx",
+       value: function rx$$1(_rx) {
+         return this.attr('r', _rx);
+       } // Alias radius x value
+     }, {
+       key: "ry",
+       value: function ry$$1(_ry) {
+         return this.rx(_ry);
+       }
+     }]);
+     return Circle;
+   }(Shape);
+   extend(Circle, {
+     x: x,
+     y: y,
+     cx: cx,
+     cy: cy,
+     width: width,
+     height: height,
+     size: size
    });
- } // Capitalize first letter of a string
+   registerMethods({
+     Element: {
+       // Create circle element
+       circle: function circle(size$$1) {
+         return this.put(new Circle()).radius(new SVGNumber(size$$1).divide(2)).move(0, 0);
+       }
+     }
+   });
+   register(Circle);
  
+   var Ellipse =
+   /*#__PURE__*/
+   function (_Shape) {
+     _inherits(Ellipse, _Shape);
  
- function capitalize(s) {
-   return s.charAt(0).toUpperCase() + s.slice(1);
- } // Ensure to six-based hex
+     function Ellipse(node) {
+       _classCallCheck(this, Ellipse);
  
+       return _possibleConstructorReturn(this, _getPrototypeOf(Ellipse).call(this, nodeOrNew('ellipse', node), Ellipse));
+     }
  
- function fullHex(hex) {
-   return hex.length === 4 ? ['#', hex.substring(1, 2), hex.substring(1, 2), hex.substring(2, 3), hex.substring(2, 3), hex.substring(3, 4), hex.substring(3, 4)].join('') : hex;
- } // Component to hex value
+     return Ellipse;
+   }(Shape);
+   extend(Ellipse, circled);
+   registerMethods('Container', {
+     // Create an ellipse
+     ellipse: function ellipse(width$$1, height$$1) {
+       return this.put(new Ellipse()).size(width$$1, height$$1).move(0, 0);
+     }
+   });
+   register(Ellipse);
  
+   var Stop =
+   /*#__PURE__*/
+   function (_Element) {
+     _inherits(Stop, _Element);
  
- function compToHex(comp) {
-   var hex = comp.toString(16);
-   return hex.length === 1 ? '0' + hex : hex;
- } // Calculate proportional width and height values when necessary
+     function Stop(node) {
+       _classCallCheck(this, Stop);
  
+       return _possibleConstructorReturn(this, _getPrototypeOf(Stop).call(this, nodeOrNew('stop', node), Stop));
+     } // add color stops
  
- function proportionalSize(element, width, height) {
-   if (width == null || height == null) {
-     var box = element.bbox();
  
-     if (width == null) {
-       width = box.width / box.height * height;
-     } else if (height == null) {
-       height = box.height / box.width * width;
-     }
-   }
+     _createClass(Stop, [{
+       key: "update",
+       value: function update(o) {
+         if (typeof o === 'number' || o instanceof SVGNumber) {
+           o = {
+             offset: arguments[0],
+             color: arguments[1],
+             opacity: arguments[2]
+           };
+         } // set attributes
  
-   return {
-     width: width,
-     height: height
-   };
- } // Map matrix array to object
  
+         if (o.opacity != null) this.attr('stop-opacity', o.opacity);
+         if (o.color != null) this.attr('stop-color', o.color);
+         if (o.offset != null) this.attr('offset', new SVGNumber(o.offset));
+         return this;
+       }
+     }]);
  
- function arrayToMatrix(a) {
-   return {
-     a: a[0],
-     b: a[1],
-     c: a[2],
-     d: a[3],
-     e: a[4],
-     f: a[5]
-   };
- } // Add centre point to transform object
+     return Stop;
+   }(Element);
+   register(Stop);
  
+   function baseFind(query, parent) {
+     return map((parent || document).querySelectorAll(query), function (node) {
+       return adopt(node);
+     });
+   } // Scoped find method
  
- function ensureCentre(o, target) {
-   o.cx = o.cx == null ? target.bbox().cx : o.cx;
-   o.cy = o.cy == null ? target.bbox().cy : o.cy;
- } // PathArray Helpers
+   function find(query) {
+     return baseFind(query, this.node);
+   }
+   registerMethods('Dom', {
+     find: find
+   });
  
+   // FIXME: add to runner
+   function from(x, y) {
+     return (this._element || this).type === 'radialGradient' ? this.attr({
+       fx: new SVGNumber(x),
+       fy: new SVGNumber(y)
+     }) : this.attr({
+       x1: new SVGNumber(x),
+       y1: new SVGNumber(y)
+     });
+   }
+   function to(x, y) {
+     return (this._element || this).type === 'radialGradient' ? this.attr({
+       cx: new SVGNumber(x),
+       cy: new SVGNumber(y)
+     }) : this.attr({
+       x2: new SVGNumber(x),
+       y2: new SVGNumber(y)
+     });
+   }
  
- function arrayToString(a) {
-   for (var i = 0, il = a.length, s = ''; i < il; i++) {
-     s += a[i][0];
+   var gradiented = /*#__PURE__*/Object.freeze({
+     from: from,
+     to: to
+   });
  
-     if (a[i][1] != null) {
-       s += a[i][1];
+   var Gradient =
+   /*#__PURE__*/
+   function (_Container) {
+     _inherits(Gradient, _Container);
  
-       if (a[i][2] != null) {
-         s += ' ';
-         s += a[i][2];
+     function Gradient(type) {
+       _classCallCheck(this, Gradient);
  
-         if (a[i][3] != null) {
-           s += ' ';
-           s += a[i][3];
-           s += ' ';
-           s += a[i][4];
+       return _possibleConstructorReturn(this, _getPrototypeOf(Gradient).call(this, nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type), Gradient));
+     } // Add a color stop
  
-           if (a[i][5] != null) {
-             s += ' ';
-             s += a[i][5];
-             s += ' ';
-             s += a[i][6];
  
-             if (a[i][7] != null) {
-               s += ' ';
-               s += a[i][7];
-             }
-           }
+     _createClass(Gradient, [{
+       key: "stop",
+       value: function stop(offset, color, opacity) {
+         return this.put(new Stop()).update(offset, color, opacity);
+       } // Update gradient
+     }, {
+       key: "update",
+       value: function update(block) {
+         // remove all stops
+         this.clear(); // invoke passed block
+         if (typeof block === 'function') {
+           block.call(this, this);
          }
+         return this;
+       } // Return the fill id
+     }, {
+       key: "url",
+       value: function url() {
+         return 'url(#' + this.id() + ')';
+       } // Alias string convertion to fill
+     }, {
+       key: "toString",
+       value: function toString() {
+         return this.url();
+       } // custom attr to handle transform
+     }, {
+       key: "attr",
+       value: function attr(a, b, c) {
+         if (a === 'transform') a = 'gradientTransform';
+         return _get(_getPrototypeOf(Gradient.prototype), "attr", this).call(this, a, b, c);
+       }
+     }, {
+       key: "targets",
+       value: function targets() {
+         return baseFind('svg [fill*="' + this.id() + '"]');
+       }
+     }, {
+       key: "bbox",
+       value: function bbox() {
+         return new Box();
+       }
+     }]);
+     return Gradient;
+   }(Container);
+   extend(Gradient, gradiented);
+   registerMethods({
+     Container: {
+       // Create gradient element in defs
+       gradient: function gradient(type, block) {
+         return this.defs().gradient(type, block);
+       }
+     },
+     // define gradient
+     Defs: {
+       gradient: function gradient(type, block) {
+         return this.put(new Gradient(type)).update(block);
        }
      }
-   }
+   });
+   register(Gradient);
  
-   return s + ' ';
- } // Deep new id assignment
+   var Pattern =
+   /*#__PURE__*/
+   function (_Container) {
+     _inherits(Pattern, _Container);
  
+     // Initialize node
+     function Pattern(node) {
+       _classCallCheck(this, Pattern);
  
- function assignNewId(node) {
-   // do the same for SVG child nodes as well
-   for (var i = node.children.length - 1; i >= 0; i--) {
-     assignNewId(node.children[i]);
-   }
+       return _possibleConstructorReturn(this, _getPrototypeOf(Pattern).call(this, nodeOrNew('pattern', node), Pattern));
+     } // Return the fill id
  
-   if (node.id) {
-     return SVG.adopt(node).id(SVG.eid(node.nodeName));
-   }
  
-   return SVG.adopt(node);
- } // Add more bounding box properties
+     _createClass(Pattern, [{
+       key: "url",
+       value: function url() {
+         return 'url(#' + this.id() + ')';
+       } // Update pattern by rebuilding
  
+     }, {
+       key: "update",
+       value: function update(block) {
+         // remove content
+         this.clear(); // invoke passed block
  
- function fullBox(b) {
-   if (b.x == null) {
-     b.x = 0;
-     b.y = 0;
-     b.width = 0;
-     b.height = 0;
-   }
+         if (typeof block === 'function') {
+           block.call(this, this);
+         }
  
-   b.w = b.width;
-   b.h = b.height;
-   b.x2 = b.x + b.width;
-   b.y2 = b.y + b.height;
-   b.cx = b.x + b.width / 2;
-   b.cy = b.y + b.height / 2;
-   return b;
- } // Get id from reference string
- function idFromReference(url) {
-   var m = (url || '').toString().match(SVG.regex.reference);
-   if (m) return m[1];
- } // Create matrix array for looping
- var abcdef = 'abcdef'.split('');
- function closeEnough(a, b, threshold) {
-   return Math.abs(b - a) < (threshold || 1e-6);
- }
- function isMatrixLike(o) {
-   return o.a != null || o.b != null || o.c != null || o.d != null || o.e != null || o.f != null;
- } // TODO: Refactor this to a static function of matrix.js
- function formatTransforms(o) {
-   // Get all of the parameters required to form the matrix
-   var flipBoth = o.flip === 'both' || o.flip === true;
-   var flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1;
-   var flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1;
-   var skewX = o.skew && o.skew.length ? o.skew[0] : isFinite(o.skew) ? o.skew : isFinite(o.skewX) ? o.skewX : 0;
-   var skewY = o.skew && o.skew.length ? o.skew[1] : isFinite(o.skew) ? o.skew : isFinite(o.skewY) ? o.skewY : 0;
-   var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX : isFinite(o.scale) ? o.scale * flipX : isFinite(o.scaleX) ? o.scaleX * flipX : flipX;
-   var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY : isFinite(o.scale) ? o.scale * flipY : isFinite(o.scaleY) ? o.scaleY * flipY : flipY;
-   var shear = o.shear || 0;
-   var theta = o.rotate || o.theta || 0;
-   var origin = new SVG.Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY);
-   var ox = origin.x;
-   var oy = origin.y;
-   var position = new SVG.Point(o.position || o.px || o.positionX, o.py || o.positionY);
-   var px = position.x;
-   var py = position.y;
-   var translate = new SVG.Point(o.translate || o.tx || o.translateX, o.ty || o.translateY);
-   var tx = translate.x;
-   var ty = translate.y;
-   var relative = new SVG.Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY);
-   var rx = relative.x;
-   var ry = relative.y; // Populate all of the values
-   return {
-     scaleX: scaleX,
-     scaleY: scaleY,
-     skewX: skewX,
-     skewY: skewY,
-     shear: shear,
-     theta: theta,
-     rx: rx,
-     ry: ry,
-     tx: tx,
-     ty: ty,
-     ox: ox,
-     oy: oy,
-     px: px,
-     py: py
-   };
- } // left matrix, right matrix, target matrix which is overwritten
- function matrixMultiply(l, r, o) {
-   // Work out the product directly
-   var a = l.a * r.a + l.c * r.b;
-   var b = l.b * r.a + l.d * r.b;
-   var c = l.a * r.c + l.c * r.d;
-   var d = l.b * r.c + l.d * r.d;
-   var e = l.e + l.a * r.e + l.c * r.f;
-   var f = l.f + l.b * r.e + l.d * r.f; // make sure to use local variables because l/r and o could be the same
-   o.a = a;
-   o.b = b;
-   o.c = c;
-   o.d = d;
-   o.e = e;
-   o.f = f;
-   return o;
- }
- function getOrigin(o, element) {
-   // Allow origin or around as the names
-   var origin = o.origin; // o.around == null ? o.origin : o.around
-   var ox, oy; // Allow the user to pass a string to rotate around a given point
-   if (typeof origin === 'string' || origin == null) {
-     // Get the bounding box of the element with no transformations applied
-     var string = (origin || 'center').toLowerCase().trim();
-     var _element$bbox = element.bbox(),
-         height = _element$bbox.height,
-         width = _element$bbox.width,
-         x = _element$bbox.x,
-         y = _element$bbox.y; // Calculate the transformed x and y coordinates
-     var bx = string.includes('left') ? x : string.includes('right') ? x + width : x + width / 2;
-     var by = string.includes('top') ? y : string.includes('bottom') ? y + height : y + height / 2; // Set the bounds eg : "bottom-left", "Top right", "middle" etc...
-     ox = o.ox != null ? o.ox : bx;
-     oy = o.oy != null ? o.oy : by;
-   } else {
-     ox = origin[0];
-     oy = origin[1];
-   } // Return the origin as it is if it wasn't a string
-   return [ox, oy];
- }
- /* globals fullBox, domContains, isNulledBox, Exception */
- SVG.Box = SVG.invent({
-   create: function create(source) {
-     var base = [0, 0, 0, 0];
-     source = typeof source === 'string' ? source.split(SVG.regex.delimiter).map(parseFloat) : Array.isArray(source) ? source : _typeof(source) === 'object' ? [source.left != null ? source.left : source.x, source.top != null ? source.top : source.y, source.width, source.height] : arguments.length === 4 ? [].slice.call(arguments) : base;
-     this.x = source[0];
-     this.y = source[1];
-     this.width = source[2];
-     this.height = source[3]; // add center, right, bottom...
-     fullBox(this);
-   },
-   extend: {
-     // Merge rect box with another, return a new instance
-     merge: function merge(box) {
-       var x = Math.min(this.x, box.x);
-       var y = Math.min(this.y, box.y);
-       return new SVG.Box(x, y, Math.max(this.x + this.width, box.x + box.width) - x, Math.max(this.y + this.height, box.y + box.height) - y);
-     },
-     transform: function transform(m) {
-       var xMin = Infinity;
-       var xMax = -Infinity;
-       var yMin = Infinity;
-       var yMax = -Infinity;
-       var pts = [new SVG.Point(this.x, this.y), new SVG.Point(this.x2, this.y), new SVG.Point(this.x, this.y2), new SVG.Point(this.x2, this.y2)];
-       pts.forEach(function (p) {
-         p = p.transform(m);
-         xMin = Math.min(xMin, p.x);
-         xMax = Math.max(xMax, p.x);
-         yMin = Math.min(yMin, p.y);
-         yMax = Math.max(yMax, p.y);
-       });
-       return new SVG.Box(xMin, yMin, xMax - xMin, yMax - yMin);
-     },
-     addOffset: function addOffset() {
-       // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
-       this.x += window.pageXOffset;
-       this.y += window.pageYOffset;
-       return this;
-     },
-     toString: function toString() {
-       return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height;
-     },
-     toArray: function toArray() {
-       return [this.x, this.y, this.width, this.height];
-     },
-     morph: function morph(x, y, width, height) {
-       this.destination = new SVG.Box(x, y, width, height);
-       return this;
-     },
-     at: function at(pos) {
-       if (!this.destination) return this;
-       return new SVG.Box(this.x + (this.destination.x - this.x) * pos, this.y + (this.destination.y - this.y) * pos, this.width + (this.destination.width - this.width) * pos, this.height + (this.destination.height - this.height) * pos);
-     }
-   },
-   // Define Parent
-   parent: SVG.Element,
-   // Constructor
-   construct: {
-     // Get bounding box
-     bbox: function bbox() {
-       var box;
+         return this;
+       } // Alias string convertion to fill
  
-       try {
-         // find native bbox
-         box = this.node.getBBox();
+     }, {
+       key: "toString",
+       value: function toString() {
+         return this.url();
+       } // custom attr to handle transform
  
-         if (isNulledBox(box) && !domContains(this.node)) {
-           throw new Exception('Element not in the dom');
-         }
-       } catch (e) {
-         try {
-           var clone = this.clone(SVG.parser().svg).show();
-           box = clone.node.getBBox();
-           clone.remove();
-         } catch (e) {
-           console.warn('Getting a bounding box of this element is not possible');
-         }
+     }, {
+       key: "attr",
+       value: function attr(a, b, c) {
+         if (a === 'transform') a = 'patternTransform';
+         return _get(_getPrototypeOf(Pattern.prototype), "attr", this).call(this, a, b, c);
+       }
+     }, {
+       key: "targets",
+       value: function targets() {
+         return baseFind('svg [fill*="' + this.id() + '"]');
+       }
+     }, {
+       key: "bbox",
+       value: function bbox() {
+         return new Box();
+       }
+     }]);
+     return Pattern;
+   }(Container);
+   registerMethods({
+     Container: {
+       // Create pattern element in defs
+       pattern: function pattern(width, height, block) {
+         return this.defs().pattern(width, height, block);
        }
-       return new SVG.Box(box);
      },
-     rbox: function rbox(el) {
-       // IE11 throws an error when element not in dom
-       try {
-         var box = new SVG.Box(this.node.getBoundingClientRect());
-         if (el) return box.transform(el.screenCTM().inverse());
-         return box.addOffset();
-       } catch (e) {
-         return new SVG.Box();
+     Defs: {
+       pattern: function pattern(width, height, block) {
+         return this.put(new Pattern()).update(block).attr({
+           x: 0,
+           y: 0,
+           width: width,
+           height: height,
+           patternUnits: 'userSpaceOnUse'
+         });
        }
      }
-   }
- });
- SVG.extend([SVG.Doc, SVG.Symbol, SVG.Image, SVG.Pattern, SVG.Marker, SVG.ForeignObject, SVG.View], {
-   viewbox: function viewbox(x, y, width, height) {
-     // act as getter
-     if (x == null) return new SVG.Box(this.attr('viewBox')); // act as setter
-     return this.attr('viewBox', new SVG.Box(x, y, width, height));
-   }
- });
+   });
+   register(Pattern);
  
- SVG.parser = function () {
-   var b;
+   var Image =
+   /*#__PURE__*/
+   function (_Shape) {
+     _inherits(Image, _Shape);
  
-   if (!SVG.parser.nodes.svg.node.parentNode) {
-     b = document.body || document.documentElement;
-     SVG.parser.nodes.svg.addTo(b);
-   }
+     function Image(node) {
+       _classCallCheck(this, Image);
  
-   return SVG.parser.nodes;
- };
- SVG.parser.nodes = {
-   svg: SVG().size(2, 0).css({
-     opacity: 0,
-     position: 'absolute',
-     left: '-100%',
-     top: '-100%',
-     overflow: 'hidden'
-   })
- };
- SVG.parser.nodes.path = SVG.parser.nodes.svg.path().node;
- /* global requestAnimationFrame */
- SVG.Animator = {
-   nextDraw: null,
-   frames: new SVG.Queue(),
-   timeouts: new SVG.Queue(),
-   timer: window.performance || window.Date,
-   transforms: [],
-   frame: function frame(fn) {
-     // Store the node
-     var node = SVG.Animator.frames.push({
-       run: fn
-     }); // Request an animation frame if we don't have one
-     if (SVG.Animator.nextDraw === null) {
-       SVG.Animator.nextDraw = requestAnimationFrame(SVG.Animator._draw);
-     } // Return the node so we can remove it easily
-     return node;
-   },
-   transform_frame: function transform_frame(fn, id) {
-     SVG.Animator.transforms[id] = fn;
-   },
-   timeout: function timeout(fn, delay) {
-     delay = delay || 0; // Work out when the event should fire
-     var time = SVG.Animator.timer.now() + delay; // Add the timeout to the end of the queue
-     var node = SVG.Animator.timeouts.push({
-       run: fn,
-       time: time
-     }); // Request another animation frame if we need one
-     if (SVG.Animator.nextDraw === null) {
-       SVG.Animator.nextDraw = requestAnimationFrame(SVG.Animator._draw);
-     }
+       return _possibleConstructorReturn(this, _getPrototypeOf(Image).call(this, nodeOrNew('image', node), Image));
+     } // (re)load image
  
-     return node;
-   },
-   cancelFrame: function cancelFrame(node) {
-     SVG.Animator.frames.remove(node);
-   },
-   clearTimeout: function clearTimeout(node) {
-     SVG.Animator.timeouts.remove(node);
-   },
-   _draw: function _draw(now) {
-     // Run all the timeouts we can run, if they are not ready yet, add them
-     // to the end of the queue immediately! (bad timeouts!!! [sarcasm])
-     var nextTimeout = null;
-     var lastTimeout = SVG.Animator.timeouts.last();
-     while (nextTimeout = SVG.Animator.timeouts.shift()) {
-       // Run the timeout if its time, or push it to the end
-       if (now >= nextTimeout.time) {
-         nextTimeout.run();
-       } else {
-         SVG.Animator.timeouts.push(nextTimeout);
-       } // If we hit the last item, we should stop shifting out more items
  
+     _createClass(Image, [{
+       key: "load",
+       value: function load(url, callback) {
+         if (!url) return this;
+         var img = new window.Image();
+         on(img, 'load', function (e) {
+           var p = this.parent(Pattern); // ensure image size
  
-       if (nextTimeout === lastTimeout) break;
-     } // Run all of the animation frames
+           if (this.width() === 0 && this.height() === 0) {
+             this.size(img.width, img.height);
+           }
  
+           if (p instanceof Pattern) {
+             // ensure pattern size if not set
+             if (p.width() === 0 && p.height() === 0) {
+               p.size(this.width(), this.height());
+             }
+           }
  
-     var nextFrame = null;
-     var lastFrame = SVG.Animator.frames.last();
+           if (typeof callback === 'function') {
+             callback.call(this, {
+               width: img.width,
+               height: img.height,
+               ratio: img.width / img.height,
+               url: url
+             });
+           }
+         }, this);
+         on(img, 'load error', function () {
+           // dont forget to unbind memory leaking events
+           off(img);
+         });
+         return this.attr('href', img.src = url, xlink);
+       }
+     }, {
+       key: "attrHook",
+       value: function attrHook(obj) {
+         var _this = this;
  
-     while (nextFrame !== lastFrame && (nextFrame = SVG.Animator.frames.shift())) {
-       nextFrame.run();
+         return obj.doc().defs().pattern(0, 0, function (pattern) {
+           pattern.add(_this);
+         });
+       }
+     }]);
+     return Image;
+   }(Shape);
+   registerMethods({
+     Container: {
+       // create image element, load image and set its size
+       image: function image(source, callback) {
+         return this.put(new Image()).size(0, 0).load(source, callback);
+       }
      }
+   });
+   register(Image);
  
-     SVG.Animator.transforms.forEach(function (el) {
-       el();
-     }); // If we have remaining timeouts or frames, draw until we don't anymore
+   var PointArray = subClassArray('PointArray', SVGArray);
+   extend(PointArray, {
+     // Convert array to string
+     toString: function toString() {
+       // convert to a poly point string
+       for (var i = 0, il = this.length, array = []; i < il; i++) {
+         array.push(this[i].join(','));
+       }
  
-     SVG.Animator.nextDraw = SVG.Animator.timeouts.first() || SVG.Animator.frames.first() ? requestAnimationFrame(SVG.Animator._draw) : null;
-   }
- };
- SVG.Morphable = SVG.invent({
-   create: function create(stepper) {
-     // FIXME: the default stepper does not know about easing
-     this._stepper = stepper || new SVG.Ease('-');
-     this._from = null;
-     this._to = null;
-     this._type = null;
-     this._context = null;
-     this._morphObj = null;
-   },
-   extend: {
-     from: function from(val) {
-       if (val == null) {
-         return this._from;
-       }
-       this._from = this._set(val);
-       return this;
+       return array.join(' ');
+     },
+     // Convert array to line object
+     toLine: function toLine() {
+       return {
+         x1: this[0][0],
+         y1: this[0][1],
+         x2: this[1][0],
+         y2: this[1][1]
+       };
      },
-     to: function to(val) {
-       if (val == null) {
-         return this._to;
+     // Get morphed array at given position
+     at: function at(pos) {
+       // make sure a destination is defined
+       if (!this.destination) return this; // generate morphed point string
+       for (var i = 0, il = this.length, array = []; i < il; i++) {
+         array.push([this[i][0] + (this.destination[i][0] - this[i][0]) * pos, this[i][1] + (this.destination[i][1] - this[i][1]) * pos]);
        }
  
-       this._to = this._set(val);
-       return this;
+       return new PointArray(array);
      },
-     type: function type(_type) {
-       // getter
-       if (_type == null) {
-         return this._type;
-       } // setter
+     // Parse point string and flat array
+     parse: function parse() {
+       var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [[0, 0]];
+       var points = []; // if it is an array
  
+       if (array instanceof Array) {
+         // and it is not flat, there is no need to parse it
+         if (array[0] instanceof Array) {
+           return array;
+         }
+       } else {
+         // Else, it is considered as a string
+         // parse points
+         array = array.trim().split(delimiter).map(parseFloat);
+       } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
+       // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
  
-       this._type = _type;
-       return this;
+       if (array.length % 2 !== 0) array.pop(); // wrap points in two-tuples and parse points as floats
+       for (var i = 0, len = array.length; i < len; i = i + 2) {
+         points.push([array[i], array[i + 1]]);
+       }
+       return points;
      },
-     _set: function _set(value) {
-       if (!this._type) {
-         var type = _typeof(value);
-         if (type === 'number') {
-           this.type(SVG.Number);
-         } else if (type === 'string') {
-           if (SVG.Color.isColor(value)) {
-             this.type(SVG.Color);
-           } else if (SVG.regex.delimiter.test(value)) {
-             this.type(SVG.regex.pathLetters.test(value) ? SVG.PathArray : SVG.Array);
-           } else if (SVG.regex.numberAndUnit.test(value)) {
-             this.type(SVG.Number);
-           } else {
-             this.type(SVG.Morphable.NonMorphable);
-           }
-         } else if (SVG.MorphableTypes.indexOf(value.constructor) > -1) {
-           this.type(value.constructor);
-         } else if (Array.isArray(value)) {
-           this.type(SVG.Array);
-         } else if (type === 'object') {
-           this.type(SVG.Morphable.ObjectBag);
-         } else {
-           this.type(SVG.Morphable.NonMorphable);
+     // Move point string
+     move: function move(x, y) {
+       var box = this.bbox(); // get relative offset
+       x -= box.x;
+       y -= box.y; // move every point
+       if (!isNaN(x) && !isNaN(y)) {
+         for (var i = this.length - 1; i >= 0; i--) {
+           this[i] = [this[i][0] + x, this[i][1] + y];
          }
        }
  
index 1e502d1776f07742f0f80b44eda1c163443ffe6a,79229460e906380015438072c4555b47a8435132..a02d63562bfb52ad7ce3acc5f3358cc3b3e7259d
@@@ -883,68 -883,7 +883,58 @@@ describe('Element', function() 
          expect(draw.get(0+parserInDoc).get(1).type).toBe('circle')
          expect(draw.get(0+parserInDoc).get(1).attr('fill')).toBe('#ff0066')
        })
-       it('does not import on single elements, even with an argument it acts as a getter', function() {
-         var rect   = draw.rect(100,100).id(null)
-           , result = rect.svg('<circle r="300"></rect>')
-         expect(
-             result === '<rect width="100" height="100"></rect>'
-          || result === '<rect height="100" width="100"></rect>'
-          || result === '<rect xmlns="http://www.w3.org/2000/svg" width="100" height="100"></rect>'
-         ).toBeTruthy()
-       })
      })
 +    describe('with a modifier function', function() {
 +      var rect, circle, group
 +      beforeEach(function() {
 +        rect   = draw.rect(10, 10)
 +        circle = draw.circle(20, 20)
 +        group  = draw.group()
 +        
 +        group.add(rect)
 +        group.add(circle)
 +      })
 +
 +      it('executes the modifier', function() {
 +        var result = group.svg(function(instance) {
 +          instance.addClass('test')
 +        })
 +
 +        expect(
 +          result === '<g><rect width="10" height="10" class="test"></rect><circle r="10" cx="10" cy="10" class="test"></circle></g>'
 +          || result === '<g><rect height="10" width="10" class="test"></rect><circle r="10" cx="10" cy="10" class="test"></circle></g>'
 +          || result === '<g xmlns="http://www.w3.org/2000/svg"><rect width="10" height="10" class="test"></rect><circle r="10" cx="10" cy="10" class="test"></circle></g>'
 +        ).toBeTruthy()
 +      })
 +
 +      it("execute the modifier to replace the node", function() {
 +        var result = group.svg(function(instance) {
 +          if (instance instanceof SVG.Circle) {
 +            return draw.rect(15, 15)
 +          }
 +        })
 +        
 +        expect(
 +          result === '<g><rect width="10" height="10"></rect><rect width="15" height="15"></rect></g>'
 +          || result === '<g><rect height="10" width="10"></rect><rect height="15" width="15"></rect></g>'
 +          || result === '<g xmlns="http://www.w3.org/2000/svg"><rect width="10" height="10"></rect><rect width="15" height="15"></rect></g>'
 +        ).toBeTruthy()
 +      })
 +
 +      it("it deletes the node if the modifier returns false", function() {
 +        var result = group.svg(function(instance) {
 +          if (instance instanceof SVG.Circle) {
 +            return false
 +          }
 +        })
 +        
 +        expect(
 +          result === '<g><rect width="10" height="10"></rect></g>'
 +          || result === '<g><rect height="10" width="10"></rect></g>'
 +          || result === '<g xmlns="http://www.w3.org/2000/svg"><rect width="10" height="10"></rect></g>'
 +        ).toBeTruthy()
 +      })
 +    })
    })
  
    describe('writeDataToDom()', function() {