]> source.dussan.org Git - svg.js.git/commitdiff
remove native() methods, add methods of types directly to elemenet
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Mon, 12 Nov 2018 13:51:34 +0000 (14:51 +0100)
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Mon, 12 Nov 2018 13:51:34 +0000 (14:51 +0100)
CHANGELOG.md
dist/svg.js
spec/spec/element.js
spec/spec/matrix.js
spec/spec/point.js
src/elements/Dom.js
src/elements/Element.js
src/modules/core/parser.js
src/types/Box.js
src/types/Matrix.js
src/types/Point.js

index 94fff522ac4809ac755b067dce24fcbf04da3a71..034cdae96069aaaaa0e84a2935196b5aae0a64dd 100644 (file)
@@ -43,6 +43,7 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:
 - removed `show()` from `SVG.A` to avoid name clash (#802)
 - removed `size()` from `SVG.Text` to avoid name clash (#799)
 - removed `move(), dmove()` etc for groups to avoid inconsistencies, we will expect users to use transforms to move around groups as they should (especially since they are much simpler now).
+- removed `native()` function
 
 ### Changed
 - gradients now have there corresponding node as type and not only radial/linear
index 3a2d11a0be31a30200ffe8965e8465dfc174d679..ab443a740da73f8daab7cc5ee1ae06cea923d395 100644 (file)
@@ -6,7 +6,7 @@
 * @copyright Wout Fierens <wout@mick-wout.com>
 * @license MIT
 *
-* BUILT: Mon Nov 12 2018 13:58:37 GMT+0100 (GMT+01:00)
+* BUILT: Mon Nov 12 2018 14:48:34 GMT+0100 (GMT+01:00)
 */;
 var SVG = (function () {
   'use strict';
@@ -1089,1699 +1089,1657 @@ var SVG = (function () {
     return Color;
   }();
 
-  var EventTarget =
+  var Point =
   /*#__PURE__*/
-  function (_Base) {
-    _inherits(EventTarget, _Base);
-
-    function EventTarget() {
-      var _this;
-
-      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
-          _ref$events = _ref.events,
-          events = _ref$events === void 0 ? {} : _ref$events;
-
-      _classCallCheck(this, EventTarget);
+  function () {
+    // Initialize
+    function Point() {
+      _classCallCheck(this, Point);
 
-      _this = _possibleConstructorReturn(this, _getPrototypeOf(EventTarget).call(this));
-      _this.events = events;
-      return _this;
+      this.init.apply(this, arguments);
     }
 
-    _createClass(EventTarget, [{
-      key: "addEventListener",
-      value: function addEventListener() {} // Bind given event to listener
+    _createClass(Point, [{
+      key: "init",
+      value: function init(x, y) {
+        var source;
+        var base = {
+          x: 0,
+          y: 0 // ensure source as object
 
-    }, {
-      key: "on",
-      value: function on$$1(event, listener, binding, options) {
-        on(this, event, listener, binding, options);
+        };
+        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;
         return this;
-      } // Unbind event from listener
+      } // Clone point
 
     }, {
-      key: "off",
-      value: function off$$1(event, listener) {
-        off(this, event, listener);
+      key: "clone",
+      value: function clone() {
+        return new Point(this);
+      } // transform point with matrix
 
-        return this;
-      }
     }, {
-      key: "dispatch",
-      value: function dispatch$$1(event, data) {
-        return dispatch(this, event, data);
+      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);
       }
     }, {
-      key: "dispatchEvent",
-      value: function dispatchEvent(event) {
-        var bag = this.getEventHolder().events;
-        if (!bag) return true;
-        var events = bag[event.type];
+      key: "toArray",
+      value: function toArray() {
+        return [this.x, this.y];
+      }
+    }]);
 
-        for (var i in events) {
-          for (var j in events[i]) {
-            events[i][j](event);
-          }
-        }
+    return Point;
+  }();
+  function point(x, y) {
+    return new Point(x, y).transform(this.screenCTM().inverse());
+  }
 
-        return !event.defaultPrevented;
-      } // Fire given event
+  function parser() {
+    // Reuse cached element if possible
+    if (!parser.nodes) {
+      var svg = makeInstance().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
+      };
+    }
 
-    }, {
-      key: "fire",
-      value: function fire(event, data) {
-        this.dispatch(event, data);
+    if (!parser.nodes.svg.node.parentNode) {
+      var b = globals.document.body || globals.document.documentElement;
+      parser.nodes.svg.addTo(b);
+    }
+
+    return parser.nodes;
+  }
+
+  function isNulledBox(box) {
+    return !box.w && !box.h && !box.x && !box.y;
+  }
+
+  function domContains(node) {
+    return (globals.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(globals.document.documentElement, node);
+  }
+
+  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;
         return this;
+      } // 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: "getEventHolder",
-      value: function getEventHolder() {
-        return this;
+      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: "getEventTarget",
-      value: function getEventTarget() {
+      key: "addOffset",
+      value: function addOffset() {
+        // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
+        this.x += globals.window.pageXOffset;
+        this.y += globals.window.pageYOffset;
         return this;
       }
     }, {
-      key: "removeEventListener",
-      value: function removeEventListener() {}
+      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 EventTarget;
-  }(Base);
+    return Box;
+  }();
 
-  /* eslint no-new-func: "off" */
-  var subClassArray = function () {
-    try {
-      // try es6 subclassing
-      return Function('name', 'baseClass', '_constructor', ['baseClass = baseClass || Array', 'return {', '  [name]: class extends baseClass {', '    constructor (...args) {', '      super(...args)', '      _constructor && _constructor.apply(this, args)', '    }', '  }', '}[name]'].join('\n'));
-    } catch (e) {
-      // Use es5 approach
-      return function (name) {
-        var baseClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Array;
+  function getBox(cb) {
+    var box;
 
-        var _constructor = arguments.length > 2 ? arguments[2] : undefined;
+    try {
+      box = cb(this.node);
 
-        var Arr = function Arr() {
-          baseClass.apply(this, arguments);
-          _constructor && _constructor.apply(this, arguments);
-        };
+      if (isNulledBox(box) && !domContains(this.node)) {
+        throw new Error('Element not in the dom');
+      }
+    } catch (e) {
+      try {
+        var clone = this.clone().addTo(parser().svg).show();
+        box = cb(clone.node);
+        clone.remove();
+      } catch (e) {
+        throw new Error('Getting a bounding box of element "' + this.node.nodeName + '" is not possible');
+      }
+    }
 
-        Arr.prototype = Object.create(baseClass.prototype);
-        Arr.prototype.constructor = Arr;
+    return box;
+  }
 
-        Arr.prototype.map = function (fn) {
-          var arr = new Arr();
-          arr.push.apply(arr, Array.prototype.map.call(this, fn));
-          return arr;
-        };
+  function bbox() {
+    return new Box(getBox.call(this, function (node) {
+      return node.getBBox();
+    }));
+  }
+  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();
+  }
+  registerMethods({
+    viewbox: {
+      viewbox: function viewbox(x, y, width, height) {
+        // act as getter
+        if (x == null) return new Box(this.attr('viewBox')); // act as setter
 
-        return Arr;
-      };
+        return this.attr('viewBox', new Box(x, y, width, height));
+      }
     }
-  }();
-
-  var List = subClassArray('List', Array, function () {
-    var arr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
-    // This catches the case, that native map tries to create an array with new Array(1)
-    if (typeof arr === 'number') return this;
-    this.length = 0;
-    this.push.apply(this, _toConsumableArray(arr));
   });
-  extend(List, {
-    each: function each(fnOrMethodName) {
-      for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
-        args[_key - 1] = arguments[_key];
-      }
 
-      if (typeof fnOrMethodName === 'function') {
-        this.forEach(function (el) {
-          fnOrMethodName.call(el, el);
-        });
-      } else {
-        return this.map(function (el) {
-          return el[fnOrMethodName].apply(el, args);
-        });
-      }
+  function closeEnough(a, b, threshold) {
+    return Math.abs(b - a) < (threshold || 1e-6);
+  }
 
-      return this;
-    },
-    toArray: function toArray() {
-      return Array.prototype.concat.apply([], this);
-    }
-  });
+  var Matrix =
+  /*#__PURE__*/
+  function () {
+    function Matrix() {
+      _classCallCheck(this, Matrix);
 
-  List.extend = function (methods) {
-    methods = methods.reduce(function (obj, name) {
-      obj[name] = function () {
-        for (var _len2 = arguments.length, attrs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
-          attrs[_key2] = arguments[_key2];
-        }
+      this.init.apply(this, arguments);
+    } // Initialize
 
-        return this.each.apply(this, [name].concat(attrs));
-      };
 
-      return obj;
-    }, {});
-    extend(List, methods);
-  };
+    _createClass(Matrix, [{
+      key: "init",
+      value: function init(source) {
+        var base = Matrix.fromArray([1, 0, 0, 1, 0, 0]); // ensure source as object
 
-  function noop() {} // Default animation values
+        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
 
-  var timeline = {
-    duration: 400,
-    ease: '>',
-    delay: 0 // Default attribute values
+        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;
+        return this;
+      } // Clones this matrix
 
-  };
-  var 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'
-  };
+    }, {
+      key: "clone",
+      value: function clone() {
+        return new Matrix(this);
+      } // Transform a matrix into another matrix by manipulating the space
 
-  var defaults = /*#__PURE__*/Object.freeze({
-    noop: noop,
-    timeline: timeline,
-    attrs: attrs
-  });
+    }, {
+      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 SVGArray = subClassArray('SVGArray', Array, function (arr) {
-    this.init(arr);
-  });
-  extend(SVGArray, {
-    init: function init(arr) {
-      // This catches the case, that native map tries to create an array with new Array(1)
-      if (typeof arr === 'number') return this;
-      this.length = 0;
-      this.push.apply(this, _toConsumableArray(this.parse(arr)));
-      return this;
-    },
-    toArray: function toArray() {
-      return Array.prototype.concat.apply([], this);
-    },
-    toString: function toString() {
-      return this.join(' ');
-    },
-    // Flattens the array if needed
-    valueOf: function valueOf() {
-      var ret = [];
-      ret.push.apply(ret, _toConsumableArray(this));
-      return ret;
-    },
-    // Parse whitespace separated string
-    parse: function parse() {
-      var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
-      // If already is an array, no need to parse it
-      if (array instanceof Array) return array;
-      return array.trim().split(delimiter).map(parseFloat);
-    },
-    clone: function clone() {
-      return new this.constructor(this);
-    },
-    toSet: function toSet() {
-      return new Set(this);
-    }
-  });
 
-  var SVGNumber =
-  /*#__PURE__*/
-  function () {
-    // Initialize
-    function SVGNumber() {
-      _classCallCheck(this, SVGNumber);
+        var t = Matrix.formatTransforms(o);
+        var current = this;
 
-      this.init.apply(this, arguments);
-    }
+        var _transform = new Point(t.ox, t.oy).transform(current),
+            ox = _transform.x,
+            oy = _transform.y; // Construct the resulting matrix
 
-    _createClass(SVGNumber, [{
-      key: "init",
-      value: function init(value, unit) {
-        unit = Array.isArray(value) ? value[1] : unit;
-        value = Array.isArray(value) ? value[0] : value; // initialize defaults
 
-        this.value = 0;
-        this.unit = unit || ''; // parse value
+        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 (typeof value === 'number') {
-          // ensure a valid numeric value
-          this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value;
-        } else if (typeof value === 'string') {
-          unit = value.match(numberAndUnit);
+        if (isFinite(t.px) || isFinite(t.py)) {
+          var origin = new Point(ox, oy).transform(transformer); // TODO: Replace t.px with isFinite(t.px)
 
-          if (unit) {
-            // make value numeric
-            this.value = parseFloat(unit[1]); // normalize
+          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
 
-            if (unit[5] === '%') {
-              this.value /= 100;
-            } else if (unit[5] === 's') {
-              this.value *= 1000;
-            } // store unit
 
+        transformer.translateO(t.tx, t.ty);
+        return transformer;
+      } // Applies a matrix defined by its affine parameters
 
-            this.unit = unit[5];
-          }
-        } else {
-          if (value instanceof SVGNumber) {
-            this.value = value.valueOf();
-            this.unit = value.unit;
-          }
-        }
+    }, {
+      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
 
-        return this;
-      }
     }, {
-      key: "toString",
-      value: function toString() {
-        return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 : this.unit === 's' ? this.value / 1e3 : this.value) + this.unit;
+      key: "multiply",
+      value: function multiply(matrix) {
+        return this.clone().multiplyO(matrix);
       }
     }, {
-      key: "toJSON",
-      value: function toJSON() {
-        return this.toString();
+      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: "toArray",
-      value: function toArray() {
-        return [this.value, this.unit];
+      key: "lmultiply",
+      value: function lmultiply(matrix) {
+        return this.clone().lmultiplyO(matrix);
       }
     }, {
-      key: "valueOf",
-      value: function valueOf() {
-        return this.value;
-      } // Add number
-
-    }, {
-      key: "plus",
-      value: function plus(number) {
-        number = new SVGNumber(number);
-        return new SVGNumber(this + number, this.unit || number.unit);
-      } // Subtract number
-
-    }, {
-      key: "minus",
-      value: function minus(number) {
-        number = new SVGNumber(number);
-        return new SVGNumber(this - number, this.unit || number.unit);
-      } // Multiply number
-
-    }, {
-      key: "times",
-      value: function times(number) {
-        number = new SVGNumber(number);
-        return new SVGNumber(this * number, this.unit || number.unit);
-      } // Divide number
+      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: "divide",
-      value: function divide(number) {
-        number = new SVGNumber(number);
-        return new SVGNumber(this / number, this.unit || number.unit);
-      }
-    }]);
-
-    return SVGNumber;
-  }();
+      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 hooks = [];
-  function registerAttrHook(fn) {
-    hooks.push(fn);
-  } // Set svg element attribute
+        var det = a * d - b * c;
+        if (!det) throw new Error('Cannot invert ' + this); // Calculate the top 2x2 matrix
 
-  function attr(attr, val, ns) {
-    var _this = this;
+        var na = d / det;
+        var nb = -b / det;
+        var nc = -c / det;
+        var nd = a / det; // Apply the inverted matrix to the top right
 
-    // act as full getter
-    if (attr == null) {
-      // get an object of attributes
-      attr = {};
-      val = this.node.attributes;
-      var _iteratorNormalCompletion = true;
-      var _didIteratorError = false;
-      var _iteratorError = undefined;
+        var ne = -(na * e + nc * f);
+        var nf = -(nb * e + nd * f); // Construct the inverted matrix
 
-      try {
-        for (var _iterator = val[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-          var node = _step.value;
-          attr[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue;
-        }
-      } catch (err) {
-        _didIteratorError = true;
-        _iteratorError = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion && _iterator.return != null) {
-            _iterator.return();
-          }
-        } finally {
-          if (_didIteratorError) {
-            throw _iteratorError;
-          }
-        }
+        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
 
-      return attr;
-    } else if (attr instanceof Array) {
-      // loop through array and get all values
-      return attr.reduce(function (last, curr) {
-        last[curr] = _this.attr(curr);
-        return last;
-      }, {});
-    } else if (_typeof(attr) === 'object') {
-      // apply every attribute individually if an object is passed
-      for (val in attr) {
-        this.attr(val, attr[val]);
+    }, {
+      key: "translate",
+      value: function translate(x, y) {
+        return this.clone().translateO(x, y);
       }
-    } else if (val === null) {
-      // remove value
-      this.node.removeAttribute(attr);
-    } else if (val == null) {
-      // act as a getter if the first and only argument is not an object
-      val = this.node.getAttribute(attr);
-      return val == null ? attrs[attr] : isNumber.test(val) ? parseFloat(val) : val;
-    } else {
-      // Loop through hooks and execute them to convert value
-      val = hooks.reduce(function (_val, hook) {
-        return hook(attr, _val, _this);
-      }, val); // ensure correct numeric values (also accepts NaN and Infinity)
-
-      if (typeof val === 'number') {
-        val = new SVGNumber(val);
-      } else if (Color.isColor(val)) {
-        // ensure full hex color
-        val = new Color(val);
-      } else if (val.constructor === Array) {
-        // Check for plain arrays and parse array values
-        val = new SVGArray(val);
-      } // if the passed attribute is leading...
-
-
-      if (attr === 'leading') {
-        // ... call the leading method instead
-        if (this.leading) {
-          this.leading(val);
-        }
-      } else {
-        // set given attribute on node
-        typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString()) : this.node.setAttribute(attr, val.toString());
-      } // rebuild if required
+    }, {
+      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;
 
-      if (this.rebuild && (attr === 'font-size' || attr === 'x')) {
-        this.rebuild();
+        return (_this$clone = this.clone()).scaleO.apply(_this$clone, arguments);
       }
-    }
-
-    return this;
-  }
-
-  var Dom =
-  /*#__PURE__*/
-  function (_EventTarget) {
-    _inherits(Dom, _EventTarget);
-
-    function Dom(node, attrs) {
-      var _this2;
+    }, {
+      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;
 
-      _classCallCheck(this, Dom);
+        // Support uniform scaling
+        if (arguments.length === 3) {
+          cy = cx;
+          cx = y;
+          y = x;
+        }
 
-      _this2 = _possibleConstructorReturn(this, _getPrototypeOf(Dom).call(this, node));
-      _this2.node = node;
-      _this2.type = node.nodeName;
+        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
 
-      if (attrs && node !== attrs) {
-        _this2.attr(attrs);
+    }, {
+      key: "rotate",
+      value: function rotate(r, cx, cy) {
+        return this.clone().rotateO(r, cx, cy);
       }
-
-      return _this2;
-    } // Add given element at a position
-
-
-    _createClass(Dom, [{
-      key: "add",
-      value: function add(element, i) {
-        element = makeInstance(element);
-
-        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]);
-        }
-
+    }, {
+      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;
-      } // Add element to given container and return self
+      } // Flip matrix on x or y, at a given offset
 
     }, {
-      key: "addTo",
-      value: function addTo(parent) {
-        return makeInstance(parent).put(this);
-      } // Returns all child elements
-
+      key: "flip",
+      value: function flip(axis, around) {
+        return this.clone().flipO(axis, around);
+      }
     }, {
-      key: "children",
-      value: function children() {
-        return new List(map(this.node.children, function (node) {
-          return adopt(node);
-        }));
-      } // Remove all elements in this container
+      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: "clear",
-      value: function clear() {
-        // remove children
-        while (this.node.hasChildNodes()) {
-          this.node.removeChild(this.node.lastChild);
-        } // remove defs reference
-
-
-        delete this._defs;
+      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;
-      } // Clone element
+      } // Skew Matrix
 
     }, {
-      key: "clone",
-      value: function clone() {
-        // write dom data to the dom so the clone can pickup the data
-        this.writeDataToDom(); // clone element and assign new id
-
-        return assignNewId(this.node.cloneNode(true));
-      } // Iterates over all children and invokes a given block
+      key: "skew",
+      value: function skew(x, y, cx, cy) {
+        var _this$clone2;
 
+        return (_this$clone2 = this.clone()).skewO.apply(_this$clone2, arguments);
+      }
     }, {
-      key: "each",
-      value: function each(block, deep) {
-        var children = this.children();
-        var i, il;
+      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;
 
-        for (i = 0, il = children.length; i < il; i++) {
-          block.apply(children[i], [i, children]);
+        // support uniformal skew
+        if (arguments.length === 3) {
+          cy = cx;
+          cx = y;
+          y = x;
+        } // Convert degrees to radians
 
-          if (deep) {
-            children[i].each(block, deep);
-          }
-        }
 
+        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;
-      } // Get first child
+      } // SkewX
 
     }, {
-      key: "first",
-      value: function first() {
-        return adopt(this.node.firstChild);
-      } // Get a element at the given index
+      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: "get",
-      value: function get(i) {
-        return adopt(this.node.childNodes[i]);
+      key: "skewY",
+      value: function skewY(y, cx, cy) {
+        return this.skew(0, y, cx, cy);
       }
     }, {
-      key: "getEventHolder",
-      value: function getEventHolder() {
-        return this.node;
+      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: "getEventTarget",
-      value: function getEventTarget() {
-        return this.node;
-      } // Checks if the given element is a child
+      key: "around",
+      value: function around(cx, cy, matrix) {
+        return this.clone().aroundO(cx, cy, matrix);
+      } // Check if two matrices are equal
 
     }, {
-      key: "has",
-      value: function has(element) {
-        return this.index(element) >= 0;
-      } // Get / set id
+      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: "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
-
+      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 this.attr('id', _id);
-      } // Gets index of given element
+        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: "index",
-      value: function index(element) {
-        return [].slice.call(this.node.childNodes).indexOf(element.node);
-      } // Get the last child
+      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
 
-    }, {
-      key: "last",
-      value: function last() {
-        return adopt(this.node.lastChild);
-      } // matches the element vs a css selector
+        o.a = a;
+        o.b = b;
+        o.c = c;
+        o.d = d;
+        o.e = e;
+        o.f = f;
+        return o;
+      }
+    }]);
 
-    }, {
-      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
+    return Matrix;
+  }();
+  function ctm() {
+    return new Matrix(this.node.getCTM());
+  }
+  function screenCTM() {
+    /* 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);
+    }
 
-    }, {
-      key: "native",
-      value: function native() {
-        return this.node;
-      } // Returns the parent element instance
+    return new Matrix(this.node.getScreenCTM());
+  }
 
-    }, {
-      key: "parent",
-      value: function parent(type) {
-        var parent = this; // check for parent
+  var EventTarget =
+  /*#__PURE__*/
+  function (_Base) {
+    _inherits(EventTarget, _Base);
 
-        if (!parent.node.parentNode) return null; // get parent element
+    function EventTarget() {
+      var _this;
 
-        parent = adopt(parent.node.parentNode);
-        if (!type) return parent; // loop trough ancestors if type is given
+      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
+          _ref$events = _ref.events,
+          events = _ref$events === void 0 ? {} : _ref$events;
 
-        while (parent && parent.node instanceof globals.window.SVGElement) {
-          // FIXME: That shouldnt be neccessary
-          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
+      _classCallCheck(this, EventTarget);
 
-    }, {
-      key: "put",
-      value: function put(element, i) {
-        this.add(element, i);
-        return element;
-      } // Add element to given container and return container
+      _this = _possibleConstructorReturn(this, _getPrototypeOf(EventTarget).call(this));
+      _this.events = events;
+      return _this;
+    }
 
-    }, {
-      key: "putIn",
-      value: function putIn(parent) {
-        return makeInstance(parent).add(this);
-      } // Remove element
+    _createClass(EventTarget, [{
+      key: "addEventListener",
+      value: function addEventListener() {} // Bind given event to listener
 
     }, {
-      key: "remove",
-      value: function remove() {
-        if (this.parent()) {
-          this.parent().removeElement(this);
-        }
+      key: "on",
+      value: function on$$1(event, listener, binding, options) {
+        on(this, event, listener, binding, options);
 
         return this;
-      } // Remove a given child
+      } // Unbind event from listener
 
     }, {
-      key: "removeElement",
-      value: function removeElement(element) {
-        this.node.removeChild(element.node);
-        return this;
-      } // Replace this with element
+      key: "off",
+      value: function off$$1(event, listener) {
+        off(this, event, listener);
 
+        return this;
+      }
     }, {
-      key: "replace",
-      value: function replace(element) {
-        element = makeInstance(element);
-        this.node.parentNode.replaceChild(element.node, this.node);
-        return element;
+      key: "dispatch",
+      value: function dispatch$$1(event, data) {
+        return dispatch(this, event, data);
       }
     }, {
-      key: "round",
-      value: function round() {
-        var precision = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
-        var map$$1 = arguments.length > 1 ? arguments[1] : undefined;
-        var factor = Math.pow(10, precision);
-        var attrs = this.attr(); // If we have no map, build one from attrs
+      key: "dispatchEvent",
+      value: function dispatchEvent(event) {
+        var bag = this.getEventHolder().events;
+        if (!bag) return true;
+        var events = bag[event.type];
 
-        if (!map$$1) {
-          map$$1 = Object.keys(attrs);
-        } // Holds rounded attributes
+        for (var i in events) {
+          for (var j in events[i]) {
+            events[i][j](event);
+          }
+        }
 
+        return !event.defaultPrevented;
+      } // Fire given event
 
-        var newAttrs = {};
-        map$$1.forEach(function (key) {
-          newAttrs[key] = Math.round(attrs[key] * factor) / factor;
-        });
-        this.attr(newAttrs);
+    }, {
+      key: "fire",
+      value: function fire(event, data) {
+        this.dispatch(event, data);
         return this;
-      } // Return id on string conversion
-
+      }
     }, {
-      key: "toString",
-      value: function toString() {
-        return this.id();
-      } // Import raw svg
-
+      key: "getEventHolder",
+      value: function getEventHolder() {
+        return this;
+      }
     }, {
-      key: "svg",
-      value: function svg(svgOrFn, outerHTML) {
-        var well, len, fragment;
-
-        if (svgOrFn === false) {
-          outerHTML = false;
-          svgOrFn = null;
-        } // act as getter if no svg string is given
-
-
-        if (svgOrFn == null || typeof svgOrFn === 'function') {
-          // The default for exports is, that the outerNode is included
-          outerHTML = outerHTML == null ? true : outerHTML; // write svgjs data to the dom
-
-          this.writeDataToDom();
-          var current = this; // An export modifier was passed
-
-          if (svgOrFn != null) {
-            current = adopt(current.node.cloneNode(true)); // If the user wants outerHTML we need to process this node, too
-
-            if (outerHTML) {
-              var result = svgOrFn(current);
-              current = result || current; // The user does not want this node? Well, then he gets nothing
-
-              if (result === false) return '';
-            } // Deep loop through all children and apply modifier
-
-
-            current.each(function () {
-              var result = svgOrFn(this);
-
-              var _this = result || this; // If modifier returns false, discard node
-
-
-              if (result === false) {
-                this.remove(); // If modifier returns new node, use it
-              } else if (result && this !== _this) {
-                this.replace(_this);
-              }
-            }, true);
-          } // Return outer or inner content
-
-
-          return outerHTML ? current.node.outerHTML : current.node.innerHTML;
-        } // Act as setter if we got a string
-        // The default for import is, that the current node is not replaced
+      key: "getEventTarget",
+      value: function getEventTarget() {
+        return this;
+      }
+    }, {
+      key: "removeEventListener",
+      value: function removeEventListener() {}
+    }]);
 
+    return EventTarget;
+  }(Base);
 
-        outerHTML = outerHTML == null ? false : outerHTML; // Create temporary holder
+  /* eslint no-new-func: "off" */
+  var subClassArray = function () {
+    try {
+      // try es6 subclassing
+      return Function('name', 'baseClass', '_constructor', ['baseClass = baseClass || Array', 'return {', '  [name]: class extends baseClass {', '    constructor (...args) {', '      super(...args)', '      _constructor && _constructor.apply(this, args)', '    }', '  }', '}[name]'].join('\n'));
+    } catch (e) {
+      // Use es5 approach
+      return function (name) {
+        var baseClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Array;
 
-        well = globals.document.createElementNS(ns, 'svg');
-        fragment = globals.document.createDocumentFragment(); // Dump raw svg
+        var _constructor = arguments.length > 2 ? arguments[2] : undefined;
 
-        well.innerHTML = svgOrFn; // Transplant nodes into the fragment
+        var Arr = function Arr() {
+          baseClass.apply(this, arguments);
+          _constructor && _constructor.apply(this, arguments);
+        };
 
-        for (len = well.children.length; len--;) {
-          fragment.appendChild(well.firstElementChild);
-        } // Add the whole fragment at once
+        Arr.prototype = Object.create(baseClass.prototype);
+        Arr.prototype.constructor = Arr;
 
+        Arr.prototype.map = function (fn) {
+          var arr = new Arr();
+          arr.push.apply(arr, Array.prototype.map.call(this, fn));
+          return arr;
+        };
 
-        return outerHTML ? this.replace(fragment) : this.add(fragment);
-      } // write svgjs data to the dom
+        return Arr;
+      };
+    }
+  }();
 
-    }, {
-      key: "writeDataToDom",
-      value: function writeDataToDom() {
-        // dump variables recursively
-        this.each(function () {
-          this.writeDataToDom();
+  var List = subClassArray('List', Array, function () {
+    var arr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+    // This catches the case, that native map tries to create an array with new Array(1)
+    if (typeof arr === 'number') return this;
+    this.length = 0;
+    this.push.apply(this, _toConsumableArray(arr));
+  });
+  extend(List, {
+    each: function each(fnOrMethodName) {
+      for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+        args[_key - 1] = arguments[_key];
+      }
+
+      if (typeof fnOrMethodName === 'function') {
+        this.forEach(function (el) {
+          fnOrMethodName.call(el, el);
+        });
+      } else {
+        return this.map(function (el) {
+          return el[fnOrMethodName].apply(el, args);
         });
-        return this;
       }
-    }]);
 
-    return Dom;
-  }(EventTarget);
-  extend(Dom, {
-    attr: attr
+      return this;
+    },
+    toArray: function toArray() {
+      return Array.prototype.concat.apply([], this);
+    }
   });
-  register(Dom);
-
-  var Doc = getClass(root);
-
-  var Element =
-  /*#__PURE__*/
-  function (_Dom) {
-    _inherits(Element, _Dom);
-
-    function Element(node, attrs) {
-      var _this;
 
-      _classCallCheck(this, Element);
+  List.extend = function (methods) {
+    methods = methods.reduce(function (obj, name) {
+      obj[name] = function () {
+        for (var _len2 = arguments.length, attrs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+          attrs[_key2] = arguments[_key2];
+        }
 
-      _this = _possibleConstructorReturn(this, _getPrototypeOf(Element).call(this, node, attrs)); // initialize data object
+        return this.each.apply(this, [name].concat(attrs));
+      };
 
-      _this.dom = {}; // create circular reference
+      return obj;
+    }, {});
+    extend(List, methods);
+  };
 
-      _this.node.instance = _assertThisInitialized(_assertThisInitialized(_this));
+  function noop() {} // Default animation values
 
-      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')) || {});
-      }
+  var timeline = {
+    duration: 400,
+    ease: '>',
+    delay: 0 // Default attribute values
 
-      return _this;
-    } // Move element by its center
+  };
+  var 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'
+  };
 
+  var defaults = /*#__PURE__*/Object.freeze({
+    noop: noop,
+    timeline: timeline,
+    attrs: attrs
+  });
 
-    _createClass(Element, [{
-      key: "center",
-      value: function center(x, y) {
-        return this.cx(x).cy(y);
-      } // Move by center over x-axis
+  var SVGArray = subClassArray('SVGArray', Array, function (arr) {
+    this.init(arr);
+  });
+  extend(SVGArray, {
+    init: function init(arr) {
+      // This catches the case, that native map tries to create an array with new Array(1)
+      if (typeof arr === 'number') return this;
+      this.length = 0;
+      this.push.apply(this, _toConsumableArray(this.parse(arr)));
+      return this;
+    },
+    toArray: function toArray() {
+      return Array.prototype.concat.apply([], this);
+    },
+    toString: function toString() {
+      return this.join(' ');
+    },
+    // Flattens the array if needed
+    valueOf: function valueOf() {
+      var ret = [];
+      ret.push.apply(ret, _toConsumableArray(this));
+      return ret;
+    },
+    // Parse whitespace separated string
+    parse: function parse() {
+      var array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+      // If already is an array, no need to parse it
+      if (array instanceof Array) return array;
+      return array.trim().split(delimiter).map(parseFloat);
+    },
+    clone: function clone() {
+      return new this.constructor(this);
+    },
+    toSet: function toSet() {
+      return new Set(this);
+    }
+  });
 
-    }, {
-      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
+  var SVGNumber =
+  /*#__PURE__*/
+  function () {
+    // Initialize
+    function SVGNumber() {
+      _classCallCheck(this, SVGNumber);
 
-    }, {
-      key: "cy",
-      value: function cy(y) {
-        return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2);
-      } // Get defs
+      this.init.apply(this, arguments);
+    }
 
-    }, {
-      key: "defs",
-      value: function defs() {
-        return this.doc().defs();
-      } // Get parent document
+    _createClass(SVGNumber, [{
+      key: "init",
+      value: function init(value, unit) {
+        unit = Array.isArray(value) ? value[1] : unit;
+        value = Array.isArray(value) ? value[0] : value; // initialize defaults
 
-    }, {
-      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
+        this.value = 0;
+        this.unit = unit || ''; // parse value
 
-    }, {
-      key: "height",
-      value: function height(_height) {
-        return this.attr('height', _height);
-      } // Checks whether the given point inside the bounding box of the element
+        if (typeof value === 'number') {
+          // ensure a valid numeric value
+          this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value;
+        } else if (typeof value === 'string') {
+          unit = value.match(numberAndUnit);
 
-    }, {
-      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
+          if (unit) {
+            // make value numeric
+            this.value = parseFloat(unit[1]); // normalize
 
-    }, {
-      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
+            if (unit[5] === '%') {
+              this.value /= 100;
+            } else if (unit[5] === 's') {
+              this.value *= 1000;
+            } // store unit
 
-    }, {
-      key: "parents",
-      value: function parents() {
-        var until = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : globals.document;
-        until = makeInstance(until);
-        var parents = new List();
-        var parent = this;
 
-        while ((parent = parent.parent()) && parent.node !== until.node && parent.node !== globals.document) {
-          parents.push(parent);
+            this.unit = unit[5];
+          }
+        } else {
+          if (value instanceof SVGNumber) {
+            this.value = value.valueOf();
+            this.unit = value.unit;
+          }
         }
 
-        return parents;
-      } // Get referenced element form attribute value
-
+        return this;
+      }
     }, {
-      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: "toString",
+      value: function toString() {
+        return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 : this.unit === 's' ? this.value / 1e3 : this.value) + this.unit;
+      }
     }, {
-      key: "setData",
-      value: function setData(o) {
-        this.dom = o;
-        return this;
-      } // Set element size to given width and height
-
+      key: "toJSON",
+      value: function toJSON() {
+        return this.toString();
+      }
+    }, {
+      key: "toArray",
+      value: function toArray() {
+        return [this.value, this.unit];
+      }
     }, {
-      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: "valueOf",
+      value: function valueOf() {
+        return this.value;
+      } // Add number
 
     }, {
-      key: "width",
-      value: function width(_width) {
-        return this.attr('width', _width);
-      } // write svgjs data to the dom
+      key: "plus",
+      value: function plus(number) {
+        number = new SVGNumber(number);
+        return new SVGNumber(this + number, this.unit || number.unit);
+      } // Subtract number
 
     }, {
-      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 _get(_getPrototypeOf(Element.prototype), "writeDataToDom", this).call(this);
-      } // Move over x-axis
+      key: "minus",
+      value: function minus(number) {
+        number = new SVGNumber(number);
+        return new SVGNumber(this - number, this.unit || number.unit);
+      } // Multiply number
 
     }, {
-      key: "x",
-      value: function x(_x) {
-        return this.attr('x', _x);
-      } // Move over y-axis
+      key: "times",
+      value: function times(number) {
+        number = new SVGNumber(number);
+        return new SVGNumber(this * number, this.unit || number.unit);
+      } // Divide number
 
     }, {
-      key: "y",
-      value: function y(_y) {
-        return this.attr('y', _y);
+      key: "divide",
+      value: function divide(number) {
+        number = new SVGNumber(number);
+        return new SVGNumber(this / number, this.unit || number.unit);
       }
     }]);
 
-    return Element;
-  }(Dom);
-  register(Element);
-
-  var Container =
-  /*#__PURE__*/
-  function (_Element) {
-    _inherits(Container, _Element);
+    return SVGNumber;
+  }();
 
-    function Container() {
-      _classCallCheck(this, Container);
+  var hooks = [];
+  function registerAttrHook(fn) {
+    hooks.push(fn);
+  } // Set svg element attribute
 
-      return _possibleConstructorReturn(this, _getPrototypeOf(Container).apply(this, arguments));
-    }
+  function attr(attr, val, ns) {
+    var _this = this;
 
-    _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
+    // act as full getter
+    if (attr == null) {
+      // get an object of attributes
+      attr = {};
+      val = this.node.attributes;
+      var _iteratorNormalCompletion = true;
+      var _didIteratorError = false;
+      var _iteratorError = undefined;
 
-        this.node.firstElementChild || this.remove();
-        return this;
+      try {
+        for (var _iterator = val[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+          var node = _step.value;
+          attr[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue;
+        }
+      } catch (err) {
+        _didIteratorError = true;
+        _iteratorError = err;
+      } finally {
+        try {
+          if (!_iteratorNormalCompletion && _iterator.return != null) {
+            _iterator.return();
+          }
+        } finally {
+          if (_didIteratorError) {
+            throw _iteratorError;
+          }
+        }
       }
-    }, {
-      key: "ungroup",
-      value: function ungroup(parent) {
-        parent = parent || this.parent();
-        this.each(function () {
-          return this.toParent(parent);
-        });
-        this.remove();
-        return this;
+
+      return attr;
+    } else if (attr instanceof Array) {
+      // loop through array and get all values
+      return attr.reduce(function (last, curr) {
+        last[curr] = _this.attr(curr);
+        return last;
+      }, {});
+    } else if (_typeof(attr) === 'object') {
+      // apply every attribute individually if an object is passed
+      for (val in attr) {
+        this.attr(val, attr[val]);
       }
-    }]);
+    } else if (val === null) {
+      // remove value
+      this.node.removeAttribute(attr);
+    } else if (val == null) {
+      // act as a getter if the first and only argument is not an object
+      val = this.node.getAttribute(attr);
+      return val == null ? attrs[attr] : isNumber.test(val) ? parseFloat(val) : val;
+    } else {
+      // Loop through hooks and execute them to convert value
+      val = hooks.reduce(function (_val, hook) {
+        return hook(attr, _val, _this);
+      }, val); // ensure correct numeric values (also accepts NaN and Infinity)
 
-    return Container;
-  }(Element);
-  register(Container);
+      if (typeof val === 'number') {
+        val = new SVGNumber(val);
+      } else if (Color.isColor(val)) {
+        // ensure full hex color
+        val = new Color(val);
+      } else if (val.constructor === Array) {
+        // Check for plain arrays and parse array values
+        val = new SVGArray(val);
+      } // if the passed attribute is leading...
 
-  var Defs =
-  /*#__PURE__*/
-  function (_Container) {
-    _inherits(Defs, _Container);
 
-    function Defs(node) {
-      _classCallCheck(this, Defs);
+      if (attr === 'leading') {
+        // ... call the leading method instead
+        if (this.leading) {
+          this.leading(val);
+        }
+      } else {
+        // set given attribute on node
+        typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString()) : this.node.setAttribute(attr, val.toString());
+      } // rebuild if required
 
-      return _possibleConstructorReturn(this, _getPrototypeOf(Defs).call(this, nodeOrNew('defs', node), node));
-    }
 
-    _createClass(Defs, [{
-      key: "flatten",
-      value: function flatten() {
-        return this;
-      }
-    }, {
-      key: "ungroup",
-      value: function ungroup() {
-        return this;
+      if (this.rebuild && (attr === 'font-size' || attr === 'x')) {
+        this.rebuild();
       }
-    }]);
+    }
 
-    return Defs;
-  }(Container);
-  register(Defs);
+    return this;
+  }
 
-  var Doc$1 =
+  var Dom =
   /*#__PURE__*/
-  function (_Container) {
-    _inherits(Doc, _Container);
+  function (_EventTarget) {
+    _inherits(Dom, _EventTarget);
 
-    function Doc(node) {
-      var _this;
+    function Dom(node, attrs) {
+      var _this2;
 
-      _classCallCheck(this, Doc);
+      _classCallCheck(this, Dom);
 
-      _this = _possibleConstructorReturn(this, _getPrototypeOf(Doc).call(this, nodeOrNew('svg', node), node));
+      _this2 = _possibleConstructorReturn(this, _getPrototypeOf(Dom).call(this, node));
+      _this2.node = node;
+      _this2.type = node.nodeName;
 
-      _this.namespace();
+      if (attrs && node !== attrs) {
+        _this2.attr(attrs);
+      }
 
-      return _this;
-    }
+      return _this2;
+    } // Add given element at a position
 
-    _createClass(Doc, [{
-      key: "isRoot",
-      value: function isRoot() {
-        return !this.node.parentNode || !(this.node.parentNode instanceof globals.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
+    _createClass(Dom, [{
+      key: "add",
+      value: function add(element, i) {
+        element = makeInstance(element);
 
-    }, {
-      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
+        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]);
+        }
 
-    }, {
-      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
+        return this;
+      } // Add element to given container and return self
 
     }, {
-      key: "parent",
-      value: function parent(type) {
-        if (this.isRoot()) {
-          return this.node.parentNode.nodeName === '#document' ? null : adopt(this.node.parentNode);
-        }
+      key: "addTo",
+      value: function addTo(parent) {
+        return makeInstance(parent).put(this);
+      } // Returns all child elements
+
+    }, {
+      key: "children",
+      value: function children() {
+        return new List(map(this.node.children, function (node) {
+          return adopt(node);
+        }));
+      } // Remove all elements in this container
 
-        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: wrapWithAttrCheck(function () {
-        return this.put(new Doc$1());
-      })
-    }
-  });
-  register(Doc$1, 'Doc', true);
+        } // remove defs reference
 
-  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
-      };
-    }
 
-    if (!parser.nodes.svg.node.parentNode) {
-      var b = globals.document.body || globals.document.documentElement;
-      parser.nodes.svg.addTo(b);
-    }
+        delete this._defs;
+        return this;
+      } // Clone element
 
-    return parser.nodes;
-  }
+    }, {
+      key: "clone",
+      value: function clone() {
+        // write dom data to the dom so the clone can pickup the data
+        this.writeDataToDom(); // clone element and assign new id
 
-  var Point =
-  /*#__PURE__*/
-  function () {
-    // Initialize
-    function Point() {
-      _classCallCheck(this, Point);
+        return assignNewId(this.node.cloneNode(true));
+      } // Iterates over all children and invokes a given block
 
-      this.init.apply(this, arguments);
-    }
+    }, {
+      key: "each",
+      value: function each(block, deep) {
+        var children = this.children();
+        var i, il;
 
-    _createClass(Point, [{
-      key: "init",
-      value: function init(x, y) {
-        var source;
-        var base = {
-          x: 0,
-          y: 0 // ensure source as object
+        for (i = 0, il = children.length; i < il; i++) {
+          block.apply(children[i], [i, children]);
 
-        };
-        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
+          if (deep) {
+            children[i].each(block, deep);
+          }
+        }
 
-        };
-        this.x = source.x == null ? base.x : source.x;
-        this.y = source.y == null ? base.y : source.y;
         return this;
-      } // Clone point
+      } // Get first child
 
     }, {
-      key: "clone",
-      value: function clone() {
-        return new Point(this);
-      } // Convert to native SVGPoint
+      key: "first",
+      value: function first() {
+        return adopt(this.node.firstChild);
+      } // Get a element at the given index
 
     }, {
-      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: "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: "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
+      key: "has",
+      value: function has(element) {
+        return this.index(element) >= 0;
+      } // Get / set id
 
-        return new Point(x, y);
-      }
     }, {
-      key: "toArray",
-      value: function toArray() {
-        return [this.x, this.y];
-      }
-    }]);
+      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 Point;
-  }();
-  registerMethods({
-    Element: {
-      // Get point
-      point: function point(x, y) {
-        return new Point(x, y).transform(this.screenCTM().inverse());
-      }
-    }
-  });
 
-  var abcdef = 'abcdef'.split('');
+        return this.attr('id', _id);
+      } // Gets index of given element
 
-  function closeEnough(a, b, threshold) {
-    return Math.abs(b - a) < (threshold || 1e-6);
-  }
+    }, {
+      key: "index",
+      value: function index(element) {
+        return [].slice.call(this.node.childNodes).indexOf(element.node);
+      } // Get the last child
 
-  var Matrix =
-  /*#__PURE__*/
-  function () {
-    function Matrix() {
-      _classCallCheck(this, Matrix);
+    }, {
+      key: "last",
+      value: function last() {
+        return adopt(this.node.lastChild);
+      } // matches the element vs a css selector
 
-      this.init.apply(this, arguments);
-    } // Initialize
+    }, {
+      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 parent element instance
 
+    }, {
+      key: "parent",
+      value: function parent(type) {
+        var parent = this; // check for parent
 
-    _createClass(Matrix, [{
-      key: "init",
-      value: function init(source) {
-        var base = Matrix.fromArray([1, 0, 0, 1, 0, 0]); // ensure source as object
+        if (!parent.node.parentNode) return null; // get parent element
 
-        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
+        parent = adopt(parent.node.parentNode);
+        if (!type) return parent; // loop trough ancestors if type is given
 
-        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;
-        return this;
-      } // Clones this matrix
+        while (parent && parent.node instanceof globals.window.SVGElement) {
+          // FIXME: That shouldnt be neccessary
+          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: "clone",
-      value: function clone() {
-        return new Matrix(this);
-      } // Transform a matrix into another matrix by manipulating the space
+      key: "put",
+      value: function put(element, i) {
+        this.add(element, i);
+        return element;
+      } // Add element to given container and return container
 
     }, {
-      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
-
+      key: "putIn",
+      value: function putIn(parent) {
+        return makeInstance(parent).add(this);
+      } // Remove element
 
-        var t = Matrix.formatTransforms(o);
-        var current = this;
+    }, {
+      key: "remove",
+      value: function remove() {
+        if (this.parent()) {
+          this.parent().removeElement(this);
+        }
 
-        var _transform = new Point(t.ox, t.oy).transform(current),
-            ox = _transform.x,
-            oy = _transform.y; // Construct the resulting matrix
+        return this;
+      } // Remove a given child
 
+    }, {
+      key: "removeElement",
+      value: function removeElement(element) {
+        this.node.removeChild(element.node);
+        return this;
+      } // Replace this with element
 
-        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
+    }, {
+      key: "replace",
+      value: function replace(element) {
+        element = makeInstance(element);
+        this.node.parentNode.replaceChild(element.node, this.node);
+        return element;
+      }
+    }, {
+      key: "round",
+      value: function round() {
+        var precision = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
+        var map$$1 = arguments.length > 1 ? arguments[1] : undefined;
+        var factor = Math.pow(10, precision);
+        var attrs = this.attr(); // If we have no map, build one from attrs
 
-        if (isFinite(t.px) || isFinite(t.py)) {
-          var origin = new Point(ox, oy).transform(transformer); // TODO: Replace t.px with isFinite(t.px)
+        if (!map$$1) {
+          map$$1 = Object.keys(attrs);
+        } // Holds rounded attributes
 
-          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
 
+        var newAttrs = {};
+        map$$1.forEach(function (key) {
+          newAttrs[key] = Math.round(attrs[key] * factor) / factor;
+        });
+        this.attr(newAttrs);
+        return this;
+      } // Return id on string conversion
 
-        transformer.translateO(t.tx, t.ty);
-        return transformer;
-      } // Applies a matrix defined by its affine parameters
+    }, {
+      key: "toString",
+      value: function toString() {
+        return this.id();
+      } // Import raw svg
 
     }, {
-      key: "compose",
-      value: function compose(o) {
-        if (o.origin) {
-          o.originX = o.origin[0];
-          o.originY = o.origin[1];
-        } // Get the parameters
+      key: "svg",
+      value: function svg(svgOrFn, outerHTML) {
+        var well, len, fragment;
 
+        if (svgOrFn === false) {
+          outerHTML = false;
+          svgOrFn = null;
+        } // act as getter if no svg string is given
 
-        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
+        if (svgOrFn == null || typeof svgOrFn === 'function') {
+          // The default for exports is, that the outerNode is included
+          outerHTML = outerHTML == null ? true : outerHTML; // write svgjs data to the dom
 
-    }, {
-      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
+          this.writeDataToDom();
+          var current = this; // An export modifier was passed
 
-        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
+          if (svgOrFn != null) {
+            current = adopt(current.node.cloneNode(true)); // If the user wants outerHTML we need to process this node, too
 
-        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
+            if (outerHTML) {
+              var result = svgOrFn(current);
+              current = result || current; // The user does not want this node? Well, then he gets nothing
 
-        var lam = (a * c + b * d) / determinant;
-        var sy = c * sx / (lam * a - b) || d * sx / (lam * b + a); // Use the translations
+              if (result === false) return '';
+            } // Deep loop through all children and apply modifier
 
-        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
+            current.each(function () {
+              var result = svgOrFn(this);
 
-    }, {
-      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
+              var _this = result || this; // If modifier returns false, discard node
 
-    }, {
-      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
+              if (result === false) {
+                this.remove(); // If modifier returns new node, use it
+              } else if (result && this !== _this) {
+                this.replace(_this);
+              }
+            }, true);
+          } // Return outer or inner content
 
-        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
+          return outerHTML ? current.node.outerHTML : current.node.innerHTML;
+        } // Act as setter if we got a string
+        // The default for import is, that the current node is not replaced
 
-        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
+        outerHTML = outerHTML == null ? false : outerHTML; // Create temporary holder
 
-    }, {
-      key: "scale",
-      value: function scale(x, y, cx, cy) {
-        var _this$clone;
+        well = globals.document.createElementNS(ns, 'svg');
+        fragment = globals.document.createDocumentFragment(); // Dump raw svg
 
-        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;
+        well.innerHTML = svgOrFn; // Transplant nodes into the fragment
 
-        // Support uniform scaling
-        if (arguments.length === 3) {
-          cy = cx;
-          cx = y;
-          y = x;
-        }
+        for (len = well.children.length; len--;) {
+          fragment.appendChild(well.firstElementChild);
+        } // Add the whole fragment at once
 
-        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
+        return outerHTML ? this.replace(fragment) : this.add(fragment);
+      } // write svgjs data to the dom
 
     }, {
-      key: "flip",
-      value: function flip(axis, around) {
-        return this.clone().flipO(axis, around);
+      key: "writeDataToDom",
+      value: function writeDataToDom() {
+        // dump variables recursively
+        this.each(function () {
+          this.writeDataToDom();
+        });
+        return this;
       }
-    }, {
-      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
+    return Dom;
+  }(EventTarget);
+  extend(Dom, {
+    attr: attr
+  });
+  register(Dom);
 
-    }, {
-      key: "skew",
-      value: function skew(x, y, cx, cy) {
-        var _this$clone2;
+  var Doc = getClass(root);
 
-        return (_this$clone2 = this.clone()).skewO.apply(_this$clone2, arguments);
+  var Element =
+  /*#__PURE__*/
+  function (_Dom) {
+    _inherits(Element, _Dom);
+
+    function Element(node, attrs) {
+      var _this;
+
+      _classCallCheck(this, Element);
+
+      _this = _possibleConstructorReturn(this, _getPrototypeOf(Element).call(this, node, attrs)); // initialize data object
+
+      _this.dom = {}; // create circular reference
+
+      _this.node.instance = _assertThisInitialized(_assertThisInitialized(_this));
+
+      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')) || {});
       }
-    }, {
-      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
+      return _this;
+    } // Move element by its center
 
 
-        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
+    _createClass(Element, [{
+      key: "center",
+      value: function center(x, y) {
+        return this.cx(x).cy(y);
+      } // Move by center over x-axis
 
     }, {
-      key: "skewX",
-      value: function skewX(x, cx, cy) {
-        return this.skew(x, 0, cx, cy);
-      }
+      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: "skewXO",
-      value: function skewXO(x, cx, cy) {
-        return this.skewO(x, 0, cx, cy);
-      } // SkewY
+      key: "cy",
+      value: function cy(y) {
+        return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2);
+      } // Get defs
 
     }, {
-      key: "skewY",
-      value: function skewY(y, cx, cy) {
-        return this.skew(0, y, cx, cy);
+      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: "skewYO",
-      value: function skewYO(y, cx, cy) {
-        return this.skewO(0, y, cx, cy);
-      } // Transform around a center point
+      key: "getEventHolder",
+      value: function getEventHolder() {
+        return this;
+      } // Set height of element
 
     }, {
-      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: "height",
+      value: function height(_height) {
+        return this.attr('height', _height);
+      } // Checks whether the given point inside the bounding box of the element
+
     }, {
-      key: "around",
-      value: function around(cx, cy, matrix) {
-        return this.clone().aroundO(cx, cy, matrix);
-      } // Convert to native SVGMatrix
+      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: "native",
-      value: function native() {
-        // create new matrix
-        var matrix = parser().svg.node.createSVGMatrix(); // update with current values
+      key: "parents",
+      value: function parents() {
+        var until = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : globals.document;
+        until = makeInstance(until);
+        var parents = new List();
+        var parent = this;
 
-        for (var i = abcdef.length - 1; i >= 0; i--) {
-          matrix[abcdef[i]] = this[abcdef[i]];
+        while ((parent = parent.parent()) && parent.node !== until.node && parent.node !== globals.document) {
+          parents.push(parent);
         }
 
-        return matrix;
-      } // Check if two matrices are equal
+        return parents;
+      } // Get referenced element form attribute value
 
     }, {
-      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: "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: "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
+      key: "setData",
+      value: function setData(o) {
+        this.dom = o;
+        return this;
+      } // Set element size to given width and height
 
-        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: "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: "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
+      key: "width",
+      value: function width(_width) {
+        return this.attr('width', _width);
+      } // write svgjs data to the dom
 
-        o.a = a;
-        o.b = b;
-        o.c = c;
-        o.d = d;
-        o.e = e;
-        o.f = f;
-        return o;
-      }
-    }]);
+    }, {
+      key: "writeDataToDom",
+      value: function writeDataToDom() {
+        // remove previously set data
+        this.node.removeAttribute('svgjs:data');
 
-    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
-           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);
+        if (Object.keys(this.dom).length) {
+          this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428
         }
 
-        return new Matrix(this.node.getScreenCTM());
+        return _get(_getPrototypeOf(Element.prototype), "writeDataToDom", this).call(this);
+      } // Move over x-axis
+
+    }, {
+      key: "x",
+      value: function x(_x) {
+        return this.attr('x', _x);
+      } // Move over y-axis
+
+    }, {
+      key: "y",
+      value: function y(_y) {
+        return this.attr('y', _y);
       }
-    }
+    }]);
+
+    return Element;
+  }(Dom);
+  extend(Element, {
+    bbox: bbox,
+    rbox: rbox,
+    point: point,
+    ctm: ctm,
+    screenCTM: screenCTM
   });
+  register(Element);
 
   var sugar = {
     stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],
@@ -2935,238 +2893,95 @@ var SVG = (function () {
       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);
     }
   });
-  registerMethods('Text', {
-    ax: function ax(x) {
-      return this.attr('x', x);
-    },
-    ay: function ay(y) {
-      return this.attr('y', y);
-    },
-    amove: function amove(x, y) {
-      return this.ax(x).ay(y);
-    }
-  }); // Add events to elements
-
-  var methods$1 = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) {
-    // add event to Element
-    var fn = function fn(f) {
-      if (f === null) {
-        off(this, event);
-      } else {
-        on(this, event, f);
-      }
-
-      return this;
-    };
-
-    last[event] = fn;
-    return last;
-  }, {});
-  registerMethods('Element', methods$1);
-
-  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 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
-
-  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
-
-  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;
-    }
-
-    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
-
-
-    var cleanRelative = relative === true ? this : relative || false;
-    var result = new Matrix(cleanRelative).transform(o);
-    return this.attr('transform', result);
-  }
-  registerMethods('Element', {
-    untransform: untransform,
-    matrixify: matrixify,
-    toParent: toParent,
-    toDoc: toDoc,
-    transform: transform
-  });
-
-  function isNulledBox(box) {
-    return !box.w && !box.h && !box.x && !box.y;
-  }
-
-  function domContains(node) {
-    return (globals.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(globals.document.documentElement, node);
-  }
-
-  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;
-        return this;
-      } // 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 += globals.window.pageXOffset;
-        this.y += globals.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);
+  registerMethods('Text', {
+    ax: function ax(x) {
+      return this.attr('x', x);
+    },
+    ay: function ay(y) {
+      return this.attr('y', y);
+    },
+    amove: function amove(x, y) {
+      return this.ax(x).ay(y);
+    }
+  }); // Add events to elements
+
+  var methods$1 = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) {
+    // add event to Element
+    var fn = function fn(f) {
+      if (f === null) {
+        off(this, event);
+      } else {
+        on(this, event, f);
       }
-    }]);
 
-    return Box;
-  }();
+      return this;
+    };
 
-  function getBox(cb) {
-    var box;
+    last[event] = fn;
+    return last;
+  }, {});
+  registerMethods('Element', methods$1);
 
-    try {
-      box = cb(this.node);
+  function untransform() {
+    return this.attr('transform', null);
+  } // merge the whole transformation chain into one matrix and returns it
 
-      if (isNulledBox(box) && !domContains(this.node)) {
-        throw new Error('Element not in the dom');
-      }
-    } catch (e) {
-      try {
-        var clone = this.clone().addTo(parser().svg).show();
-        box = cb(clone.node);
-        clone.remove();
-      } catch (e) {
-        throw new Error('Getting a bounding box of element "' + this.node.nodeName + '" is not possible');
+  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 box;
-  }
+      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
 
-  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();
-      }
-    },
-    viewbox: {
-      viewbox: function viewbox(x, y, width, height) {
-        // act as getter
-        if (x == null) return new Box(this.attr('viewBox')); // act as setter
+  function toParent(parent) {
+    if (this === parent) return this;
+    var ctm$$1 = this.screenCTM();
+    var pCtm = parent.screenCTM().inverse();
+    this.addTo(parent).untransform().transform(pCtm.multiply(ctm$$1));
+    return this;
+  } // same as above with parent equals root-svg
 
-        return this.attr('viewBox', new Box(x, y, width, height));
-      }
+  function toDoc() {
+    return this.toParent(this.doc());
+  } // Add transformations
+
+  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;
     }
+
+    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
+
+
+    var cleanRelative = relative === true ? this : relative || false;
+    var result = new Matrix(cleanRelative).transform(o);
+    return this.attr('transform', result);
+  }
+  registerMethods('Element', {
+    untransform: untransform,
+    matrixify: matrixify,
+    toParent: toParent,
+    toDoc: toDoc,
+    transform: transform
   });
 
   function rx(rx) {
@@ -3282,6 +3097,152 @@ var SVG = (function () {
   });
   register(Circle);
 
+  var Container =
+  /*#__PURE__*/
+  function (_Element) {
+    _inherits(Container, _Element);
+
+    function Container() {
+      _classCallCheck(this, Container);
+
+      return _possibleConstructorReturn(this, _getPrototypeOf(Container).apply(this, arguments));
+    }
+
+    _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);
+  register(Container);
+
+  var Defs =
+  /*#__PURE__*/
+  function (_Container) {
+    _inherits(Defs, _Container);
+
+    function Defs(node) {
+      _classCallCheck(this, Defs);
+
+      return _possibleConstructorReturn(this, _getPrototypeOf(Defs).call(this, nodeOrNew('defs', node), node));
+    }
+
+    _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), node));
+
+      _this.namespace();
+
+      return _this;
+    }
+
+    _createClass(Doc, [{
+      key: "isRoot",
+      value: function isRoot() {
+        return !this.node.parentNode || !(this.node.parentNode instanceof globals.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: wrapWithAttrCheck(function () {
+        return this.put(new Doc$1());
+      })
+    }
+  });
+  register(Doc$1, 'Doc', true);
+
   var Ellipse =
   /*#__PURE__*/
   function (_Shape) {
@@ -3440,7 +3401,7 @@ var SVG = (function () {
       }
     }, {
       key: "bbox",
-      value: function bbox() {
+      value: function bbox$$1() {
         return new Box();
       }
     }]);
@@ -3515,7 +3476,7 @@ var SVG = (function () {
       }
     }, {
       key: "bbox",
-      value: function bbox() {
+      value: function bbox$$1() {
         return new Box();
       }
     }]);
@@ -6067,12 +6028,12 @@ var SVG = (function () {
       });
       return this;
     },
-    zoom: function zoom(level, point) {
+    zoom: function zoom(level, point$$1) {
       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);
+        this.element().zoom(morpher.at(pos), point$$1);
         return morpher.done();
       });
       return this;
index b36ea8234330032daf1a4b1f670e4d3bd7107235..9afedc6b76d222c34e989a5fc5a7962035c4ecaf 100644 (file)
@@ -10,13 +10,6 @@ describe('Element', function() {
     expect(rect.node.instance).toBe(rect)
   })
 
-  describe('native()', function() {
-    it('returns the node reference', function() {
-      var rect = draw.rect(100,100)
-      expect(rect.native()).toBe(rect.node)
-    })
-  })
-
   describe('attr()', function() {
     var rect
 
index 42ac1fe7ee401d764998fa326aca9ab8a1062b32..b6c0348d5d70d929bb8c73e4bf1a86859415888f 100644 (file)
@@ -372,11 +372,4 @@ describe('Matrix', function() {
       expect(matrix.f).toBeCloseTo(-81.2931393017)
     })
   })
-
-  describe('native()', function() {
-    it('returns the node reference', function() {
-      expect(new SVG.Matrix().native() instanceof window.SVGMatrix).toBeTruthy()
-    })
-  })
-
 })
index 768d7e9362fe06b811240409df740e358187a5c8..8be944a049afbefb0f89ee7192e1bb7f494d8962 100644 (file)
@@ -60,16 +60,6 @@ describe('Point', function() {
         expect(point.y).toBe(4)
       })
     })
-
-    describe('with native SVGPoint given', function() {
-      it('creates a point from native SVGPoint', function() {
-        var point = new SVG.Point(new SVG.Point(2,4).native())
-
-        expect(point.x).toBe(2)
-        expect(point.y).toBe(4)
-      })
-    })
-
   })
 
   describe('clone()', function() {
index 6d35f1ef4f121955799a6a0ac7126ee9b7aded36..55d58583f0df5a64dd0e0830bb4dfb89ac66e4c7 100644 (file)
@@ -137,11 +137,6 @@ export default class Dom extends EventTarget {
     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
-  native () {
-    return this.node
-  }
-
   // Returns the parent element instance
   parent (type) {
     var parent = this
index 3b96bf4a17c0591d2755010ebd40534f3d3326cc..456ddad541fa7846c82234eb0d635e3ce00448bb 100644 (file)
@@ -1,5 +1,14 @@
-import { getClass, makeInstance, register, root } from '../utils/adopter.js'
+import { bbox, rbox } from '../types/Box.js'
+import { ctm, screenCTM } from '../types/Matrix.js'
+import {
+  extend,
+  getClass,
+  makeInstance,
+  register,
+  root
+} from '../utils/adopter.js'
 import { globals } from '../utils/window.js'
+import { point } from '../types/Point.js'
 import { proportionalSize } from '../utils/utils.js'
 import { reference } from '../modules/core/regex.js'
 import Dom from './Dom.js'
@@ -145,4 +154,8 @@ export default class Element extends Dom {
   }
 }
 
+extend(Element, {
+  bbox, rbox, point, ctm, screenCTM
+})
+
 register(Element)
index ccbbc54a394219b6f91dd62034896cd5fa4afe3d..1ff2380e05624870c0f1f7954ca2b46bf483ec56 100644 (file)
@@ -1,10 +1,10 @@
-import Doc from '../../elements/Doc.js'
 import { globals } from '../../utils/window.js'
+import { makeInstance } from '../../utils/adopter.js'
 
 export default function parser () {
   // Reuse cached element if possible
   if (!parser.nodes) {
-    let svg = new Doc().size(2, 0)
+    let svg = makeInstance().size(2, 0)
     svg.node.cssText = [
       'opacity: 0',
       'position: absolute',
index 8c1c4cadd6c50ac5ebf24072dfeba7dd00144489..e55f1146939ef25c8a61888df0a8e2476dec031e 100644 (file)
@@ -125,19 +125,17 @@ function getBox (cb) {
   return box
 }
 
+export function bbox () {
+  return new Box(getBox.call(this, (node) => node.getBBox()))
+}
+
+export function rbox (el) {
+  let box = new Box(getBox.call(this, (node) => node.getBoundingClientRect()))
+  if (el) return box.transform(el.screenCTM().inverse())
+  return box.addOffset()
+}
+
 registerMethods({
-  Element: {
-    // Get bounding box
-    bbox () {
-      return new Box(getBox.call(this, (node) => node.getBBox()))
-    },
-
-    rbox (el) {
-      let box = new Box(getBox.call(this, (node) => node.getBoundingClientRect()))
-      if (el) return box.transform(el.screenCTM().inverse())
-      return box.addOffset()
-    }
-  },
   viewbox: {
     viewbox (x, y, width, height) {
       // act as getter
index ee12488dce756088caed15a3ca51ca11f61168ca..a1eb317e27b297ee136403d3183346bef1e1db26 100644 (file)
@@ -1,12 +1,7 @@
 import { delimiter } from '../modules/core/regex.js'
 import { radians } from '../utils/utils.js'
-import { registerMethods } from '../utils/methods.js'
 import Element from '../elements/Element.js'
 import Point from './Point.js'
-import parser from '../modules/core/parser.js'
-
-// Create matrix array for looping
-const abcdef = 'abcdef'.split('')
 
 function closeEnough (a, b, threshold) {
   return Math.abs(b - a) < (threshold || 1e-6)
@@ -379,19 +374,6 @@ export default class Matrix {
     return this.clone().aroundO(cx, cy, matrix)
   }
 
-  // Convert to native SVGMatrix
-  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
   equals (other) {
     var comp = new Matrix(other)
@@ -499,26 +481,20 @@ export default class Matrix {
   }
 }
 
-registerMethods({
-  Element: {
-    // Get current matrix
-    ctm () {
-      return new Matrix(this.node.getCTM())
-    },
-
-    // Get current screen matrix
-    screenCTM () {
-      /* 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())
-    }
-  }
-})
+export function ctm () {
+  return new Matrix(this.node.getCTM())
+}
+
+export function screenCTM () {
+  /* 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())
+}
index 6a2b968b5666f1edb682bcc1d0addb01712b8cb5..27d81ea3f3de265235af3fc826f31525db66a144 100644 (file)
@@ -1,6 +1,3 @@
-import { registerMethods } from '../utils/methods.js'
-import parser from '../modules/core/parser.js'
-
 export default class Point {
   // Initialize
   constructor (...args) {
@@ -28,17 +25,6 @@ export default class Point {
     return new Point(this)
   }
 
-  // Convert to native SVGPoint
-  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
   transform (m) {
     // Perform the matrix multiplication
@@ -54,11 +40,6 @@ export default class Point {
   }
 }
 
-registerMethods({
-  Element: {
-    // Get point
-    point: function (x, y) {
-      return new Point(x, y).transform(this.screenCTM().inverse())
-    }
-  }
-})
+export function point (x, y) {
+  return new Point(x, y).transform(this.screenCTM().inverse())
+}