]> source.dussan.org Git - svg.js.git/commitdiff
resolve circular references and make example working again
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Sat, 27 Oct 2018 18:43:35 +0000 (20:43 +0200)
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Sat, 27 Oct 2018 18:43:35 +0000 (20:43 +0200)
67 files changed:
abilities [new file with mode: 0644]
dirty.html
package.json
src/A.js
src/Animator.js
src/Bare.js
src/Base.js [new file with mode: 0644]
src/Box.js
src/Circle.js
src/ClipPath.js
src/Color.js
src/Container.js [deleted file]
src/Controller.js
src/Defs.js
src/Doc.js
src/Element.js
src/Ellipse.js
src/EventTarget.js
src/G.js
src/Gradient.js
src/HtmlNode.js
src/Image.js
src/Line.js
src/Marker.js
src/Mask.js
src/Matrix.js
src/Morphable.js
src/Parent.js
src/Path.js
src/PathArray.js
src/Pattern.js
src/Point.js
src/Polygon.js
src/Polyline.js
src/Rect.js
src/Runner.js
src/SVGArray.js
src/SVGNumber.js
src/Shape.js [deleted file]
src/Stop.js
src/Symbol.js
src/Text.js
src/TextPath.js
src/Timeline.js
src/Tspan.js [new file with mode: 0644]
src/Use.js
src/adopter.js [new file with mode: 0644]
src/attr.js
src/classes.js
src/containers.js [new file with mode: 0644]
src/css.js
src/data.js
src/default.js [deleted file]
src/elements.js [new file with mode: 0644]
src/event.js
src/flatten.js [deleted file]
src/fx.js [deleted file]
src/helpers.js
src/memory.js
src/parser.js
src/poly.js [new file with mode: 0644]
src/selector.js
src/set.js [new file with mode: 0644]
src/svg.js
src/textable.js [new file with mode: 0644]
src/tools.js
src/utilities.js [deleted file]

diff --git a/abilities b/abilities
new file mode 100644 (file)
index 0000000..4d5aa11
--- /dev/null
+++ b/abilities
@@ -0,0 +1,156 @@
+
+
+>> Element <<
+  Stop
+  Bare
+  >> Parent <<
+    Text
+      TextPath
+    >> Container<<
+      Doc
+      G
+      Symbol
+      Defs
+      ClipPath
+      Mask
+      A
+      Gradient
+  Shape
+    Rect
+    Circle
+    Path
+    Ellipse
+    Polygon
+    Polyline
+    Line
+    Image
+
+Animate
+Box
+Color
+Controller
+
+
+=====================================================================
+
+
+classes:
+
+  // MetaData
+  Title
+
+  // Parents
+  SVG
+  G
+
+  // Elements
+  Rect
+  Circle
+  Path
+  Ellipse
+  Polygon
+  Polyline
+  Line
+  Image
+
+  // Text Stuff
+  TSpan
+  TextPath
+
+  // Data Type
+  Box
+  Matrix
+  SVGNumber
+  SVGArray
+  PointArray
+  PathArray
+  Color
+  Controller
+
+abilities:
+  Animate
+  Container
+  Event
+  Doc
+  Movements
+  Dom
+  Create
+
+
+
+
+// DocAbility.js
+
+import SVG from 'Svg.js'
+export default function ( ...worksOn ) {
+  let workSet = new Set(worksOn)
+  return {
+
+    doc: function () {
+      return this.parent(SVG)
+    }
+
+  }
+}
+
+-> svg.js -> DocAbility.js -> 'Svg.js'
+
+extend ( [ Rect ], DocAbility() )
+
+
+
+// DomAbility.js
+
+import {makeInstance} from 'helpers.js'
+
+export default function ( ...worksOn ) {
+  let workSet = new Set(worksOn)
+  let maker = makeInstance(workSet)
+
+  return {
+
+    addTo (parent) {
+      return maker(parent).put(this)
+    }
+
+  }
+}
+
+class Rect {
+
+
+  static tagName = 'Rect'
+}
+
+
+
+
+new SVG[capitalize[node.nodeName]]
+
+
+
+new SVG.Doc() -> <svg xml="">......</svg>
+
+// SVG.js
+
+extend( [ ...Parents ], Container(...Elements, ...Parents) ) )
+extend( [ Text ], Container( TSpan, TextPath ) )
+
+
+
+Element.js
+
+new Element ()
+
+export default class Element {
+
+  static
+
+  static
+
+}
+
+Rect.js
+
+import Element from Element.js
+export default class Rect extends Element
index f3e739aced89b3360da11b2fa8b449da6475f731..5f605de0131f96a01fba4c9f85ed148098ad0e27 100644 (file)
@@ -4,14 +4,7 @@
 <head>
   <meta charset="utf-8">
   <title></title>
-  <script type="text/javascript" src="dist/svg.js"></script>
-  <script type="text/javascript" src="src/helpers.js"></script>
-  <script type="text/javascript" src="src/transform.js"></script>
-  <script type="text/javascript" src="src/matrix.js"></script>
-  <script type="text/javascript" src="src/morph.js"></script>
-  <script type="text/javascript" src="src/runner.js"></script>
-  <script type="text/javascript" src="src/timeline.js"></script>
-  <script type="text/javascript" src="src/controller.js"></script>
+  <!--<script type="text/javascript" src="dist/svg.js"></script>-->
 
 
 
 </svg>
 
 <!-- Modifying the svg -->
-<script>
+<script type="module">
+
+import SVG from './src/svg.js'
+
+window.SVG = SVG
 
 let rect = SVG('rect').hide()
 let {sin, PI: pi, round, sqrt} = Math
index c718bec154233a76cebdfa4d9831275df0b679b4..645cdd3454b23ce7ac4f13604efdf097d09e17f7 100644 (file)
@@ -60,7 +60,8 @@
     "build:dev": "gulp --dont-break",
     "test": "karma start .config/karma.conf.js --single-run",
     "test:dots": "karma start .config/karma.conf.js --single-run --reporters dots",
-    "test:quick": "karma start .config/karma.quick.js"
+    "test:quick": "karma start .config/karma.quick.js",
+    "server": "http-server ./ -d"
   },
   "devDependencies": {
     "@babel/core": "^7.1.2",
index 72e13e400bb6b3a900f936b35526b8031d93e143..eb95dc0acf2fdf6190286e08eb6ee41b1c0727de 100644 (file)
--- a/src/A.js
+++ b/src/A.js
@@ -1,10 +1,10 @@
-import {Container, Element} from './classes.js'
-import {nodeOrNew, addFactory} from './tools.js'
+import Base from './Base.js'
+import {nodeOrNew} from './tools.js'
 import {xlink} from './namespaces.js'
 
-export default class A extends Container {
+export default class A extends Base{
   constructor (node) {
-    super(nodeOrNew('a', node))
+    super(nodeOrNew('a', node), A)
   }
 
   // Link url
@@ -18,22 +18,23 @@ export default class A extends Container {
   }
 }
 
-addFactory(Container, {
-  // Create a hyperlink element
-  link: function (url) {
-    return this.put(new A()).to(url)
-  }
-})
+A.constructors = {
+  Container: {
+    // Create a hyperlink element
+    link: function (url) {
+      return this.put(new A()).to(url)
+    }
+  },
+  Element: {
+    // Create a hyperlink element
+    linkTo: function (url) {
+      var link = new A()
 
-addFactory(Element, {
-  // Create a hyperlink element
-  linkTo: function (url) {
-    var link = new A()
+      if (typeof url === 'function') { url.call(link, link) } else {
+        link.to(url)
+      }
 
-    if (typeof url === 'function') { url.call(link, link) } else {
-      link.to(url)
+      return this.parent().put(link).put(this)
     }
-
-    return this.parent().put(link).put(this)
   }
-})
+}
index d49ab121720a4734e57b187c5232d3d60eb9ff1b..eca6ee3079386d22cd3040b647b920099310828c 100644 (file)
@@ -1,6 +1,6 @@
 import Queue from './Queue.js'
 
-export default {
+const Animator = {
   nextDraw: null,
   frames: new Queue(),
   timeouts: new Queue(),
@@ -81,3 +81,5 @@ export default {
         : null
   }
 }
+
+export default Animator
index 783fa6a7c29d1f1d38ae285bf2831fba3bc8bfea..b70f3297b36a3bd1a3a3bae6f1b8e64dd70d5842 100644 (file)
@@ -1,10 +1,9 @@
 import {nodeOrNew} from './tools.js'
-import Parent from './Parent.js'
 
-export default function Bare (element, inherit) {
-  return class Custom extends inherit {
+export default function Bare (element, inherit = {}) {
+  let custom =  class Custom extends inherit {
     constructor (node) {
-      super(nodeOrNew(element, node))
+      super(nodeOrNew(element, node), Custom)
     }
 
     words (text) {
@@ -19,16 +18,18 @@ export default function Bare (element, inherit) {
       return this
     }
   }
-}
 
-export let constructors = {
-  // Create an element that is not described by SVG.js
-  element: function (element, inherit) {
-    let custom = createCustom(element, inherit)
-    return this.put(new custom())
-  }
+  extend(custom, inherit)
 }
 
+// export let constructors = {
+//   // Create an element that is not described by SVG.js
+//   element: function (element, inherit) {
+//     let custom = createCustom(element, inherit)
+//     return this.put(new custom())
+//   }
+// }
+
 // extend(Parent, {
 //   // Create an element that is not described by SVG.js
 //   element: function (element, inherit) {
diff --git a/src/Base.js b/src/Base.js
new file mode 100644 (file)
index 0000000..6b1242b
--- /dev/null
@@ -0,0 +1,14 @@
+export default class Base {
+  constructor (node, {extensions = []}) {
+    this.tags = []
+
+    for (let extension of extensions) {
+      extension.setup.call(this, node)
+      this.tags.push(extension.name)
+    }
+  }
+
+  is (ability) {
+    return this.tags.includes(ability)
+  }
+}
index 0c98dd55143ab222f1505a40e50679bbb754de1b..36ee7f8835c3fdb557fcf4df38d7acc8a9027f5b 100644 (file)
@@ -1,11 +1,16 @@
-import {Parent, Doc, Symbol, Image, Pattern, Marker, Point} from './classes.js'
+//import {Parent, Doc, Symbol, Image, Pattern, Marker, Point} from './classes.js'
+import Point from './Point.js'
 import parser from './parser.js'
 import {fullBox, domContains, isNulledBox} from './helpers.js'
 import {extend} from './tools.js'
 import {delimiter} from './regex.js'
 
 export default class Box {
-  constructor (source) {
+  constructor (...args) {
+    this.init(...args)
+  }
+
+  init (source) {
     var base = [0, 0, 0, 0]
     source = typeof source === 'string' ? source.split(delimiter).map(parseFloat)
       : Array.isArray(source) ? source
@@ -77,20 +82,6 @@ export default class Box {
   }
 }
 
-
-extend(Parent, {
-  // Get bounding box
-  bbox () {
-    return new Box(getBox((node) => node.getBBox()))
-  },
-
-  rbox (el) {
-    let box = new Box(getBox((node) => node.getBoundingClientRect()))
-    if (el) return box.transform(el.screenCTM().inverse())
-    return box.addOffset()
-  }
-})
-
 function getBox(cb) {
   let box
 
@@ -106,14 +97,26 @@ function getBox(cb) {
       box = cb(clone.node)
       clone.remove()
     } catch (e) {
+      throw (e)
       console.warn('Getting a bounding box of this element is not possible')
     }
   }
   return box
 }
 
-
-extend([Doc, Symbol, Image, Pattern, Marker], {
+Box.constructors = {
+  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: function (x, y, width, height) {
     // act as getter
     if (x == null) return new Box(this.attr('viewBox'))
@@ -121,4 +124,4 @@ extend([Doc, Symbol, Image, Pattern, Marker], {
     // act as setter
     return this.attr('viewBox', new Box(x, y, width, height))
   }
-})
+}
index fc8be72e8b986b25290603d26825a5e31d568166..6d4b12ba824ddf4d56116204caa856615c8c27db 100644 (file)
@@ -1,10 +1,11 @@
-import SVGNumber from './SVGNumber.js'
-import Parent from './Parent.js'
+import Base from './Base.js'
+import {nodeOrNew, extend} from './tools.js'
 import {x, y, cx, cy, width, height, size} from './circled.js'
+import SVGNumber from './SVGNumber.js'
 
-export default class Circle extends Shape {
+export default class Circle extends Base {
   constructor (node) {
-    super(nodeOrNew('circle', node))
+    super(nodeOrNew('circle', node), Circle)
   }
 
   radius (r) {
@@ -24,11 +25,13 @@ export default class Circle extends Shape {
 
 extend(Circle, {x, y, cx, cy, width, height, size})
 
-addFactory(Parent, {
-  // Create circle element
-  circle (size) {
-    return this.put(new Circle())
-      .radius(new SVGNumber(size).divide(2))
-      .move(0, 0)
+Circle.constructors = {
+  Element: {
+    // Create circle element
+    circle (size) {
+      return this.put(new Circle())
+        .radius(new SVGNumber(size).divide(2))
+        .move(0, 0)
+    }
   }
-})
+}
index ef820e57105ac319ce5705c38170927dc89b42fc..486b30cd5249dab1d89688294cc592324404c856 100644 (file)
@@ -1,11 +1,11 @@
-import Container from './Container.js'
-import Element from './Element.js'
+import Base from './Base.js'
 import {nodeOrNew, extend} from './tools.js'
 import find from './selector.js'
+import {remove} from './Element.js'
 
-export default class ClipPath extends Container {
+export default class ClipPath extends Base {
   constructor (node) {
-    super(nodeOrNew('clipPath', node))
+    super(nodeOrNew('clipPath', node), ClipPath)
   }
 
   // Unclip all clipped elements and remove itself
@@ -16,7 +16,7 @@ export default class ClipPath extends Container {
     })
 
     // remove clipPath from parent
-    return super.remove()
+    return remove.call(this)
   }
 
   targets () {
@@ -24,31 +24,33 @@ export default class ClipPath extends Container {
   }
 }
 
-addFactory(Container, {
-  // Create clipping element
-  clip: function() {
-    return this.defs().put(new ClipPath)
-  }
-})
-
-extend(Element, {
-  // Distribute clipPath to svg element
-  clipWith (element) {
-    // use given clip or create a new one
-    let clipper = element instanceof ClipPath
-      ? element
-      : this.parent().clip().add(element)
-
-    // apply mask
-    return this.attr('clip-path', 'url("#' + clipper.id() + '")')
-  },
 
-  // Unclip element
-  unclip () {
-    return this.attr('clip-path', null)
+ClipPath.constructors = {
+  Container: {
+    // Create clipping element
+    clip: function() {
+      return this.defs().put(new ClipPath)
+    }
   },
-
-  clipper () {
-    return this.reference('clip-path')
+  Element: {
+    // Distribute clipPath to svg element
+    clipWith (element) {
+      // use given clip or create a new one
+      let clipper = element instanceof ClipPath
+        ? element
+        : this.parent().clip().add(element)
+
+      // apply mask
+      return this.attr('clip-path', 'url("#' + clipper.id() + '")')
+    },
+
+    // Unclip element
+    unclip () {
+      return this.attr('clip-path', null)
+    },
+
+    clipper () {
+      return this.reference('clip-path')
+    }
   }
-})
+}
index 1e2befbe0550d20ed4dc6473e6c02a18d5e8fd25..de657505b621876a77538417a3633e45d6bc9fde 100644 (file)
@@ -29,10 +29,15 @@ SVG.hsl()
 SVG.lab('rgb(100, 100, 100)')
 */
 
-import {isHex, isRgb, whitespace, rgb} from './regex.js'
+import {isHex, isRgb, whitespace, rgb, hex} from './regex.js'
+import {fullHex, compToHex} from './helpers.js'
 
 export default class Color {
-  constructor (color, g, b) {
+  constructor (...args) {
+    this.init(...args)
+  }
+
+  init (color, g, b) {
     let match
 
     // initialize defaults
diff --git a/src/Container.js b/src/Container.js
deleted file mode 100644 (file)
index 5d6dc43..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-import Parent from './Parent.js'
-export default class Container extends Parent {}
index 81f67219c6d23d6e974c356c5e926eeec59d6827..a48d9466c0a4d01ecac9357fffe3acb5f7ce5f3f 100644 (file)
@@ -1,5 +1,6 @@
 
 import {timeline} from './defaults.js'
+import {extend} from './tools.js'
 
 /***
 Base Class
index 21b67032b053fca537e6fc280b39865f5256bb82..e8c5ac277bf9f2ba13371321ad1c6c39e8677321 100644 (file)
@@ -1,8 +1,11 @@
-import Container from './Container.js'
+import Base from './Base.js'
 import {nodeOrNew} from './tools.js'
 
-export default class Defs extends Container {
+export default class Defs extends Base {
   constructor (node) {
-    super(nodeOrNew('defs', node))
+    super(nodeOrNew('defs', node), Defs)
   }
+
+  flatten () { return this }
+  ungroup () { return this }
 }
index e4969965958bcbf6e8ff12ee98cabdc0da1965af..4b89deb7bd1ad0d97c2b84abafa1f33948982aab 100644 (file)
@@ -1,75 +1,87 @@
-import Container from './Container.js'
-import Parent from './Parent.js'
-import {adopt, extend} from './tools.js'
-import {ns, xlink, xmlns, svgjs} from './namespaces.js'
+import Base from './Base.js'
+import Defs from './Defs.js'
+import { extend, nodeOrNew } from './tools.js'
+import { ns, xlink, xmlns, svgjs } from './namespaces.js'
+import {adopt} from './adopter.js'
+import * as EventTarget from './EventTarget.js'
+import * as Element from './Element.js'
+import * as Parent from './Parent.js'
 
-export default class Doc extends Container {
-   constructor (node) {
-     super(nodeOrNew('svg', node))
-     this.namespace()
-   }
+export default class Doc extends Base {
+  constructor(node) {
+    super(nodeOrNew('svg', node), Doc)
+    this.namespace()
+  }
+
+  isRoot() {
+    return !this.node.parentNode
+      || !(this.node.parentNode instanceof window.SVGElement)
+      || this.node.parentNode.nodeName === '#document'
+  }
 
-   isRoot () {
-     return !this.node.parentNode || !(this.node.parentNode instanceof window.SVGElement) || this.node.parentNode.nodeName === '#document'
-   }
+  // Check if this is a root svg
+  // If not, call docs from this element
+  doc() {
+    if (this.isRoot()) return this
+    return Element.doc.call(this)
+  }
 
-   // Check if this is a root svg
-   // If not, call docs from this element
-   doc () {
-     if (this.isRoot()) return this
-     return super.doc()
-   }
+  // Add namespaces
+  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)
+  }
 
-   // Add namespaces
-   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
+  defs() {
+    if (!this.isRoot()) return this.doc().defs()
 
-   // Creates and returns defs element
-   defs () {
-     if (!this.isRoot()) return this.doc().defs()
-     return adopt(this.node.getElementsByTagName('defs')[0]) ||
+    if (!this.isRoot()) return this.doc().defs()
+    return adopt(this.node.getElementsByTagName('defs')[0]) ||
       this.put(new Defs())
-   }
+  }
 
-   // custom parent method
-   parent (type) {
-     if (this.isRoot()) {
-       return this.node.parentNode.nodeName === '#document' ? null : this.node.parentNode
-     }
+  // custom parent method
+  parent(type) {
+    if (this.isRoot()) {
+      return this.node.parentNode.nodeName === '#document'
+        ? null
+        : this.node.parentNode
+    }
 
-     return super.parent(type)
-   }
+    return Element.parent.call(this, type)
+  }
 
-   // Removes the doc from the DOM
-   remove () {
-     if (!this.isRoot()) {
-       return super.remove()
-     }
+  // Removes the doc from the DOM
+  remove() {
+    if (!this.isRoot()) {
+      return Element.remove.call(this)
+    }
 
-     if (this.parent()) {
-       this.parent().removeChild(this.node)
-     }
+    if (this.parent()) {
+      this.parent().removeChild(this.node)
+    }
 
-     return this
-   }
+    return this
+  }
 
-   clear () {
-     // remove children
-     while (this.node.hasChildNodes()) {
-       this.node.removeChild(this.node.lastChild)
-     }
-     return this
-   }
+  clear() {
+    // remove children
+    while (this.node.hasChildNodes()) {
+      this.node.removeChild(this.node.lastChild)
+    }
+    return this
+  }
 }
 
-addFactory(Container, {
-  // Create nested svg document
-  nested () {
-    return this.put(new Doc())
-  }
-})
+extend(Doc, [EventTarget, Element, Parent])
+
+// addFactory(Container, {
+//   // Create nested svg document
+//   nested() {
+//     return this.put(new Doc())
+//   }
+// })
index 76675638c72fb161e242d8dc77e5d7b32262f7b9..bed762c6494d76236b0d8c7e6f16a317a0b9e545 100644 (file)
-import {proportionalSize, assignNewId, makeInstance, matches} from './helpers.js'
+import {proportionalSize, assignNewId, matcher} from './helpers.js'
+import {makeInstance, adopt} from './adopter.js'
 import {eid} from './tools.js'
 import {delimiter} from './regex.js'
 import {ns} from './namespaces.js'
-import {adopt} from './tools.js'
-// import {Doc, EventTarget, Parent} from './classes.js'
-import EventTarget from './EventTarget.js'
 import Doc from './Doc.js'
-import Parent from './Parent.js'
-
-export default class Element extends EventTarget {
-  constructor (node) {
-    // event listener
-    this.events = {}
-
-    // initialize data object
-    this.dom = {}
-
-    // create circular reference
-    this.node = node
-    if (this.node) {
-      this.type = node.nodeName
-      this.node.instance = this
-      this.events = node.events || {}
-
-      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')) || {})
-      }
-    }
+import SVGNumber from './SVGNumber.js'
+
+export const name = 'Element'
+
+export function setup (node) {
+  // initialize data object
+  this.dom = {}
+
+  // create circular reference
+  this.node = node
+
+  this.type = node.nodeName
+  this.node.instance = 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')) || {})
   }
+}
 
   // Move over x-axis
-  x (x) {
-    return this.attr('x', x)
-  }
+export function x (x) {
+  return this.attr('x', x)
+}
 
   // Move over y-axis
-  y (y) {
-    return this.attr('y', y)
-  }
+export function y (y) {
+  return this.attr('y', y)
+}
 
   // Move by center over x-axis
-  cx (x) {
-    return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2)
-  }
+export function cx (x) {
+  return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2)
+}
 
   // Move by center over y-axis
-  cy (y) {
-    return y == null
-      ? this.y() + this.height() / 2
-      : this.y(y - this.height() / 2)
-  }
+export function cy (y) {
+  return y == null
+    ? this.y() + this.height() / 2
+    : this.y(y - this.height() / 2)
+}
 
   // Move element to given x and y values
-  move (x, y) {
-    return this.x(x).y(y)
-  }
+export function move (x, y) {
+  return this.x(x).y(y)
+}
 
   // Move element by its center
-  center (x, y) {
-    return this.cx(x).cy(y)
-  }
+export function center (x, y) {
+  return this.cx(x).cy(y)
+}
 
   // Set width of element
-  width (width) {
-    return this.attr('width', width)
-  }
+export function width (width) {
+  return this.attr('width', width)
+}
 
   // Set height of element
-  height (height) {
-    return this.attr('height', height)
-  }
+export function height (height) {
+  return this.attr('height', height)
+}
 
   // Set element size to given width and height
-  size (width, height) {
-    let p = proportionalSize(this, width, height)
+export function size (width, height) {
+  let p = proportionalSize(this, width, height)
 
-    return this
-      .width(new SVGNumber(p.width))
-      .height(new SVGNumber(p.height))
-  }
+  return this
+    .width(new SVGNumber(p.width))
+    .height(new SVGNumber(p.height))
+}
 
   // Clone element
-  clone (parent) {
-    // write dom data to the dom so the clone can pickup the data
-    this.writeDataToDom()
+export function clone (parent) {
+  // write dom data to the dom so the clone can pickup the data
+  this.writeDataToDom()
 
-    // clone element and assign new id
-    let clone = assignNewId(this.node.cloneNode(true))
+  // clone element and assign new id
+  let clone = assignNewId(this.node.cloneNode(true))
 
-    // insert the clone in the given parent or after myself
-    if (parent) parent.add(clone)
-    else this.after(clone)
+  // insert the clone in the given parent or after myself
+  if (parent) parent.add(clone)
+  else this.after(clone)
 
-    return clone
-  }
+  return clone
+}
 
   // Remove element
-  remove () {
-    if (this.parent()) { this.parent().removeElement(this) }
+export function remove () {
+  if (this.parent()) { this.parent().removeElement(this) }
 
-    return this
-  }
+  return this
+}
 
   // Replace element
-  replace (element) {
-    this.after(element).remove()
+export function replace (element) {
+  this.after(element).remove()
 
-    return element
-  }
+  return element
+}
 
   // Add element to given container and return self
-  addTo (parent) {
-    return makeInstance(parent).put(this)
-  }
+export function addTo (parent) {
+  return makeInstance(parent).put(this)
+}
 
   // Add element to given container and return container
-  putIn (parent) {
-    return makeInstance(parent).add(this)
-  }
+export function putIn (parent) {
+  return makeInstance(parent).add(this)
+}
 
   // Get / set id
-  id (id) {
-    // generate new id if no id set
-    if (typeof id === 'undefined' && !this.node.id) {
-      this.node.id = eid(this.type)
-    }
-
-    // dont't set directly width this.node.id to make `null` work correctly
-    return this.attr('id', id)
+export function id (id) {
+  // generate new id if no id set
+  if (typeof id === 'undefined' && !this.node.id) {
+    this.node.id = eid(this.type)
   }
 
+  // dont't set directly width this.node.id to make `null` work correctly
+  return this.attr('id', id)
+}
+
   // Checks whether the given point inside the bounding box of the element
-  inside (x, y) {
-    let box = this.bbox()
+export function inside (x, y) {
+  let box = this.bbox()
 
-    return x > box.x &&
-      y > box.y &&
-      x < box.x + box.width &&
-      y < box.y + box.height
-  }
+  return x > box.x &&
+    y > box.y &&
+    x < box.x + box.width &&
+    y < box.y + box.height
+}
 
   // Return id on string conversion
-  toString () {
-    return this.id()
-  }
+export function toString () {
+  return this.id()
+}
 
   // Return array of classes on the node
-  classes () {
-    var attr = this.attr('class')
-    return attr == null ? [] : attr.trim().split(delimiter)
-  }
+export function classes () {
+  var attr = this.attr('class')
+  return attr == null ? [] : attr.trim().split(delimiter)
+}
 
   // Return true if class exists on the node, false otherwise
-  hasClass (name) {
-    return this.classes().indexOf(name) !== -1
-  }
+export function hasClass (name) {
+  return this.classes().indexOf(name) !== -1
+}
 
   // Add class to the node
-  addClass (name) {
-    if (!this.hasClass(name)) {
-      var array = this.classes()
-      array.push(name)
-      this.attr('class', array.join(' '))
-    }
-
-    return this
+export function addClass (name) {
+  if (!this.hasClass(name)) {
+    var array = this.classes()
+    array.push(name)
+    this.attr('class', array.join(' '))
   }
 
+  return this
+}
+
   // Remove class from the node
-  removeClass (name) {
-    if (this.hasClass(name)) {
-      this.attr('class', this.classes().filter(function (c) {
-        return c !== name
-      }).join(' '))
-    }
-
-    return this
+export function removeClass (name) {
+  if (this.hasClass(name)) {
+    this.attr('class', this.classes().filter(function (c) {
+      return c !== name
+    }).join(' '))
   }
 
+  return this
+}
+
   // Toggle the presence of a class on the node
-  toggleClass (name) {
-    return this.hasClass(name) ? this.removeClass(name) : this.addClass(name)
-  }
+export function toggleClass (name) {
+  return this.hasClass(name) ? this.removeClass(name) : this.addClass(name)
+}
 
-  // FIXME: getIdFromReference
-  // Get referenced element form attribute value
-  reference (attr) {
-    return get(this.attr(attr))
-  }
+// FIXME: getIdFromReference
+// Get referenced element form attribute value
+export function reference (attr) {
+  return get(this.attr(attr))
+}
 
   // Returns the parent element instance
-  parent (type) {
-    var parent = this
+export function parent (type) {
+  var parent = this
 
-    // check for parent
-    if (!parent.node.parentNode) return null
+  // check for parent
+  if (!parent.node.parentNode) return null
 
-    // get parent element
-    parent = adopt(parent.node.parentNode)
+  // get parent element
+  parent = adopt(parent.node.parentNode)
 
-    if (!type) return parent
+  if (!type) return parent
 
-    // loop trough ancestors if type is given
-    while (parent && parent.node instanceof window.SVGElement) {
-      if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent
-      parent = adopt(parent.node.parentNode)
-    }
+  // loop trough ancestors if type is given
+  while (parent && parent.node instanceof window.SVGElement) {
+    if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent
+    parent = adopt(parent.node.parentNode)
   }
+}
 
   // Get parent document
-  doc () {
-    let p = this.parent(Doc)
-    return p && p.doc()
-  }
+export function doc () {
+  let p = this.parent(Doc)
+  return p && p.doc()
+}
 
   // Get defs
-  defs () {
-    return this.doc().defs()
-  }
+export function defs () {
+  return this.doc().defs()
+}
 
   // return array of all ancestors of given type up to the root svg
-  parents (type) {
-    let parents = []
-    let parent = this
+export function parents (type) {
+  let parents = []
+  let parent = this
 
-    do {
-      parent = parent.parent(type)
-      if (!parent || !parent.node) break
+  do {
+    parent = parent.parent(type)
+    if (!parent || !parent.node) break
 
-      parents.push(parent)
-    } while (parent.parent)
+    parents.push(parent)
+  } while (parent.parent)
 
-    return parents
-  }
+  return parents
+}
 
   // matches the element vs a css selector
-  matches (selector) {
-    return matches(this.node, selector)
-  }
+export function matches (selector) {
+  return matches(this.node, selector)
+}
 
   // Returns the svg node to call native svg methods on it
-  native () {
-    return this.node
-  }
+export function native () {
+  return this.node
+}
 
   // Import raw svg
-  svg (svg) {
-    var well, len
-
-    // act as a setter if svg is given
-    if (svg && this instanceof Parent) {
-      // create temporary holder
-      well = document.createElementNS(ns, 'svg')
-      // dump raw svg
-      well.innerHTML = svg
-
-      // transplant nodes
-      for (len = well.children.length; len--;) {
-        this.node.appendChild(well.firstElementChild)
-      }
-
-    // otherwise act as a getter
-    } else {
-      // write svgjs data to the dom
-      this.writeDataToDom()
-
-      return this.node.outerHTML
-    }
-
-    return this
-  }
+export function svg () {
+  // write svgjs data to the dom
+  this.writeDataToDom()
+
+  return this.node.outerHTML
+}
 
   // write svgjs data to the dom
-  writeDataToDom () {
-    // dump variables recursively
-    if (this.is(Parent)) {
-      this.each(function () {
-        this.writeDataToDom()
-      })
-    }
-
-    // remove previously set data
-    this.node.removeAttribute('svgjs:data')
-
-    if (Object.keys(this.dom).length) {
-      this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)) // see #428
-    }
-    return this
+export function writeDataToDom () {
+  // remove previously set data
+  this.node.removeAttribute('svgjs:data')
+
+  if (Object.keys(this.dom).length) {
+    this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)) // see #428
   }
+  return this
+}
 
   // set given data to the elements data property
-  setData (o) {
-    this.dom = o
-    return this
-  }
+export function setData (o) {
+  this.dom = o
+  return this
+}
 
-  getEventTarget () {
-    return this.node
-  }
+export function getEventTarget () {
+  return this.node
 }
+
+export {default as attr} from './attr.js'
index 2a6fc517782ae80bfa0c429521426461eec851ef..cb025bba88260f56462584eb558757fcd704915d 100644 (file)
@@ -1,17 +1,18 @@
-import Parent from './Parent.js'
+import Base from './Base.js'
 import * as circled from './circled.js'
+import {extend} from './tools.js'
 
-export default class Ellipse extends Shape {
+export default class Ellipse extends Base {
   constructor (node) {
-    super(nodeOrNew('ellipse', node))
+    super(nodeOrNew('ellipse', node), Ellipse)
   }
 }
 
 extend(Ellipse, circled)
 
-addFactory(Container, {
-  // Create an ellipse
-  ellipse: function (width, height) {
-    return this.put(new Ellipse()).size(width, height).move(0, 0)
-  }
-})
+// addFactory(Container, {
+//   // Create an ellipse
+//   ellipse: function (width, height) {
+//     return this.put(new Ellipse()).size(width, height).move(0, 0)
+//   }
+// })
index c762929612e3d834fff17854abd9a5c5d7102261..ce18d1f4720345fcb38672db9fc363257c1f5c90 100644 (file)
@@ -1,25 +1,29 @@
-import {on, off, dispatch} from './event.js'
+import {on as _on, off as _off, dispatch as _dispatch} from './event.js'
+
+export const name = 'EventTarget'
+
+export function setup (node = {}) {
+  this.events = node.events || {}
+}
 
-export default class EventTarget {
   // Bind given event to listener
-  on (event, listener, binding, options) {
-    on(this, event, listener, binding, options)
-    return this
-  }
+export function on (event, listener, binding, options) {
+  _on(this, event, listener, binding, options)
+  return this
+}
 
   // Unbind event from listener
-  off (event, listener) {
-    off(this, event, listener)
-    return this
-  }
+export function off (event, listener) {
+  _off(this, event, listener)
+  return this
+}
 
-  dispatch (event, data) {
-    return dispatch(this, event, data)
-  }
+export function dispatch (event, data) {
+  return _dispatch(this, event, data)
+}
 
   // Fire given event
-  fire (event, data) {
-    this.dispatch(event, data)
-    return this
-  }
+export function fire (event, data) {
+  this.dispatch(event, data)
+  return this
 }
index 2d60cfec24ac90c60b44dde83fc4d1b584b7d52e..93d2b7f18184e2e865a14efce79954f887a1fb1c 100644 (file)
--- a/src/G.js
+++ b/src/G.js
@@ -1,15 +1,16 @@
-import Container from './Container.js'
-import Parent from './Parent.js'
+import Base from './Base.js'
 
-export default class G extends Container {
+export default class G extends Base {
   constructor (node) {
-    super(nodeorNew('group', node))
+    super(nodeorNew('g', node), G)
   }
 }
 
-addFactory(Parent, {
-  // Create a group element
-  group: function () {
-    return this.put(new G())
+G.constructors = {
+  Element: {
+    // Create a group element
+    group: function () {
+      return this.put(new G())
+    }
   }
-})
+}
index da76666805c349e598b5fdf44edb4209e0f53a82..ab4cc4f0b47a70b7f5d25218dd063bb30f4dfd46 100644 (file)
@@ -1,10 +1,15 @@
 import Stop from './Stop.js'
+import Base from './Base.js'
 import * as gradiented from './gradiented.js'
-import {nodeOrNew, extend, addFactory} from './tools.js'
+import {nodeOrNew, extend} from './tools.js'
+import attr from './attr.js'
 
-export default class Gradient extends Container {
+export default class Gradient extends Base {
   constructor (type) {
-    super(nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type))
+    super(
+      nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type),
+      Gradient
+    )
   }
 
   // Add a color stop
@@ -38,23 +43,23 @@ export default class Gradient extends Container {
   // custom attr to handle transform
   attr (a, b, c) {
     if (a === 'transform') a = 'gradientTransform'
-    return super.attr(a, b, c)
+    return attr.call(this, a, b, c)
   }
 }
 
 extend(Gradient, gradiented)
 
-addFactory(Parent, {
-  // Create gradient element in defs
-  gradient (type, block) {
-    return this.defs().gradient(type, block)
-  }
-})
-
-// Base gradient generation
-addFactory(Defs, {
+Gradient.constructors = {
+  Container: {
+    // Create gradient element in defs
+    gradient (type, block) {
+      return this.defs().gradient(type, block)
+    }
+  },
   // define gradient
-  gradient: function (type, block) {
-    return this.put(new Gradient(type)).update(block)
+  Defs: {
+    gradient (type, block) {
+      return this.put(new Gradient(type)).update(block)
+    }
   }
-})
+}
index 4a12d3f1ccb9c4397423cd56681cab4d66e3a35d..f674d0b43b48296b1d1fe85b4da04c1e312bac53 100644 (file)
@@ -1,8 +1,9 @@
-import {makeInstance} from './helpers.js'
-import EventTarget from './EventTarget.js'
+import {makeInstance} from './adopter.js'
+import Base from './Base.js'
 
-export default class HtmlNode extends EventTarget {
+export default class HtmlNode extends Base {
   constructor (element) {
+    super(element, HtmlNode)
     this.node = element
   }
 
index c70be1d50b10c4d14336878af2fea90c9b3c8d3b..389a38d8dfb5a4b9adab06fd1e4fd8fa55959bf8 100644 (file)
@@ -1,13 +1,12 @@
-import Shape from './Shape.js'
-import Container from './Container.js'
+import Base from './Base.js'
 import Pattern from './Pattern.js'
 import {on, off} from './event.js'
-import {nodeOrNew, addFactory} from './tools.js'
+import {nodeOrNew} from './tools.js'
 import {xlink} from './namespaces.js'
 
-export default class Image extends Shape {
+export default class Image extends Base {
   constructor (node) {
-    super(nodeOrNew('image', node))
+    super(nodeOrNew('image', node), Image)
   }
 
   // (re)load image
@@ -50,9 +49,11 @@ export default class Image extends Shape {
   }
 }
 
-addFactory(Container, {
-  // create image element, load image and set its size
-  image (source, callback) {
-    return this.put(new Image()).size(0, 0).load(source, callback)
+Image.constructors = {
+  Container: {
+    // create image element, load image and set its size
+    image (source, callback) {
+      return this.put(new Image()).size(0, 0).load(source, callback)
+    }
   }
-})
+}
index ddd00e9f5b861b92bd0870051ee40c0346034062..930820b962ca2120f41ec6288bf6077556c487aa 100644 (file)
@@ -1,11 +1,12 @@
 import {proportionalSize} from './helpers.js'
 import {nodeOrNew} from './tools.js'
-import {Shape, Container, PointArray} from './classes.js'
+import PointArray from './PointArray.js'
+import Base from './Base.js'
 
-export default class Line extends Shape {
+export default class Line extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('line', node))
+    super(nodeOrNew('line', node), Line)
   }
 
   // Get array
@@ -42,14 +43,16 @@ export default class Line extends Shape {
 
 }
 
-addFactory(Container, {
-  // Create a line element
-  line (...args) {
-    // make sure plot is called as a setter
-    // x1 is not necessarily a number, it can also be an array, a string and a PointArray
-    return Line.prototype.plot.apply(
-      this.put(new Line())
-    , args[0] != null ? args : [0, 0, 0, 0]
-    )
+Line.constructors = {
+  Container: {
+    // Create a line element
+    line (...args) {
+      // make sure plot is called as a setter
+      // x1 is not necessarily a number, it can also be an array, a string and a PointArray
+      return Line.prototype.plot.apply(
+        this.put(new Line())
+      , args[0] != null ? args : [0, 0, 0, 0]
+      )
+    }
   }
-})
+}
index 298ac6fe7c9219b583f5ac59b62b2cf926401045..dfba967019937c4129e94c235e89d3c0138f6e5b 100644 (file)
@@ -1,14 +1,14 @@
-import Container from './Container.js'
-import Defs from './Defs.js'
-import Line from './Line.js'
-import Polyline from './Polyline.js'
-import Polygon from './Polygon.js'
-import Path from './Path.js'
+import Base from './Base.js'
+// import Defs from './Defs.js'
+// import Line from './Line.js'
+// import Polyline from './Polyline.js'
+// import Polygon from './Polygon.js'
+// import Path from './Path.js'
 
-export default class Marker extends Container {
+export default class Marker extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('marker', node))
+    super(nodeOrNew('marker', node), Marker)
   }
 
   // Set width of element
@@ -43,40 +43,40 @@ export default class Marker extends Container {
   }
 }
 
-addFactory(Container, {
-  marker (width, height, block) {
-    // Create marker element in defs
-    return this.defs().marker(width, height, block)
-  }
-})
-
-extend(Defs, {
-  // Create marker
-  marker (width, height, block) {
-    // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
-    return this.put(new Marker())
-      .size(width, height)
-      .ref(width / 2, height / 2)
-      .viewbox(0, 0, width, height)
-      .attr('orient', 'auto')
-      .update(block)
-  }
-})
+Marker.constructors = {
+  Container: {
+    marker (width, height, block) {
+      // Create marker element in defs
+      return this.defs().marker(width, height, block)
+    }
+  },
+  Defs: {
+    // Create marker
+    marker (width, height, block) {
+      // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
+      return this.put(new Marker())
+        .size(width, height)
+        .ref(width / 2, height / 2)
+        .viewbox(0, 0, width, height)
+        .attr('orient', 'auto')
+        .update(block)
+    }
+  },
+  marker: {
+    // Create and attach markers
+    marker (marker, width, height, block) {
+      var attr = ['marker']
 
-extend([Line, Polyline, Polygon, Path], {
-  // Create and attach markers
-  marker (marker, width, height, block) {
-    var attr = ['marker']
+      // Build attribute name
+      if (marker !== 'all') attr.push(marker)
+      attr = attr.join('-')
 
-    // Build attribute name
-    if (marker !== 'all') attr.push(marker)
-    attr = attr.join('-')
+      // Set marker attribute
+      marker = arguments[1] instanceof Marker
+        ? arguments[1]
+        : this.defs().marker(width, height, block)
 
-    // Set marker attribute
-    marker = arguments[1] instanceof Marker
-      ? arguments[1]
-      : this.doc().marker(width, height, block)
-
-    return this.attr(attr, marker)
+      return this.attr(attr, marker)
+    }
   }
-})
+}
index bdd60863ef94c2c1f312528e2bbbe5eb1cba06ff..969fe2c352878ccc1d29fbf5e1e530747ffb0f32 100644 (file)
@@ -1,9 +1,9 @@
-import Container from './Container.js'
-import Element from './Element.js'
+import Base from './Base.js'
 import {nodeOrNew} from './tools.js'
 import find from './selector.js'
+import {remove} from  './Element.js'
 
-export default class Mask extends Container {
+export default class Mask extends Base {
   // Initialize node
   constructor (node) {
     super(nodeOrNew('mask', node))
@@ -17,7 +17,7 @@ export default class Mask extends Container {
     })
 
     // remove mask from parent
-    return super.remove()
+    return remove.call(this)
   }
 
   targets () {
@@ -26,30 +26,32 @@ export default class Mask extends Container {
 
 }
 
-addFactory(Container, {
-  mask () {
-    return this.defs().put(new Mask())
-  }
-})
-
-extend(Element, {
-  // Distribute mask to svg element
-  maskWith (element) {
-    // use given mask or create a new one
-    var masker = element instanceof Mask
-      ? element
-      : this.parent().mask().add(element)
-
-    // apply mask
-    return this.attr('mask', 'url("#' + masker.id() + '")')
-  },
 
-  // Unmask element
-  unmask () {
-    return this.attr('mask', null)
+Mask.constructors = {
+  Container: {
+    mask () {
+      return this.defs().put(new Mask())
+    }
   },
-
-  masker () {
-    return this.reference('mask')
+  Element: {
+    // Distribute mask to svg element
+    maskWith (element) {
+      // use given mask or create a new one
+      var masker = element instanceof Mask
+        ? element
+        : this.parent().mask().add(element)
+
+      // apply mask
+      return this.attr('mask', 'url("#' + masker.id() + '")')
+    },
+
+    // Unmask element
+    unmask () {
+      return this.attr('mask', null)
+    },
+
+    masker () {
+      return this.reference('mask')
+    }
   }
-})
+}
index 5edbc5c888378dfc4d0a7c1eb3141c9add6f711d..d53db3ce2963b74c03698ff58ce758af18265eb4 100644 (file)
@@ -1,16 +1,21 @@
 import {abcdef, arrayToMatrix, closeEnough, formatTransforms, isMatrixLike, matrixMultiply} from './helpers.js'
-import {Element, Point, Doc} from './classes.js'
+import Point from './Point.js'
 import {delimiter} from './regex.js'
 import {radians} from './utils.js'
 import parser from './parser.js'
+import Base from './Base.js'
 
 export default class Matrix {
+  constructor (...args) {
+    this.init(...args)
+  }
+
   // Initialize
-  constructor (source) {
+  init (source) {
     var base = arrayToMatrix([1, 0, 0, 1, 0, 0])
 
     // ensure source as object
-    source = source instanceof Element ? source.matrixify()
+    source = source instanceof Base && source.is('Element') ? source.matrixify()
       : typeof source === 'string' ? arrayToMatrix(source.split(delimiter).map(parseFloat))
       : Array.isArray(source) ? arrayToMatrix(source)
       : (typeof source === 'object' && isMatrixLike(source)) ? source
@@ -369,7 +374,7 @@ export default class Matrix {
   // Convert to native SVGMatrix
   native () {
     // create new matrix
-    var matrix = parser.nodes.node.createSVGMatrix()
+    var matrix = parser().node.createSVGMatrix()
 
     // update with current values
     for (var i = abcdef.length - 1; i >= 0; i--) {
@@ -408,27 +413,29 @@ export default class Matrix {
   }
 }
 
-extend(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 (this instanceof Doc && !this.isRoot()) {
-      var rect = this.rect(1, 1)
-      var m = rect.node.getScreenCTM()
-      rect.remove()
-      return new Matrix(m)
+Matrix.constructors = {
+  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 (this instanceof Doc && !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())
     }
-    return new Matrix(this.node.getScreenCTM())
   }
-})
+}
 
 // let extensions = {}
 // ['rotate'].forEach((method) => {
index 1cb437e55997d5509c4eca9db63e8c45625795a4..c7e1c0fed925dc192703e3c95ce75555f60bba11 100644 (file)
@@ -1,8 +1,9 @@
 import {extend} from './tools.js'
-import {Color, SVGNumber, SVGArray, PathArray} from './classes.js'
+import {Color, SVGNumber, SVGArray, PathArray, Box, Matrix, PointArray} from './classes.js'
+import {Ease} from './Controller.js'
 
 export default class Morphable {
-  constructor () {
+  constructor (stepper) {
     // FIXME: the default stepper does not know about easing
     this._stepper = stepper || new Ease('-')
 
@@ -106,7 +107,11 @@ export default class Morphable {
 }
 
 Morphable.NonMorphable = class {
-  constructor (val) {
+  constructor (...args) {
+    this.init(...args)
+  }
+
+  init (val) {
     val = Array.isArray(val) ? val[0] : val
     this.value = val
   }
@@ -121,7 +126,11 @@ Morphable.NonMorphable = class {
 }
 
 Morphable.TransformBag = class {
-  constructor (obj) {
+  constructor (...args) {
+    this.init(...args)
+  }
+
+  init (obj) {
     if (Array.isArray(obj)) {
       obj = {
         scaleX: obj[0],
@@ -166,7 +175,11 @@ Morphable.TransformBag.defaults = {
 }
 
 Morphable.ObjectBag = class {
-  constructor (objOrArr) {
+  constructor (...args) {
+    this.init(...args)
+  }
+
+  init (objOrArr) {
     this.values = []
 
     if (Array.isArray(objOrArr)) {
@@ -218,7 +231,7 @@ extend(morphableTypes, {
       .to(val, args)
   },
   fromArray (arr) {
-    this.constructor(arr)
+    this.init(arr)
     return this
   }
 })
index ce22f803055583dc8c0d2fe35a872d2df2247d10..e2c725c0e7abdfdbfe31852e720bc37003137fe5 100644 (file)
-import {makeInstance} from './helpers.js'
-import Element from './Element.js'
-import {adopt} from './tools.js'
+import {makeInstance, adopt} from './adopter.js'
 import {map} from './utils.js'
 
-export default class Parent extends Element {
-  // Returns all child elements
-  children () {
-    return map(this.node.children, function (node) {
-      return adopt(node)
-    })
+
+// Returns all child elements
+export function children () {
+  return map(this.node.children, function (node) {
+    return adopt(node)
+  })
+}
+
+// Add given element at a position
+export function add (element, i) {
+  element = makeInstance(element)
+
+  if (element.node !== this.node.children[i]) {
+    this.node.insertBefore(element.node, this.node.children[i] || null)
   }
 
-  // Add given element at a position
-  add (element, i) {
-    element = makeInstance(element)
+  return this
+}
+
+// Basically does the same as `add()` but returns the added element instead
+export function put (element, i) {
+  this.add(element, i)
+  return element.instance || element
+}
+
+// Checks if the given element is a child
+export function has (element) {
+  return this.index(element) >= 0
+}
+
+// Gets index of given element
+export function index (element) {
+  return [].slice.call(this.node.children).indexOf(element.node)
+}
+
+// Get a element at the given index
+export function get (i) {
+  return adopt(this.node.children[i])
+}
+
+// Get first child
+export function first () {
+  return this.get(0)
+}
 
-    if (element.node !== this.node.children[i]) {
-      this.node.insertBefore(element.node, this.node.children[i] || null)
+// Get the last child
+export function last () {
+  return this.get(this.node.children.length - 1)
+}
+
+// Iterates over all children and invokes a given block
+export function each (block, deep) {
+  var children = this.children()
+  var i, il
+
+  for (i = 0, il = children.length; i < il; i++) {
+    if (children[i] instanceof Base) {
+      block.apply(children[i], [i, children])
     }
 
-    return this
+    if (deep && (children[i] instanceof Base && children[i].is('Parent'))) {
+      children[i].each(block, deep)
+    }
   }
 
-  // Basically does the same as `add()` but returns the added element instead
-  put (element, i) {
-    this.add(element, i)
-    return element.instance || element
-  }
+  return this
+}
 
-  // Checks if the given element is a child
-  has (element) {
-    return this.index(element) >= 0
-  }
+// Remove a given child
+export function removeElement (element) {
+  this.node.removeChild(element.node)
 
-  // Gets index of given element
-  index (element) {
-    return [].slice.call(this.node.children).indexOf(element.node)
-  }
+  return this
+}
 
-  // Get a element at the given index
-  get (i) {
-    return adopt(this.node.children[i])
+// Remove all elements in this container
+export function clear () {
+  // remove children
+  while (this.node.hasChildNodes()) {
+    this.node.removeChild(this.node.lastChild)
   }
 
-  // Get first child
-  first () {
-    return this.get(0)
-  }
+  // remove defs reference
+  delete this._defs
 
-  // Get the last child
-  last () {
-    return this.get(this.node.children.length - 1)
-  }
+  return this
+}
 
-  // Iterates over all children and invokes a given block
-  each (block, deep) {
-    var children = this.children()
-    var i, il
+// Import raw svg
+export function svg (svg) {
+  var well, len
 
-    for (i = 0, il = children.length; i < il; i++) {
-      if (children[i] instanceof Element) {
-        block.apply(children[i], [i, children])
-      }
+  // act as a setter if svg is given
+  if (svg) {
+    // create temporary holder
+    well = document.createElementNS(ns, 'svg')
+    // dump raw svg
+    well.innerHTML = svg
 
-      if (deep && (children[i] instanceof Parent)) {
-        children[i].each(block, deep)
-      }
+    // transplant nodes
+    for (len = well.children.length; len--;) {
+      this.node.appendChild(well.firstElementChild)
     }
 
-    return this
+  // otherwise act as a getter
+  } else {
+    // write svgjs data to the dom
+    this.writeDataToDom()
+
+    return this.node.outerHTML
   }
 
-  // Remove a given child
-  removeElement (element) {
-    this.node.removeChild(element.node)
+  return this
+}
+
+// write svgjs data to the dom
+export function writeDataToDom () {
+  // dump variables recursively
+  this.each(function () {
+    this.writeDataToDom()
+  })
 
-    return this
+  // remove previously set data
+  this.node.removeAttribute('svgjs:data')
+
+  if (Object.keys(this.dom).length) {
+    this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)) // see #428
   }
+  return this
+}
 
-  // Remove all elements in this container
-  clear () {
-    // remove children
-    while (this.node.hasChildNodes()) {
-      this.node.removeChild(this.node.lastChild)
-    }
+export function flatten (parent) {
+  this.each(function () {
+    if (this.is('Parent')) return this.flatten(parent).ungroup(parent)
+    return this.toParent(parent)
+  })
 
-    // remove defs reference
-    delete this._defs
+  // we need this so that Doc does not get removed
+  this.node.firstElementChild || this.remove()
 
-    return this
-  }
+  return this
+}
+
+export function ungroup (parent) {
+  parent = parent || this.parent()
+
+  this.each(function () {
+    return this.toParent(parent)
+  })
+
+  this.remove()
+
+  return this
 }
index 72269778d27eca442509f26154181baa4089b78a..b06da5b06a49ee3604d69b4855e5b81808cb75f7 100644 (file)
@@ -1,12 +1,12 @@
 import {proportionalSize} from './helpers.js'
 import {nodeOrNew} from './tools.js'
-import Shape from './Shape.js'
+import Base from './Base.js'
 import PathArray from './PathArray.js'
 
-export default class Path extends Shape {
+export default class Path extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('path', node))
+    super(nodeOrNew('path', node), Path)
   }
 
   // Get array
@@ -61,11 +61,13 @@ export default class Path extends Shape {
 // Define morphable array
 Path.prototype.MorphArray = PathArray
 
-  // Add parent method
-addFactory(Container, {
-  // Create a wrapped path element
-  path (d) {
-    // make sure plot is called as a setter
-    return this.put(new Path()).plot(d || new PathArray())
+// Add parent method
+Path.constructors = {
+  Container: {
+    // Create a wrapped path element
+    path (d) {
+      // make sure plot is called as a setter
+      return this.put(new Path()).plot(d || new PathArray())
+    }
   }
-})
+}
index 24d8665b8ca96eccb87ddf75e4dbfe6210fca174..f6c22d1b94e6a5f1c0d1c12d49097825634b7366 100644 (file)
@@ -2,6 +2,7 @@ import {arrayToString, pathRegReplace} from './helpers.js'
 import parser from './parser.js'
 import {numbersWithDots, pathLetters, hyphen, delimiter, isPathLetter} from './regex.js'
 import Point from './Point.js'
+import SVGArray from './SVGArray.js'
 
 let pathHandlers = {
   M: function (c, p, p0) {
@@ -83,11 +84,11 @@ export default class PathArray extends SVGArray {
 
   // Convert array to string
   toString () {
-    return arrayToString(this.value)
+    return arrayToString(this)
   }
 
   toArray () {
-    return this.value.reduce(function (prev, curr) {
+    return this.reduce(function (prev, curr) {
       return [].concat.call(prev, curr)
     }, [])
   }
@@ -103,29 +104,29 @@ export default class PathArray extends SVGArray {
 
     if (!isNaN(x) && !isNaN(y)) {
       // move every point
-      for (var l, i = this.value.length - 1; i >= 0; i--) {
-        l = this.value[i][0]
+      for (var l, i = this.length - 1; i >= 0; i--) {
+        l = this[i][0]
 
         if (l === 'M' || l === 'L' || l === 'T') {
-          this.value[i][1] += x
-          this.value[i][2] += y
+          this[i][1] += x
+          this[i][2] += y
         } else if (l === 'H') {
-          this.value[i][1] += x
+          this[i][1] += x
         } else if (l === 'V') {
-          this.value[i][1] += y
+          this[i][1] += y
         } else if (l === 'C' || l === 'S' || l === 'Q') {
-          this.value[i][1] += x
-          this.value[i][2] += y
-          this.value[i][3] += x
-          this.value[i][4] += y
+          this[i][1] += x
+          this[i][2] += y
+          this[i][3] += x
+          this[i][4] += y
 
           if (l === 'C') {
-            this.value[i][5] += x
-            this.value[i][6] += y
+            this[i][5] += x
+            this[i][6] += y
           }
         } else if (l === 'A') {
-          this.value[i][6] += x
-          this.value[i][7] += y
+          this[i][6] += x
+          this[i][7] += y
         }
       }
     }
@@ -140,34 +141,34 @@ export default class PathArray extends SVGArray {
     var i, l
 
     // recalculate position of all points according to new size
-    for (i = this.value.length - 1; i >= 0; i--) {
-      l = this.value[i][0]
+    for (i = this.length - 1; i >= 0; i--) {
+      l = this[i][0]
 
       if (l === 'M' || l === 'L' || l === 'T') {
-        this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x
-        this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y
+        this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
+        this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y
       } else if (l === 'H') {
-        this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x
+        this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
       } else if (l === 'V') {
-        this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y
+        this[i][1] = ((this[i][1] - box.y) * height) / box.height + box.y
       } else if (l === 'C' || l === 'S' || l === 'Q') {
-        this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x
-        this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y
-        this.value[i][3] = ((this.value[i][3] - box.x) * width) / box.width + box.x
-        this.value[i][4] = ((this.value[i][4] - box.y) * height) / box.height + box.y
+        this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
+        this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y
+        this[i][3] = ((this[i][3] - box.x) * width) / box.width + box.x
+        this[i][4] = ((this[i][4] - box.y) * height) / box.height + box.y
 
         if (l === 'C') {
-          this.value[i][5] = ((this.value[i][5] - box.x) * width) / box.width + box.x
-          this.value[i][6] = ((this.value[i][6] - box.y) * height) / box.height + box.y
+          this[i][5] = ((this[i][5] - box.x) * width) / box.width + box.x
+          this[i][6] = ((this[i][6] - box.y) * height) / box.height + box.y
         }
       } else if (l === 'A') {
         // resize radii
-        this.value[i][1] = (this.value[i][1] * width) / box.width
-        this.value[i][2] = (this.value[i][2] * height) / box.height
+        this[i][1] = (this[i][1] * width) / box.width
+        this[i][2] = (this[i][2] * height) / box.height
 
         // move position values
-        this.value[i][6] = ((this.value[i][6] - box.x) * width) / box.width + box.x
-        this.value[i][7] = ((this.value[i][7] - box.y) * height) / box.height + box.y
+        this[i][6] = ((this[i][6] - box.x) * width) / box.width + box.x
+        this[i][7] = ((this[i][7] - box.y) * height) / box.height + box.y
       }
     }
 
@@ -180,9 +181,9 @@ export default class PathArray extends SVGArray {
 
     pathArray = new PathArray(pathArray)
 
-    equalCommands = this.value.length === pathArray.value.length
-    for (i = 0, il = this.value.length; equalCommands && i < il; i++) {
-      equalCommands = this.value[i][0] === pathArray.value[i][0]
+    equalCommands = this.length === pathArray.value.length
+    for (i = 0, il = this.length; equalCommands && i < il; i++) {
+      equalCommands = this[i][0] === pathArray.value[i][0]
     }
 
     return equalCommands
@@ -206,7 +207,7 @@ export default class PathArray extends SVGArray {
     // make sure a destination is defined
     if (!this.destination) return this
 
-    var sourceArray = this.value
+    var sourceArray = this
     var destinationArray = this.destination.value
     var array = []
     var pathArray = new PathArray()
@@ -246,11 +247,11 @@ export default class PathArray extends SVGArray {
 
     if (typeof array === 'string') {
       array = array
-        .replace(regex.numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
-        .replace(regex.pathLetters, ' $& ') // put some room between letters and numbers
-        .replace(regex.hyphen, '$1 -')      // add space before hyphen
+        .replace(numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
+        .replace(pathLetters, ' $& ') // put some room between letters and numbers
+        .replace(hyphen, '$1 -')      // add space before hyphen
         .trim()                                 // trim
-        .split(regex.delimiter)   // split into array
+        .split(delimiter)   // split into array
     } else {
       array = array.reduce(function (prev, curr) {
         return [].concat.call(prev, curr)
@@ -266,7 +267,7 @@ export default class PathArray extends SVGArray {
 
     do {
       // Test if we have a path letter
-      if (regex.isPathLetter.test(array[index])) {
+      if (isPathLetter.test(array[index])) {
         s = array[index]
         ++index
       // If last letter was a move command and we got no new, it defaults to [L]ine
@@ -286,7 +287,7 @@ export default class PathArray extends SVGArray {
     return result
   }
 
-  // Get bounding box of path
+  Get bounding box of path
   bbox () {
     parser().path.setAttribute('d', this.toString())
     return parser.nodes.path.getBBox()
index 00f9de5a04eeb7206944381f2fd96986a774c55e..add8b713978f342060be8fb04808374c8c6e031c 100644 (file)
@@ -1,7 +1,8 @@
-import {Container, Defs} from './classes.js'
+import Base from './Base.js'
 import {nodeOrNew} from './tools.js'
+import attr from './attr.js'
 
-export default class Pattern extends Container {
+export default class Pattern extends Base {
   // Initialize node
   constructor (node) {
     super(nodeOrNew('pattern', node))
@@ -11,6 +12,7 @@ export default class Pattern extends Container {
   url () {
     return 'url(#' + this.id() + ')'
   }
+
   // Update pattern by rebuilding
   update (block) {
     // remove content
@@ -23,34 +25,35 @@ export default class Pattern extends Container {
 
     return this
   }
+
   // Alias string convertion to fill
   toString () {
     return this.url()
   }
+
   // custom attr to handle transform
   attr (a, b, c) {
     if (a === 'transform') a = 'patternTransform'
-    return super.attr(a, b, c)
+    return attr.call(this, a, b, c)
   }
 }
 
-  // Add parent method
-addFactory(Container, {
-  // Create pattern element in defs
-  pattern (width, height, block) {
-    return this.defs().pattern(width, height, block)
-  }
-})
-
-extend(Defs, {
-  // Define gradient
-  pattern (width, height, block) {
-    return this.put(new Pattern()).update(block).attr({
-      x: 0,
-      y: 0,
-      width: width,
-      height: height,
-      patternUnits: 'userSpaceOnUse'
-    })
+Pattern.constructors = {
+  Container: {
+    // Create pattern element in defs
+    pattern (width, height, block) {
+      return this.defs().pattern(width, height, block)
+    }
+  },
+  Defs: {
+    pattern (width, height, block) {
+      return this.put(new Pattern()).update(block).attr({
+        x: 0,
+        y: 0,
+        width: width,
+        height: height,
+        patternUnits: 'userSpaceOnUse'
+      })
+    }
   }
-})
+}
index ff184734e508685467fed79ac5419f08b2224ce8..588016884c29f0475b91dd8bd880248dbd8fd533 100644 (file)
@@ -1,5 +1,4 @@
 import parser from './parser.js'
-import Element from './Element.js'
 
 export default class Point {
   // Initialize
@@ -44,9 +43,11 @@ export default class Point {
   }
 }
 
-extend(Element, {
-  // Get point
-  point: function (x, y) {
-    return new Point(x, y).transform(this.screenCTM().inverse())
+Point.constructors = {
+  Element: {
+    // Get point
+    point: function (x, y) {
+      return new Point(x, y).transform(this.screenCTM().inverse())
+    }
   }
-})
+}
index 112d33b30a39bdab5960b70d81aa25f956698548..3f7f9487c3593dc86ae0a76c1a564900c21c7b17 100644 (file)
@@ -1,55 +1,26 @@
 import {proportionalSize} from './helpers.js'
-import Shape from './Shape.js'
-import {nodeOrNew} from './tools.js'
+import Base from './Base.js'
+import {nodeOrNew, extend} from './tools.js'
 import * as pointed from './pointed.js'
+import * as poly from './poly.js'
 import PointArray from './PointArray.js'
 
-export default class Polygon extends Shape {
+export default class Polygon extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('polygon', node))
+    super(nodeOrNew('polygon', node), Polygon)
   }
 }
 
-addFactory(Parent, {
-  // Create a wrapped polygon element
-  polygon (p) {
-    // make sure plot is called as a setter
-    return this.put(new Polygon()).plot(p || new PointArray())
+Polygon.constructors = {
+  Parent: {
+    // Create a wrapped polygon element
+    polygon (p) {
+      // make sure plot is called as a setter
+      return this.put(new Polygon()).plot(p || new PointArray())
+    }
   }
-})
-
+}
 
-// // Add polygon-specific functions
-// extend([Polyline, Polygon], {
-//   // Get array
-//   array: function () {
-//     return this._array || (this._array = new PointArray(this.attr('points')))
-//   },
-//
-//   // Plot new path
-//   plot: function (p) {
-//     return (p == null) ? this.array()
-//       : this.clear().attr('points', typeof p === 'string' ? p
-//       : (this._array = new PointArray(p)))
-//   },
-//
-//   // Clear array cache
-//   clear: function () {
-//     delete this._array
-//     return this
-//   },
-//
-//   // Move by left top corner
-//   move: function (x, y) {
-//     return this.attr('points', this.array().move(x, y))
-//   },
-//
-//   // Set element size to given width and height
-//   size: function (width, height) {
-//     let p = proportionalSize(this, width, height)
-//     return this.attr('points', this.array().size(p.width, p.height))
-//   }
-// })
-//
-// extend([Polyline, Polygon], pointed)
+extend(Polygon, pointed)
+extend(Polygon, poly)
index 9c28438773c50549884e10d8e676d777222e4c2d..c3c8c0c38c3a134e5d516026eee3e9a555d110ac 100644 (file)
@@ -1,19 +1,25 @@
-import Shape from './Shape.js'
-import {nodeOrNew} from './tools.js'
+import Base from './Base.js'
+import {nodeOrNew, extend} from './tools.js'
 import PointArray from './PointArray.js'
+import * as pointed from './pointed.js'
+import * as poly from './poly.js'
 
-export default class Polyline extends Shape {
+export default class Polyline extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('polyline', node))
+    super(nodeOrNew('polyline', node), Polyline)
   }
 }
 
-// Add parent method
-addFactory (Parent, {
-  // Create a wrapped polyline element
-  polyline (p) {
-    // make sure plot is called as a setter
-    return this.put(new Polyline()).plot(p || new PointArray())
+Polyline.constructors = {
+  Parent: {
+    // Create a wrapped polygon element
+    polyline (p) {
+      // make sure plot is called as a setter
+      return this.put(new Polyline()).plot(p || new PointArray())
+    }
   }
-})
+}
+
+extend(Polyline, pointed)
+extend(Polyline, poly)
index 825adeb93aff80272401239b97b995b77cc5c393..f83243c985f53b50a3299b15de26aedc4a26f415 100644 (file)
@@ -1,16 +1,23 @@
-import Shape from './Shape.js'
-import {nodeOrNew} from './tools.js'
+import Base from './Base.js'
+import {nodeOrNew, extend} from './tools.js'
+import * as EventTarget from './EventTarget.js'
+import * as Element from './Element.js'
+import * as Parent from './Parent.js'
 
-export default class Rect extends Shape {
+export default class Rect extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('rect', node))
+    super(nodeOrNew('rect', node), Rect)
   }
 }
 
-addFactory(Parent, {
-  // Create a rect element
-  rect (width, height) {
-    return this.put(new Rect()).size(width, height)
+extend(Rect, [EventTarget, Element, Parent])
+
+Rect.constructors = {
+  Container: {
+    // Create a rect element
+    rect (width, height) {
+      return this.put(new Rect()).size(width, height)
+    }
   }
-})
+}
index c29c72c8792e53218ced1ba5c4e5f84ca367cb73..9633c90cc961fd7964ab1ee8a2583e9e7ce26bbd 100644 (file)
@@ -2,10 +2,12 @@ import {isMatrixLike, getOrigin} from './helpers.js'
 import Matrix from './Matrix.js'
 import Morphable from './Morphable.js'
 import SVGNumber from './SVGNumber.js'
-import Element from './Element.js'
 import Timeline from './Timeline.js'
 import {Controller, Ease, Stepper} from './Controller.js'
 import {noop, timeline} from './defaults.js'
+import {extend} from './tools.js'
+import Animator from './Animator.js'
+import Point from './Point.js'
 
 // FIXME: What is this doing here?
 // easing = {
@@ -15,7 +17,7 @@ import {noop, timeline} from './defaults.js'
 //   '<': function (pos) { return -Math.cos(pos * Math.PI / 2) + 1 }
 // }
 
-export default class Runner extends EventTarget {
+export default class Runner {
   constructor (options) {
     // Store a unique id on the runner, so that we can identify it later
     this.id = Runner.id++
@@ -435,22 +437,6 @@ export default class Runner extends EventTarget {
 
 Runner.id = 0
 
-extend(Element, {
-  animate (duration, delay, when) {
-    var o = Runner.sanitise(duration, delay, when)
-    var timeline = this.timeline()
-    return new Runner(o.duration)
-      .loop(o)
-      .element(this)
-      .timeline(timeline)
-      .schedule(delay, when)
-  },
-
-  delay (by, when) {
-    return this.animate(0, by, when)
-  }
-})
-
 class FakeRunner{
   constructor (transforms = new Matrix(), id = -1, done = true) {
     this.transforms = transforms
@@ -557,44 +543,60 @@ class RunnerArray {
   }
 }
 
-extend(Element, {
-  // this function searches for all runners on the element and deletes the ones
-  // which run before the current one. This is because absolute transformations
-  // overwfrite anything anyway so there is no need to waste time computing
-  // other runners
-  _clearTransformRunnersBefore (currentRunner) {
-    this._transformationRunners.clearBefore(currentRunner.id)
-  },
-
-  _currentTransform (current) {
-    return this._transformationRunners.runners
-      // we need the equal sign here to make sure, that also transformations
-      // on the same runner which execute before the current transformation are
-      // taken into account
-      .filter((runner) => runner.id <= current.id)
-      .map(getRunnerTransform)
-      .reduce(lmultiply, new Matrix())
-  },
-
-  addRunner (runner) {
-    this._transformationRunners.add(runner)
-
-    Animator.transform_frame(
-      mergeTransforms.bind(this), this._frameId
-    )
-  },
-
-  _prepareRunner () {
-    if (this._frameId == null) {
-      this._transformationRunners = new RunnerArray()
-        .add(new FakeRunner(new Matrix(this)))
-
-      this._frameId = Element.frameId++
+let frameId = 0
+Runner.constructors = {
+  Element: {
+    animate (duration, delay, when) {
+      var o = Runner.sanitise(duration, delay, when)
+      var timeline = this.timeline()
+      return new Runner(o.duration)
+        .loop(o)
+        .element(this)
+        .timeline(timeline)
+        .schedule(delay, when)
+    },
+
+    delay (by, when) {
+      return this.animate(0, by, when)
+    },
+
+    // this function searches for all runners on the element and deletes the ones
+    // which run before the current one. This is because absolute transformations
+    // overwfrite anything anyway so there is no need to waste time computing
+    // other runners
+    _clearTransformRunnersBefore (currentRunner) {
+      this._transformationRunners.clearBefore(currentRunner.id)
+    },
+
+    _currentTransform (current) {
+      return this._transformationRunners.runners
+        // we need the equal sign here to make sure, that also transformations
+        // on the same runner which execute before the current transformation are
+        // taken into account
+        .filter((runner) => runner.id <= current.id)
+        .map(getRunnerTransform)
+        .reduce(lmultiply, new Matrix())
+    },
+
+    addRunner (runner) {
+      this._transformationRunners.add(runner)
+
+      Animator.transform_frame(
+        mergeTransforms.bind(this), this._frameId
+      )
+    },
+
+    _prepareRunner () {
+      if (this._frameId == null) {
+        this._transformationRunners = new RunnerArray()
+          .add(new FakeRunner(new Matrix(this)))
+
+        this._frameId = frameId++
+      }
     }
   }
-})
+}
 
-Element.frameId = 0
 
 extend(Runner, {
   attr (a, v) {
index e7881df86fe29be714b05855d24c894a2f5e9ede..927383393acee26a08295e3bd8800723a37d8ee5 100644 (file)
@@ -12,8 +12,14 @@ let BaseArray = (function() {
 })()
 
 export default class SVGArray extends BaseArray {
-  constructor (array, fallback) {
+  constructor (...args) {
     super()
+    this.init(...args)
+  }
+
+  init (array, fallback) {
+    //this.splice(0, this.length)
+    this.length = 0
     this.push(...this.parse(array || fallback))
   }
 
index cb1fd2835084a2f8dab0c107c0e276e6292b25bc..e07b521d06ae69e1d6b3a0529644f3a655307cc4 100644 (file)
@@ -3,7 +3,11 @@ import {numberAndUnit} from './regex.js'
 // Module for unit convertions
 export default class SVGNumber {
   // Initialize
-  constructor (value, unit) {
+  constructor (...args) {
+    this.init(...args)
+  }
+
+  init (value, unit) {
     unit = Array.isArray(value) ? value[1] : unit
     value = Array.isArray(value) ? value[0] : value
 
diff --git a/src/Shape.js b/src/Shape.js
deleted file mode 100644 (file)
index d73ffb6..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-import Element from './Element.js'
-
-export default class Shape extends Element { }
index 6bce999a203e015de73cfab19ee8f5951d053f7b..1631b4a8abaa6d20dc652746fe44f50fd6363d47 100644 (file)
@@ -1,10 +1,10 @@
-import Element from './Element.js'
+import Base from './Base.js'
 import SVGNumber from './SVGNumber.js'
 import {nodeOrNew} from './tools.js'
 
-export default class Stop extends Element {
+export default class Stop extends Base {
   constructor (node) {
-    super(nodeOrNew('stop', node))
+    super(nodeOrNew('stop', node), Stop)
   }
 
   // add color stops
index 98d10ef17141c6c01311cd575c45c20a225788a7..e9936e2d5c66f2828687d8144fbf343a39491c56 100644 (file)
@@ -1,16 +1,17 @@
-import Container from './Container.js'
+import Base from './Base.js'
 import {nodeOrNew} from './tools.js'
 
-export default class Symbol extends Container {
+export default class Symbol extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('symbol', node))
+    super(nodeOrNew('symbol', node), Symbol)
   }
 }
 
-addFactory(Container, {
-  // create symbol
-  symbol () {
-    return this.put(new Symbol())
+Symbol.constructors = {
+  Container: {
+    symbol () {
+      return this.put(new Symbol())
+    }
   }
-})
+}
index 3d9f074bc89f927ba484763c91e2184a2cb4197e..3d7fd1de0018e14ae5879a8c71b529dc56d1e92c 100644 (file)
@@ -1,12 +1,13 @@
-import Parent from './Parent.js'
+import Base from './Base.js'
 import SVGNumber from './SVGNumber.js'
-import {nodeOrNew, adopt} from './tools.js'
+import {nodeOrNew, extend} from './tools.js'
 import {attrs} from './defaults.js'
+import * as textable from './textable.js'
 
-export default class Text extends Parent {
+export default class Text extends Base {
   // Initialize node
   constructor (node) {
-    super(nodeOrNew('text', node))
+    super(nodeOrNew('text', node), Text)
 
     this.dom.leading = new SVGNumber(1.3)    // store leading value for rebuilding
     this._rebuild = true                      // enable automatic updating of dy values
@@ -155,90 +156,18 @@ export default class Text extends Parent {
   }
 }
 
+extend(Text, textable)
 
-addFactory(Parent, {
-  // Create text element
-  text (text) {
-    return this.put(new Text()).text(text)
-  },
-
-  // Create plain text element
-  plain (text) {
-    return this.put(new Text()).plain(text)
-  }
-})
+Text.constructors = {
+  Container: {
+    // Create text element
+    text (text) {
+      return this.put(new Text()).text(text)
+    },
 
-
-class Tspan extends Parent {
-  // Initialize node
-  constructor (node) {
-    super(nodeOrNew('tspan', node))
-  }
-
-  // Set text content
-  text (text) {
-    if (text == null) return this.node.textContent + (this.dom.newLined ? '\n' : '')
-
-    typeof text === 'function' ? text.call(this, this) : this.plain(text)
-
-    return this
-  }
-
-  // Shortcut dx
-  dx (dx) {
-    return this.attr('dx', dx)
-  }
-
-  // Shortcut dy
-  dy (dy) {
-    return this.attr('dy', dy)
-  }
-
-  // Create new line
-  newLine () {
-    // fetch text parent
-    var t = this.parent(Text)
-
-    // mark new line
-    this.dom.newLined = true
-
-    // apply new position
-    return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x())
-  }
-}
-
-extend([Text, Tspan], {
-  // Create plain text node
-  plain: function (text) {
-    // clear if build mode is disabled
-    if (this._build === false) {
-      this.clear()
-    }
-
-    // create text node
-    this.node.appendChild(document.createTextNode(text))
-
-    return this
-  },
-
-  // Create a tspan
-  tspan: function (text) {
-    var tspan = new Tspan()
-
-    // clear if build mode is disabled
-    if (!this._build) {
-      this.clear()
+    // Create plain text element
+    plain (text) {
+      return this.put(new Text()).plain(text)
     }
-
-    // add new tspan
-    this.node.appendChild(tspan.node)
-
-    return tspan.text(text)
-  },
-
-  // FIXME: Does this also work for textpath?
-  // Get length of text element
-  length: function () {
-    return this.node.getComputedTextLength()
   }
-})
+}
index cd1757efe46d424b04f06dfb7e861b480a1523f5..c4685ecc2d3812a78583b9fb52e1765410b1e158 100644 (file)
@@ -33,48 +33,47 @@ export default class TextPath extends Text {
   }
 }
 
-addFactory(Parent, {
-  textPath (text, path) {
-    return this.defs().path(path).text(text).addTo(this)
-  }
-})
-
-
-extend([Text], {
-    // Create path for text to run on
-  path: function (track) {
-    var path = new TextPath()
-
-    // if d is a path, reuse it
-    if (!(track instanceof Path)) {
-      // create path element
-      track = this.doc().defs().path(track)
+TextPath.constructors = {
+  Container: {
+    textPath (text, path) {
+      return this.defs().path(path).text(text).addTo(this)
     }
-
-    // link textPath to path and add content
-    path.attr('href', '#' + track, xlink)
-
-    // add textPath element as child node and return textPath
-    return this.put(path)
   },
-
-  // FIXME: make this plural?
-  // Get the textPath children
-  textPath: function () {
-    return this.select('textPath')
-  }
-})
-
-extend([Path], {
-  // creates a textPath from this path
-  text: function (text) {
-    if (text instanceof Text) {
-      var txt = text.text()
-      return text.clear().path(this).text(txt)
+  Text: {
+      // Create path for text to run on
+    path: function (track) {
+      var path = new TextPath()
+
+      // if d is a path, reuse it
+      if (!(track instanceof Path)) {
+        // create path element
+        track = this.doc().defs().path(track)
+      }
+
+      // link textPath to path and add content
+      path.attr('href', '#' + track, xlink)
+
+      // add textPath element as child node and return textPath
+      return this.put(path)
+    },
+
+    // FIXME: make this plural?
+    // Get the textPath children
+    textPath: function () {
+      return this.select('textPath')
     }
-    return this.parent().put(new Text()).path(this).text(text)
+  },
+  Path: {
+    // creates a textPath from this path
+    text: function (text) {
+      if (text instanceof Text) {
+        var txt = text.text()
+        return text.clear().path(this).text(txt)
+      }
+      return this.parent().put(new Text()).path(this).text(text)
+    }
+    // FIXME: Maybe add `targets` to get all textPaths associated with this path
   }
-  // FIXME: Maybe add `targets` to get all textPaths associated with this path
-})
+}
 
 TextPath.prototype.MorphArray = PathArray
index 01a8e2203a7626ab78f22c91789fb8f37c0f769b..fc19db86fdee8579aae6323461f08db798fa5d9c 100644 (file)
@@ -1,4 +1,3 @@
-import EventTarget from './EventTarget.js'
 import Animator from './Animator.js'
 
 var time = window.performance || Date
@@ -10,14 +9,14 @@ var makeSchedule = function (runnerInfo) {
   return {start: start, duration: duration, end: end, runner: runnerInfo.runner}
 }
 
-export default class Timeline extends EventTarget {
+export default class Timeline {
   // Construct a new timeline on the given element
   constructor () {
     this._timeSource = function () {
       return time.now()
     }
 
-    this._dispatcher = document.makeInstance('div')
+    this._dispatcher = document.createElement('div')
 
     // Store the timing variables
     this._startTime = 0
@@ -261,9 +260,11 @@ export default class Timeline extends EventTarget {
   }
 }
 
-extend(Element, {
-  timeline: function () {
-    this._timeline = (this._timeline || new Timeline())
-    return this._timeline
+Timeline.constructors = {
+  Element: {
+    timeline: function () {
+      this._timeline = (this._timeline || new Timeline())
+      return this._timeline
+    }
   }
-})
+}
diff --git a/src/Tspan.js b/src/Tspan.js
new file mode 100644 (file)
index 0000000..f5030c9
--- /dev/null
@@ -0,0 +1,43 @@
+import Base from './Base.js'
+import {nodeOrNew, extend} from './tools.js'
+import * as textable from './textable.js'
+
+export default class Tspan extends Base {
+  // Initialize node
+  constructor (node) {
+    super(nodeOrNew('tspan', node), Tspan)
+  }
+
+  // Set text content
+  text (text) {
+    if (text == null) return this.node.textContent + (this.dom.newLined ? '\n' : '')
+
+    typeof text === 'function' ? text.call(this, this) : this.plain(text)
+
+    return this
+  }
+
+  // Shortcut dx
+  dx (dx) {
+    return this.attr('dx', dx)
+  }
+
+  // Shortcut dy
+  dy (dy) {
+    return this.attr('dy', dy)
+  }
+
+  // Create new line
+  newLine () {
+    // fetch text parent
+    var t = this.parent(Text)
+
+    // mark new line
+    this.dom.newLined = true
+
+    // apply new position
+    return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x())
+  }
+}
+
+extend(Tspan, textable)
index 9cf1711b8a9ab393b6a52611b7dd2dd3efc9ed59..5c4fe8e163ff8200ebb769839fede4366a20475d 100644 (file)
@@ -1,9 +1,9 @@
-import {Shape, Container} from './classes.js'
+import Base from './Base.js'
 import {xlink} from './namespaces.js'
 
-export default class Use extends Shape {
+export default class Use extends Base {
   constructor (node) {
-    super(nodeOrNew('use', node))
+    super(nodeOrNew('use', node), Use)
   }
 
   // Use element as a reference
@@ -13,9 +13,11 @@ export default class Use extends Shape {
   }
 }
 
-addFactory(Container, {
-  // Create a use element
-  use: function (element, file) {
-    return this.put(new Use()).element(element, file)
+Use.constructors = {
+  Container: {
+    // Create a use element
+    use: function (element, file) {
+      return this.put(new Use()).element(element, file)
+    }
   }
-})
+}
diff --git a/src/adopter.js b/src/adopter.js
new file mode 100644 (file)
index 0000000..81969bd
--- /dev/null
@@ -0,0 +1,56 @@
+import Base from './Base.js'
+import * as elements from './elements.js'
+import {capitalize} from './helpers.js'
+import HtmlNode from './HtmlNode.js'
+
+export function makeInstance (element) {
+  if (element instanceof Base) return element
+
+  if (typeof element === 'object') {
+    return adopt(element)
+  }
+
+  if (element == null) {
+    return new Doc()
+  }
+
+  if (typeof element === 'string' && element.charAt(0) !== '<') {
+    return adopt(document.querySelector(element))
+  }
+
+  var node = makeNode('svg')
+  node.innerHTML = element
+
+  element = adopt(node.firstElementChild)
+
+  return element
+}
+
+// Adopt existing svg elements
+export function adopt (node) {
+  // check for presence of node
+  if (!node) return null
+
+  // make sure a node isn't already adopted
+  if (node.instance instanceof Element) return node.instance
+
+  if (!(node instanceof window.SVGElement)) {
+    return new HtmlNode(node)
+  }
+
+  // initialize variables
+  var element
+
+  // adopt with element-specific settings
+  if (node.nodeName === 'svg') {
+    element = new elements.Doc(node)
+  } else if (node.nodeName === 'linearGradient' || node.nodeName === 'radialGradient') {
+    element = new elements.Gradient(node)
+  } else if (elements[capitalize(node.nodeName)]) {
+    element = new elements[capitalize(node.nodeName)](node)
+  } else {
+    element = new elements.Bare(node)
+  }
+
+  return element
+}
index a0c95b4a56d7c717103acbdf7a568bc7ae004960..ddf4de267b959869efee33dcd23bfe528cc97868 100644 (file)
@@ -1,6 +1,9 @@
-import {isNumer, isImage} from './regex.js'
+import {isNumber, isImage} from './regex.js'
 import {attrs as defaults} from './defaults.js'
-import {Color, SVGArray, Image} from './classes.js'
+import Color from './Color.js'
+import SVGArray from './SVGArray.js'
+import Image from './Image.js'
+import SVGNumber from './SVGNumber.js'
 
 // Set svg element attribute
 export default function attr (attr, val, ns) {
@@ -21,8 +24,8 @@ export default function attr (attr, val, ns) {
     // FIXME: implement
   } else if (typeof attr === 'object') {
     // apply every attribute individually if an object is passed
-    for (val in a) this.attr(val, attr[val])
-  }else if (val === null) {
+    for (val in attr) this.attr(val, attr[val])
+  } else if (val === null) {
       // remove value
     this.node.removeAttribute(attr)
   } else if (val == null) {
@@ -34,7 +37,7 @@ export default function attr (attr, val, ns) {
   } else {
     // convert image fill and stroke to patterns
     if (attr === 'fill' || attr === 'stroke') {
-      if (isImage.test(v)) {
+      if (isImage.test(val)) {
         val = this.doc().defs().image(val)
       }
 
@@ -48,7 +51,7 @@ export default function attr (attr, val, ns) {
     // ensure correct numeric values (also accepts NaN and Infinity)
     if (typeof val === 'number') {
       val = new SVGNumber(val)
-    } else if (isColor(val)) {
+    } else if (Color.isColor(val)) {
       // ensure full hex color
       val = new Color(val)
     } else if (Array.isArray(val)) {
index df6515146c968a7d7dd62788912525e00f8d3b21..feebee223fdc594da513bf12916eabaa58f39f84 100644 (file)
@@ -1,13 +1,8 @@
-export {default as EventTarget} from './EventTarget.js'
-export {default as Element} from './Element.js'
 export {default as HtmlNode} from './HtmlNode.js'
-export {default as Parent} from './Parent.js'
-export {default as Container} from './Container.js'
 export {default as Doc} from './Doc.js'
 export {default as Defs} from './Defs.js'
 export {default as G} from './G.js'
 export {default as Animator} from './Animator.js'
-export {default as Shape} from './Shape.js'
 export {default as Bare} from './Bare.js'
 export {default as Circle} from './Circle.js'
 export {default as ClipPath} from './ClipPath.js'
diff --git a/src/containers.js b/src/containers.js
new file mode 100644 (file)
index 0000000..56287de
--- /dev/null
@@ -0,0 +1,11 @@
+export {default as Bare} from './Bare.js'
+export {default as ClipPath} from './ClipPath.js'
+export {default as Defs} from './Defs.js'
+export {default as Doc} from './Doc.js'
+export {default as Gradient} from './Gradient.js'
+export {default as G} from './G.js'
+export {default as A} from './A.js'
+export {default as Marker} from './Marker.js'
+export {default as Mask} from './Mask.js'
+export {default as Pattern} from './Pattern.js'
+export {default as Symbol} from './Symbol.js'
index e5b079475a5e8f2f111e06200395f31b22d20d11..b85c0fe751e44d70d24e9dc5ff8462469e1eaefb 100644 (file)
@@ -1,70 +1,66 @@
 import {camelCase} from './helpers.js'
-import Element from './Element.js'
-import {extend} from './tools.js'
 import {isBlank} from './regex.js'
 
-extend(Element, {
   // Dynamic style generator
-  css (style, val) {
-    let ret = {}
-    let i
-    if (arguments.length === 0) {
-      // get full style as object
-      this.node.style.cssText.split(/\s*;\s*/)
-        .filter(function (el) { return !!el.length })
-        .forEach(function (el) {
-        let t = el.split(/\s*:\s*/)
-        ret[t[0]] = t[1]
-      })
-      return ret
-    }
+export function css (style, val) {
+  let ret = {}
+  let i
+  if (arguments.length === 0) {
+    // get full style as object
+    this.node.style.cssText.split(/\s*;\s*/)
+      .filter(function (el) { return !!el.length })
+      .forEach(function (el) {
+      let t = el.split(/\s*:\s*/)
+      ret[t[0]] = t[1]
+    })
+    return ret
+  }
 
-    if (arguments.length < 2) {
-      // get style properties in the array
-      if (Array.isArray(style)) {
-        for (let name of style) {
-          let cased = camelCase(name)
-          ret[cased] = this.node.style(cased)
-        }
-        return ret
+  if (arguments.length < 2) {
+    // get style properties in the array
+    if (Array.isArray(style)) {
+      for (let name of style) {
+        let cased = camelCase(name)
+        ret[cased] = this.node.style(cased)
       }
+      return ret
+    }
 
-      // get style for property
-      if (typeof style === 'string') {
-        return this.node.style[camelCase(style)]
-      }
+    // get style for property
+    if (typeof style === 'string') {
+      return this.node.style[camelCase(style)]
+    }
 
-      // set styles in object
-      if (typeof style === 'object') {
-        for (name in style) {
-          // set empty string if null/undefined/'' was given
-          this.node.style[camelCase(name)] =
-            (style[name] == null || isBlank.test(style[name])) ? '' : style[name]
-        }
+    // set styles in object
+    if (typeof style === 'object') {
+      for (name in style) {
+        // set empty string if null/undefined/'' was given
+        this.node.style[camelCase(name)] =
+          (style[name] == null || isBlank.test(style[name])) ? '' : style[name]
       }
     }
+  }
 
-    // set style for property
-    if (arguments.length === 2) {
-      this.node.style[camelCase(style)] =
-        (val == null || isBlank.test(val)) ? '' : val
-    }
+  // set style for property
+  if (arguments.length === 2) {
+    this.node.style[camelCase(style)] =
+      (val == null || isBlank.test(val)) ? '' : val
+  }
 
-    return this
-  },
+  return this
+}
 
   // Show element
-  show () {
-    return this.css('display', '')
-  },
+export function show () {
+  return this.css('display', '')
+}
 
   // Hide element
-  hide () {
-    return this.css('display', 'none')
-  },
+export function hide () {
+  return this.css('display', 'none')
+}
 
   // Is element visible?
-  visible () {
-    return this.css('display') !== 'none'
-  }
-})
+export function visible () {
+  return this.css('display') !== 'none'
+}
index 530986d99ec2402cb511346ce5df766c88361d52..c1437728c10abc1240dda8ee5090adbdcd9dc06b 100644 (file)
@@ -1,27 +1,22 @@
-import Element from './Element.js'
-import {extend} from './tools.js'
-
-extend(Element, {
-  // Store data values on svg nodes
-  data (a, v, r) {
-    if (typeof a === 'object') {
-      for (v in a) {
-        this.data(v, a[v])
-      }
-    } else if (arguments.length < 2) {
-      try {
-        return JSON.parse(this.attr('data-' + a))
-      } catch (e) {
-        return this.attr('data-' + a)
-      }
-    } else {
-      this.attr('data-' + a,
-        v === null ? null
-        : r === true || typeof v === 'string' || typeof v === 'number' ? v
-        : JSON.stringify(v)
-      )
+// Store data values on svg nodes
+export function data (a, v, r) {
+  if (typeof a === 'object') {
+    for (v in a) {
+      this.data(v, a[v])
     }
-
-    return this
+  } else if (arguments.length < 2) {
+    try {
+      return JSON.parse(this.attr('data-' + a))
+    } catch (e) {
+      return this.attr('data-' + a)
+    }
+  } else {
+    this.attr('data-' + a,
+      v === null ? null
+      : r === true || typeof v === 'string' || typeof v === 'number' ? v
+      : JSON.stringify(v)
+    )
   }
-})
+
+  return this
+}
diff --git a/src/default.js b/src/default.js
deleted file mode 100644 (file)
index e82d1db..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-
-SVG.void = function () {}
-
-SVG.defaults = {
-
-  // Default animation values
-  timeline: {
-    duration: 400,
-    ease: '>',
-    delay: 0
-  },
-
-  // Default attribute values
-  attrs: {
-
-    // fill and stroke
-    'fill-opacity': 1,
-    'stroke-opacity': 1,
-    'stroke-width': 0,
-    'stroke-linejoin': 'miter',
-    'stroke-linecap': 'butt',
-    fill: '#000000',
-    stroke: '#000000',
-    opacity: 1,
-
-    // position
-    x: 0,
-    y: 0,
-    cx: 0,
-    cy: 0,
-
-    // size
-    width: 0,
-    height: 0,
-
-    // radius
-    r: 0,
-    rx: 0,
-    ry: 0,
-
-    // gradient
-    offset: 0,
-    'stop-opacity': 1,
-    'stop-color': '#000000',
-
-    // text
-    'font-size': 16,
-    'font-family': 'Helvetica, Arial, sans-serif',
-    'text-anchor': 'start'
-  }
-}
diff --git a/src/elements.js b/src/elements.js
new file mode 100644 (file)
index 0000000..4709c96
--- /dev/null
@@ -0,0 +1,24 @@
+export {default as Bare} from './Bare.js'
+export {default as Circle} from './Circle.js'
+export {default as ClipPath} from './ClipPath.js'
+export {default as Defs} from './Defs.js'
+export {default as Doc} from './Doc.js'
+export {default as Ellipse} from './Ellipse.js'
+export {default as Gradient} from './Gradient.js'
+export {default as G} from './G.js'
+export {default as HtmlNode} from './HtmlNode.js'
+export {default as A} from './A.js'
+export {default as Image} from './Image.js'
+export {default as Line} from './Line.js'
+export {default as Marker} from './Marker.js'
+export {default as Mask} from './Mask.js'
+export {default as Path} from './Path.js'
+export {default as Pattern} from './Pattern.js'
+export {default as Polygon} from './Polygon.js'
+export {default as Polyline} from './Polyline.js'
+export {default as Rect} from './Rect.js'
+export {default as Stop} from './Stop.js'
+export {default as Symbol} from './Symbol.js'
+export {default as Text} from './Text.js'
+export {default as TextPath} from './TextPath.js'
+export {default as Use} from './Use.js'
index 8d54782f6f77f421f480bdfa556fd2e0cc94b6c8..c7832aa66480e9e868d8cb59b6ca17599ec04ae0 100644 (file)
@@ -1,5 +1,3 @@
-import EventTarget from './EventTarget.js'
-import Element from './Element.js'
 import {delimiter} from './regex.js'
 
 // // Add events to elements
@@ -30,10 +28,16 @@ import {delimiter} from './regex.js'
 
 let listenerId = 0
 
+function getEventTarget (node) {
+  return node instanceof Base && node.is('EventTarget')
+    ? node.getEventTarget()
+    : node
+}
+
 // Add event binder in the SVG namespace
 export function on (node, events, listener, binding, options) {
   var l = listener.bind(binding || node)
-  var n = node instanceof EventTarget ? node.getEventTarget() : node
+  var n = getEventTarget(node)
 
   // events can be an array of events or a string of events
   events = Array.isArray(events) ? events : events.split(delimiter)
@@ -67,7 +71,9 @@ export function on (node, events, listener, binding, options) {
 
 // Add event unbinder in the SVG namespace
 export function off (node, events, listener, options) {
-  var n = node instanceof EventTarget ? node.getEventTarget() : node
+  var n = getEventTarget(node)
+
+  // we cannot remove an event if its not an svg.js instance
   if (!n.instance) return
 
   // listener can be a function or a number
@@ -126,7 +132,7 @@ export function off (node, events, listener, options) {
 }
 
 export function dispatch (node, event, data) {
-  var n = node instanceof EventTarget ? node.getEventTarget() : node
+  var n = getEventTarget(node)
 
   // Dispatch event
   if (event instanceof window.Event) {
diff --git a/src/flatten.js b/src/flatten.js
deleted file mode 100644 (file)
index 34a21bb..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-import {Doc, G, Parent, Defs} from './classes.js'
-
-export function flatten (parent) {
-  // flatten is only possible for svgs and groups
-  if (!(this instanceof G || this instanceof Doc)) {
-    return this
-  }
-
-  parent = parent ||
-    (this instanceof Doc && this.isRoot()
-      ? this
-      : this.parent(Parent))
-
-  this.each(function () {
-    if (this instanceof Defs) return this
-    if (this instanceof Parent) return this.flatten(parent)
-    return this.toParent(parent)
-  })
-
-  // we need this so that Doc does not get removed
-  this.node.firstElementChild || this.remove()
-
-  return this
-}
-
-export function ungroup (parent) {
-  // ungroup is only possible for nested svgs and groups
-  if (!(this instanceof G || (this instanceof Doc && !this.isRoot()))) {
-    return this
-  }
-
-  parent = parent || this.parent(Parent)
-
-  this.each(function () {
-    return this.toParent(parent)
-  })
-
-  // we need this so that Doc does not get removed
-  this.remove()
-
-  return this
-}
diff --git a/src/fx.js b/src/fx.js
deleted file mode 100644 (file)
index dd515df..0000000
--- a/src/fx.js
+++ /dev/null
@@ -1,1368 +0,0 @@
-SVG.easing = {
-  '-': function (pos) { return pos },
-  '<>': function (pos) { return -Math.cos(pos * Math.PI) / 2 + 0.5 },
-  '>': function (pos) { return Math.sin(pos * Math.PI / 2) },
-  '<': function (pos) { return -Math.cos(pos * Math.PI / 2) + 1 }
-}
-
-SVG.morph = function (pos) {
-  return function (from, to) {
-    return new SVG.MorphObj(from, to).at(pos)
-  }
-}
-
-let time = window.performance || window.Date
-
-SVG.Timeline = SVG.invent ({
-
-  create: function () {
-
-    // Store all of the closures to animate
-    this._closures = []
-
-    this._startTime = time.now()
-    this._duration = 0
-
-    this._running = true
-
-  },
-
-  extend: {
-
-    animate (duration, ease, delay, epoch) {
-
-    }
-
-    loop (times, reverse) {
-
-    }
-
-    duration (time) {
-      this._duration = time
-    }
-
-    delay (by, epoch) {
-      if (epoch) {
-        this._startTime = time.now()
-      }
-      this._duration = 0
-      this._startTime += by
-    }
-
-    ease (fn) {
-
-    }
-
-    play ()
-    pause ()
-    stop ()
-    finish (all=true)
-    speed (newSpeed)
-    seek (dt)
-    persist (dt || forever) // 0 by default
-    reverse ()
-
-
-
-
-
-
-    // fn is a function that takes a position in range [0, 1]
-    schedule (fn) { // fn can not take parameters
-
-
-
-
-
-
-let declarative = rect.animate(300, '>', 200)
-  .loop().color('blue')
-  .animate(SVG.Spring(300))
-
-onmousemove() {
-  declarative.x(mouseX).y(mouseY)
-}
-
-      SVG.MorphObj = SVG.invent({
-
-        create: function (from, to) {
-          // prepare color for morphing
-          if (SVG.Color.isColor(to)) return new SVG.Color(from).morph(to)
-          // prepare value list for morphing
-          if (SVG.regex.delimiter.test(from)) return new SVG.Array(from).morph(to)
-          // prepare number for morphing
-          if (SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to)
-
-          // prepare for plain morphing
-          this.value = from
-          this.destination = to
-        },
-
-        extend: {
-          at: function (pos, real) {
-            return real < 1 ? this.value : this.destination
-          },
-
-          valueOf: function () {
-            return this.value
-          }
-        }
-
-      })
-
-
-add('fill-color', val)
-
-add('x', val, 'animations')
-
-add('x', val, 'styles')
-
-add('line-cap', val, 'attrs')
-
-.style(name, val) {
-
-
-  styleAttr ('style', name, val)
-}
-
-.animate(spring)
-
-onmousemove(() => {
-  el.animate(SVG.Spring(500))
-    .move(event.pointX, event.pointY)
-    .finish()
-})
-
-
-
-Morphable ()
-
-Controlable ()
-
-new Controller(target, controller)
-
-
-
-
-Number
-Array
-PathArray
-ViewBox
-PointArray
-Color
-
-
-
-
-
-
-
-
-
-
-SVG.Timeline = {
-  styleAttr (type, name, val) {
-    let morpher = new Morph(val).controller(this.controller)
-    queue (
-      ()=> {
-        morpher = morpher.morph(element[type]('name'))
-      },
-      morpher.at
-    )
-  }
-}
-
-.styleAttr (type, name, val) {
-
-  let morpher = declarative ? new Controller(target) : new Morph().to(val)
-  queue (
-    ()=> {
-      morpher = morpher.from(element[type](name))
-    },
-    () => {
-      this.element[type](name, morpher.at(pos))
-    }
-  )
-}
-
-viewbox(box) {
-  new Box
-  let morpher = new Morph().to(box) // box: {width, heught, x, y}
-}
-
-
-new Morph(from, to)
-
-
-new Morpg(from, to, controller = (from, to, pos) => {from + pos * (to - from)})
-
-
-// Something line
-path = "a, b, c"
-
-SVG.color {
-  toArray: [r, g, b]
-  fromArray: new Color({r, g, b})
-}
-
-
-
-
-
-
-morph: function (pathArray) {
-  pathArray = new SVG.PathArray(pathArray)
-
-  if (this.equalCommands(pathArray)) {
-    this.destination = pathArray
-  } else {
-    this.destination = null
-  }
-
-  return this
-},
-
-[['M', 3, 5], ['L', 5, 6]]
-
-['M', 3, 4, 'L', ...]
-
-
-
-
-function detectSomething (item) {
-  if(from instanceof SVG.Morphable) return from.controller(controller)
-  // prepare color for morphing
-  if (SVG.Color.isColor(to)) return new SVG.Color(from, controller)
-  // prepare value list for morphing
-  if (SVG.regex.delimiter.test(from)) return new SVG.Array(from).morph(to)
-  // prepare number for morphing
-  if (SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to)
-
-  return item
-}
-
-foo->bar
-
-
-all of these things implement
-
-interface Morphable {
-  from: (thing)=> {}
-  to: (thing)=> {}
-  at: (pos)=> {}
-  controller: (fn (nowOrFrom, target, pos))=> {}
-}
-
-
-new SVG.MorphObj(el.attr(name))
-
-animate().attr('line-joint', 5)
-
-SVG.MorphObj = SVG.invent({
-
-  create: function (from, to) {
-    // prepare color for morphing
-    if (SVG.Color.isColor(to)) return new SVG.Color(from).morph(to)
-    // prepare value list for morphing
-    if (SVG.regex.delimiter.test(from)) return new SVG.Array(from).morph(to)
-    // prepare number for morphing
-    if (SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to)
-
-    // prepare for plain morphing
-    this.value = from
-    this.destination = to
-  },
-
-  extend: {
-    at: function (pos, real) {
-      return real < 1 ? this.value : this.destination
-    },
-
-    valueOf: function () {
-      return this.value
-    }
-  }
-
-})
-
-
-// Only works with a single number
-new MorphObj {
-
-  constr: (control= (from, to, c)=> {from + pos * (to - from)}) {
-  }
-
-  _detect: // Gets the user input and returns the right kind of object
-
-  from: (from) => {
-
-    if (SVG.Color.isColor(to)) return new SVG.Color(from).morph(to)
-    // prepare value list for morphing
-    if (SVG.regex.delimiter.test(from)) return new SVG.Array(from).morph(to)
-    // prepare number for morphing
-    if (SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to)
-
-    // prepare for plain morphing
-    this.value = from
-    this.destination = to
-  }
-
-  to: (val) => {
-
-  }
-  at (pos) {
-
-    let type = from.type
-    let from = from.toArray()
-    let to = to.toArray()
-    result = []
-    for (i)
-      result[i] = this.controller(from[i], to[i], pos) : to[i]
-
-    type.fromArray(result)
-  }
-}
-
-if(declartive) {
-  mropher.init()
-  morpher.at(pos/fn)
-}
-
-
-
-controller(currentPos, target)
-
-
-morph interface
-detect type function
-
-
-if (mouse in box)
-  move box
-  animate(spring)
-
-zoom(level, point) {
-  let morpher = SVG.Number(level).controller(this.controller)
-  this.queue(
-    () => {morpher = morpher.from(element.zoom())},
-    (pos) => {element.zoom(morpher.at(pos), point)}
-  )
-}
-
-x (x) {
-
-}
-
-this.queue(fn, morpher)
-
-new Morph(x(), xGiven)
-
-      x: function (x, relative) {
-        if (this.target() instanceof SVG.G) {
-          this.transform({x: x}, relative)
-          return this
-        }
-
-        var num = new SVG.Number(x)
-        num.relative = relative
-        return this.add('x', num)
-      },
-
-
-      viewbox: function(box) {
-        var m = SVG.Box(box)
-      }
-
-
-      new Runner (function(time) {
-
-
-      })
-
-
-      var closure = function (time) {
-
-        // If it is time to do something, act now.
-        var running = start < time && time < end
-        if (running && this._running) {
-          closure.position = (time - closure.start) / closure.duration
-          fn (time)
-        }
-
-        // If we are not paused or stopped, request another frame
-        if (this._running) SVG.Animator.frame(closure, this._startTime)
-
-        // Tell the caller whether this animation is finished
-        closure.finished = !running
-
-      }.bind(this)
-
-      closure.stop() // toggles a stop flag
-      closure.pause()
-      closure.run(t)  // If it was paused, it
-
-
-      closure.start = this._startTime
-      closure.end = this._startTime + this._duration
-      closure.positon =
-      var forwards = true // Decide if running forward based on looping
-
-
-      // TODO:  Store a list of closures
-
-      SVG.Animator.timeout(closure, this._startTime)
-      _continue()
-    }
-
-    _step (dt) {
-
-    }
-
-    // Checks if we are running and continues the animation
-    _continue () {
-      ,   continue: function () {
-              if (this.paused) return
-              if (!this.nextFrame)
-                  this.step()
-              return this
-          }
-
-    }
-  },
-
-
-  construct: {
-    animate: function(o, ease, delay, epoch) {
-      return (this.timeline = this.timeline || new SVG.Timeline(o, ease, delay, epoch))
-    }
-  }
-})
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-// SVG.Situation = SVG.invent({
-//
-//   create: function (o) {
-//     this.init = false
-//     this.reversed = false
-//     this.reversing = false
-//
-//     this.duration = new SVG.Number(o.duration).valueOf()
-//     this.delay = new SVG.Number(o.delay).valueOf()
-//
-//     this.start = +new Date() + this.delay
-//     this.finish = this.start + this.duration
-//     this.ease = o.ease
-//
-//     // this.loop is incremented from 0 to this.loops
-//     // it is also incremented when in an infinite loop (when this.loops is true)
-//     this.loop = 0
-//     this.loops = false
-//
-//     this.animations = {
-//       // functionToCall: [list of morphable objects]
-//       // e.g. move: [SVG.Number, SVG.Number]
-//     }
-//
-//     this.attrs = {
-//       // holds all attributes which are not represented from a function svg.js provides
-//       // e.g. someAttr: SVG.Number
-//     }
-//
-//     this.styles = {
-//       // holds all styles which should be animated
-//       // e.g. fill-color: SVG.Color
-//     }
-//
-//     this.transforms = [
-//       // holds all transformations as transformation objects
-//       // e.g. [SVG.Rotate, SVG.Translate, SVG.Matrix]
-//     ]
-//
-//     this.once = {
-//       // functions to fire at a specific position
-//       // e.g. "0.5": function foo(){}
-//     }
-//   }
-//
-// })
-//
-// SVG.Timeline = SVG.invent({
-//
-//   create: function (element) {
-//     this._target = element
-//     this.situations = []
-//     this.active = false
-//     this.situation = null
-//     this.paused = false
-//     this.lastPos = 0
-//     this.pos = 0
-//     // The absolute position of an animation is its position in the context of its complete duration (including delay and loops)
-//     // When performing a delay, absPos is below 0 and when performing a loop, its value is above 1
-//     this.absPos = 0
-//     this._speed = 1
-//   },
-//
-//   extend: {
-//
-//     /**
-//      * sets or returns the target of this animation
-//      * @param o object || number In case of Object it holds all parameters. In case of number its the duration of the animation
-//      * @param ease function || string Function which should be used for easing or easing keyword
-//      * @param delay Number indicating the delay before the animation starts
-//      * @return target || this
-//      */
-//     animate: function (o, ease, delay) {
-//       if (typeof o === 'object') {
-//         ease = o.ease
-//         delay = o.delay
-//         o = o.duration
-//       }
-//
-//       var situation = new SVG.Situation({
-//         duration: o || 1000,
-//         delay: delay || 0,
-//         ease: SVG.easing[ease || '-'] || ease
-//       })
-//
-//       this.queue(situation)
-//
-//       return this
-//     },
-//
-//     /**
-//      * sets a delay before the next element of the queue is called
-//      * @param delay Duration of delay in milliseconds
-//      * @return this.target()
-//      */
-//     delay: function (delay) {
-//       // The delay is performed by an empty situation with its duration
-//       // attribute set to the duration of the delay
-//       var situation = new SVG.Situation({
-//         duration: delay,
-//         delay: 0,
-//         ease: SVG.easing['-']
-//       })
-//
-//       return this.queue(situation)
-//     },
-//
-//     /**
-//      * sets or returns the target of this animation
-//      * @param null || target SVG.Element which should be set as new target
-//      * @return target || this
-//      */
-//     target: function (target) {
-//       if (target && target instanceof SVG.Element) {
-//         this._target = target
-//         return this
-//       }
-//
-//       return this._target
-//     },
-//
-//     // returns the absolute position at a given time
-//     timeToAbsPos: function (timestamp) {
-//       return (timestamp - this.situation.start) / (this.situation.duration / this._speed)
-//     },
-//
-//     // returns the timestamp from a given absolute positon
-//     absPosToTime: function (absPos) {
-//       return this.situation.duration / this._speed * absPos + this.situation.start
-//     },
-//
-//     // starts the animationloop
-//     startAnimFrame: function () {
-//       this.stopAnimFrame()
-//       this.animationFrame = window.requestAnimationFrame(function () { this.step() }.bind(this))
-//     },
-//
-//     // cancels the animationframe
-//     stopAnimFrame: function () {
-//       window.cancelAnimationFrame(this.animationFrame)
-//     },
-//
-//     // kicks off the animation - only does something when the queue is currently not active and at least one situation is set
-//     start: function () {
-//       // dont start if already started
-//       if (!this.active && this.situation) {
-//         this.active = true
-//         this.startCurrent()
-//       }
-//
-//       return this
-//     },
-//
-//     // start the current situation
-//     startCurrent: function () {
-//       this.situation.start = +new Date() + this.situation.delay / this._speed
-//       this.situation.finish = this.situation.start + this.situation.duration / this._speed
-//       return this.initAnimations().step()
-//     },
-//
-//     /**
-//      * adds a function / Situation to the animation queue
-//      * @param fn function / situation to add
-//      * @return this
-//      */
-//     queue: function (fn) {
-//       if (typeof fn === 'function' || fn instanceof SVG.Situation) {
-//         this.situations.push(fn)
-//       }
-//
-//       if (!this.situation) this.situation = this.situations.shift()
-//
-//       return this
-//     },
-//
-//     /**
-//      * pulls next element from the queue and execute it
-//      * @return this
-//      */
-//     dequeue: function () {
-//       // stop current animation
-//       this.stop()
-//
-//       // get next animation from queue
-//       this.situation = this.situations.shift()
-//
-//       if (this.situation) {
-//         if (this.situation instanceof SVG.Situation) {
-//           this.start()
-//         } else {
-//           // If it is not a SVG.Situation, then it is a function, we execute it
-//           this.situation(this)
-//         }
-//       }
-//
-//       return this
-//     },
-//
-//     // updates all animations to the current state of the element
-//     // this is important when one property could be changed from another property
-//     initAnimations: function () {
-//       var i, j, source
-//       var s = this.situation
-//
-//       if (s.init) return this
-//
-//       for (i in s.animations) {
-//         source = this.target()[i]()
-//
-//         if (!Array.isArray(source)) {
-//           source = [source]
-//         }
-//
-//         if (!Array.isArray(s.animations[i])) {
-//           s.animations[i] = [s.animations[i]]
-//         }
-//
-//         // if(s.animations[i].length > source.length) {
-//         //  source.concat = source.concat(s.animations[i].slice(source.length, s.animations[i].length))
-//         // }
-//
-//         for (j = source.length; j--;) {
-//           // The condition is because some methods return a normal number instead
-//           // of a SVG.Number
-//           if (s.animations[i][j] instanceof SVG.Number) {
-//             source[j] = new SVG.Number(source[j])
-//           }
-//
-//           s.animations[i][j] = source[j].morph(s.animations[i][j])
-//         }
-//       }
-//
-//       for (i in s.attrs) {
-//         s.attrs[i] = new SVG.MorphObj(this.target().attr(i), s.attrs[i])
-//       }
-//
-//       for (i in s.styles) {
-//         s.styles[i] = new SVG.MorphObj(this.target().css(i), s.styles[i])
-//       }
-//
-//       s.initialTransformation = this.target().matrixify()
-//
-//       s.init = true
-//       return this
-//     },
-//
-//     clearQueue: function () {
-//       this.situations = []
-//       return this
-//     },
-//
-//     clearCurrent: function () {
-//       this.situation = null
-//       return this
-//     },
-//
-//     /** stops the animation immediately
-//      * @param jumpToEnd A Boolean indicating whether to complete the current animation immediately.
-//      * @param clearQueue A Boolean indicating whether to remove queued animation as well.
-//      * @return this
-//      */
-//     stop: function (jumpToEnd, clearQueue) {
-//       var active = this.active
-//       this.active = false
-//
-//       if (clearQueue) {
-//         this.clearQueue()
-//       }
-//
-//       if (jumpToEnd && this.situation) {
-//         // initialize the situation if it was not
-//         !active && this.startCurrent()
-//         this.atEnd()
-//       }
-//
-//       this.stopAnimFrame()
-//
-//       return this.clearCurrent()
-//     },
-//
-//     /** resets the element to the state where the current element has started
-//      * @return this
-//      */
-//     reset: function () {
-//       if (this.situation) {
-//         var temp = this.situation
-//         this.stop()
-//         this.situation = temp
-//         this.atStart()
-//       }
-//       return this
-//     },
-//
-//     // Stop the currently-running animation, remove all queued animations, and complete all animations for the element.
-//     finish: function () {
-//       this.stop(true, false)
-//
-//       while (this.dequeue().situation && this.stop(true, false));
-//
-//       this.clearQueue().clearCurrent()
-//
-//       return this
-//     },
-//
-//     // set the internal animation pointer at the start position, before any loops, and updates the visualisation
-//     atStart: function () {
-//       return this.at(0, true)
-//     },
-//
-//     // set the internal animation pointer at the end position, after all the loops, and updates the visualisation
-//     atEnd: function () {
-//       if (this.situation.loops === true) {
-//         // If in a infinite loop, we end the current iteration
-//         this.situation.loops = this.situation.loop + 1
-//       }
-//
-//       if (typeof this.situation.loops === 'number') {
-//         // If performing a finite number of loops, we go after all the loops
-//         return this.at(this.situation.loops, true)
-//       } else {
-//         // If no loops, we just go at the end
-//         return this.at(1, true)
-//       }
-//     },
-//
-//     // set the internal animation pointer to the specified position and updates the visualisation
-//     // if isAbsPos is true, pos is treated as an absolute position
-//     at: function (pos, isAbsPos) {
-//       var durDivSpd = this.situation.duration / this._speed
-//
-//       this.absPos = pos
-//       // If pos is not an absolute position, we convert it into one
-//       if (!isAbsPos) {
-//         if (this.situation.reversed) this.absPos = 1 - this.absPos
-//         this.absPos += this.situation.loop
-//       }
-//
-//       this.situation.start = +new Date() - this.absPos * durDivSpd
-//       this.situation.finish = this.situation.start + durDivSpd
-//
-//       return this.step(true)
-//     },
-//
-//     /**
-//      * sets or returns the speed of the animations
-//      * @param speed null || Number The new speed of the animations
-//      * @return Number || this
-//      */
-//     speed: function (speed) {
-//       if (speed === 0) return this.pause()
-//
-//       if (speed) {
-//         this._speed = speed
-//         // We use an absolute position here so that speed can affect the delay before the animation
-//         return this.at(this.absPos, true)
-//       } else return this._speed
-//     },
-//
-//     // Make loopable
-//     loop: function (times, reverse) {
-//       var c = this.last()
-//
-//       // store total loops
-//       c.loops = (times != null) ? times : true
-//       c.loop = 0
-//
-//       if (reverse) c.reversing = true
-//       return this
-//     },
-//
-//     // pauses the animation
-//     pause: function () {
-//       this.paused = true
-//       this.stopAnimFrame()
-//
-//       return this
-//     },
-//
-//     // unpause the animation
-//     play: function () {
-//       if (!this.paused) return this
-//       this.paused = false
-//       // We use an absolute position here so that the delay before the animation can be paused
-//       return this.at(this.absPos, true)
-//     },
-//
-//     /**
-//      * toggle or set the direction of the animation
-//      * true sets direction to backwards while false sets it to forwards
-//      * @param reversed Boolean indicating whether to reverse the animation or not (default: toggle the reverse status)
-//      * @return this
-//      */
-//     reverse: function (reversed) {
-//       var c = this.last()
-//
-//       if (typeof reversed === 'undefined') c.reversed = !c.reversed
-//       else c.reversed = reversed
-//
-//       return this
-//     },
-//
-//     /**
-//      * returns a float from 0-1 indicating the progress of the current animation
-//      * @param eased Boolean indicating whether the returned position should be eased or not
-//      * @return number
-//      */
-//     progress: function (easeIt) {
-//       return easeIt ? this.situation.ease(this.pos) : this.pos
-//     },
-//
-//     /**
-//      * adds a callback function which is called when the current animation is finished
-//      * @param fn Function which should be executed as callback
-//      * @return number
-//      */
-//     after: function (fn) {
-//       var c = this.last()
-//       function wrapper (e) {
-//         if (e.detail.situation === c) {
-//           fn.call(this, c)
-//           this.off('finished.fx', wrapper) // prevent memory leak
-//         }
-//       }
-//
-//       this.target().on('finished.fx', wrapper)
-//
-//       return this._callStart()
-//     },
-//
-//     // adds a callback which is called whenever one animation step is performed
-//     during: function (fn) {
-//       var c = this.last()
-//       function wrapper (e) {
-//         if (e.detail.situation === c) {
-//           fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, c)
-//         }
-//       }
-//
-//       // see above
-//       this.target().off('during.fx', wrapper).on('during.fx', wrapper)
-//
-//       this.after(function () {
-//         this.off('during.fx', wrapper)
-//       })
-//
-//       return this._callStart()
-//     },
-//
-//     // calls after ALL animations in the queue are finished
-//     afterAll: function (fn) {
-//       var wrapper = function wrapper (e) {
-//         fn.call(this)
-//         this.off('allfinished.fx', wrapper)
-//       }
-//
-//       // see above
-//       this.target().off('allfinished.fx', wrapper).on('allfinished.fx', wrapper)
-//
-//       return this._callStart()
-//     },
-//
-//     // calls on every animation step for all animations
-//     duringAll: function (fn) {
-//       var wrapper = function (e) {
-//         fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, e.detail.situation)
-//       }
-//
-//       this.target().off('during.fx', wrapper).on('during.fx', wrapper)
-//
-//       this.afterAll(function () {
-//         this.off('during.fx', wrapper)
-//       })
-//
-//       return this._callStart()
-//     },
-//
-//     last: function () {
-//       return this.situations.length ? this.situations[this.situations.length - 1] : this.situation
-//     },
-//
-//     // adds one property to the animations
-//     add: function (method, args, type) {
-//       this.last()[type || 'animations'][method] = args
-//       return this._callStart()
-//     },
-//
-//     /** perform one step of the animation
-//      *  @param ignoreTime Boolean indicating whether to ignore time and use position directly or recalculate position based on time
-//      *  @return this
-//      */
-//     step: function (ignoreTime) {
-//       // convert current time to an absolute position
-//       if (!ignoreTime) this.absPos = this.timeToAbsPos(+new Date())
-//
-//       // This part convert an absolute position to a position
-//       if (this.situation.loops !== false) {
-//         var absPos, absPosInt, lastLoop
-//
-//         // If the absolute position is below 0, we just treat it as if it was 0
-//         absPos = Math.max(this.absPos, 0)
-//         absPosInt = Math.floor(absPos)
-//
-//         if (this.situation.loops === true || absPosInt < this.situation.loops) {
-//           this.pos = absPos - absPosInt
-//           lastLoop = this.situation.loop
-//           this.situation.loop = absPosInt
-//         } else {
-//           this.absPos = this.situation.loops
-//           this.pos = 1
-//           // The -1 here is because we don't want to toggle reversed when all the loops have been completed
-//           lastLoop = this.situation.loop - 1
-//           this.situation.loop = this.situation.loops
-//         }
-//
-//         if (this.situation.reversing) {
-//           // Toggle reversed if an odd number of loops as occured since the last call of step
-//           this.situation.reversed = this.situation.reversed !== Boolean((this.situation.loop - lastLoop) % 2)
-//         }
-//       } else {
-//         // If there are no loop, the absolute position must not be above 1
-//         this.absPos = Math.min(this.absPos, 1)
-//         this.pos = this.absPos
-//       }
-//
-//       // while the absolute position can be below 0, the position must not be below 0
-//       if (this.pos < 0) this.pos = 0
-//
-//       if (this.situation.reversed) this.pos = 1 - this.pos
-//
-//       // apply easing
-//       var eased = this.situation.ease(this.pos)
-//
-//       // call once-callbacks
-//       for (var i in this.situation.once) {
-//         if (i > this.lastPos && i <= eased) {
-//           this.situation.once[i].call(this.target(), this.pos, eased)
-//           delete this.situation.once[i]
-//         }
-//       }
-//
-//       // fire during callback with position, eased position and current situation as parameter
-//       if (this.active) this.target().fire('during', {pos: this.pos, eased: eased, fx: this, situation: this.situation})
-//
-//       // the user may call stop or finish in the during callback
-//       // so make sure that we still have a valid situation
-//       if (!this.situation) {
-//         return this
-//       }
-//
-//       // apply the actual animation to every property
-//       this.eachAt()
-//
-//       // do final code when situation is finished
-//       if ((this.pos === 1 && !this.situation.reversed) || (this.situation.reversed && this.pos === 0)) {
-//         // stop animation callback
-//         this.stopAnimFrame()
-//
-//         // fire finished callback with current situation as parameter
-//         this.target().fire('finished', {fx: this, situation: this.situation})
-//
-//         if (!this.situations.length) {
-//           this.target().fire('allfinished')
-//
-//           // Recheck the length since the user may call animate in the afterAll callback
-//           if (!this.situations.length) {
-//             this.target().off('.fx') // there shouldnt be any binding left, but to make sure...
-//             this.active = false
-//           }
-//         }
-//
-//         // start next animation
-//         if (this.active) this.dequeue()
-//         else this.clearCurrent()
-//       } else if (!this.paused && this.active) {
-//         // we continue animating when we are not at the end
-//         this.startAnimFrame()
-//       }
-//
-//       // save last eased position for once callback triggering
-//       this.lastPos = eased
-//       return this
-//     },
-//
-//     // calculates the step for every property and calls block with it
-//     eachAt: function () {
-//       var i, at
-//       var self = this
-//       var target = this.target()
-//       var s = this.situation
-//
-//       // apply animations which can be called trough a method
-//       for (i in s.animations) {
-//         at = [].concat(s.animations[i]).map(function (el) {
-//           return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
-//         })
-//
-//         target[i].apply(target, at)
-//       }
-//
-//       // apply animation which has to be applied with attr()
-//       for (i in s.attrs) {
-//         at = [i].concat(s.attrs[i]).map(function (el) {
-//           return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
-//         })
-//
-//         target.attr.apply(target, at)
-//       }
-//
-//       // apply animation which has to be applied with css()
-//       for (i in s.styles) {
-//         at = [i].concat(s.styles[i]).map(function (el) {
-//           return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
-//         })
-//
-//         target.css.apply(target, at)
-//       }
-//
-//       // animate initialTransformation which has to be chained
-//       if (s.transforms.length) {
-//
-//         // TODO: ANIMATE THE TRANSFORMS
-//
-//         // // get initial initialTransformation
-//         // at = s.initialTransformation
-//         // for(i = 0, len = s.transforms.length; i < len; i++){
-//         //
-//         //   // get next transformation in chain
-//         //   var a = s.transforms[i]
-//         //
-//         //   // multiply matrix directly
-//         //   if(a instanceof SVG.Matrix){
-//         //
-//         //     if(a.relative){
-//         //       at = at.multiply(new SVG.Matrix().morph(a).at(s.ease(this.pos)))
-//         //     }else{
-//         //       at = at.morph(a).at(s.ease(this.pos))
-//         //     }
-//         //     continue
-//         //   }
-//         //
-//         //   // when transformation is absolute we have to reset the needed transformation first
-//         //   if(!a.relative)
-//         //     a.undo(at.decompose())
-//         //
-//         //   // and reapply it after
-//         //   at = at.multiply(a.at(s.ease(this.pos)))
-//         //
-//         // }
-//         //
-//         // // set new matrix on element
-//         // target.matrix(at)
-//       }
-//
-//       return this
-//     },
-//
-//     // adds an once-callback which is called at a specific position and never again
-//     once: function (pos, fn, isEased) {
-//       var c = this.last()
-//       if (!isEased) pos = c.ease(pos)
-//
-//       c.once[pos] = fn
-//
-//       return this
-//     },
-//
-//     _callStart: function () {
-//       setTimeout(function () { this.start() }.bind(this), 0)
-//       return this
-//     }
-//
-//   },
-//
-//   parent: SVG.Element,
-//
-//   // Add method to parent elements
-//   construct: {
-//     // Get fx module or create a new one, then animate with given duration and ease
-//     animate: function (o, ease, delay) {
-//       return (this.fx || (this.fx = new SVG.Timeline(this))).animate(o, ease, delay)
-//     },
-//     delay: function (delay) {
-//       return (this.fx || (this.fx = new SVG.Timeline(this))).delay(delay)
-//     },
-//     stop: function (jumpToEnd, clearQueue) {
-//       if (this.fx) {
-//         this.fx.stop(jumpToEnd, clearQueue)
-//       }
-//
-//       return this
-//     },
-//     finish: function () {
-//       if (this.fx) {
-//         this.fx.finish()
-//       }
-//
-//       return this
-//     },
-//     // Pause current animation
-//     pause: function () {
-//       if (this.fx) {
-//         this.fx.pause()
-//       }
-//
-//       return this
-//     },
-//     // Play paused current animation
-//     play: function () {
-//       if (this.fx) { this.fx.play() }
-//
-//       return this
-//     },
-//     // Set/Get the speed of the animations
-//     speed: function (speed) {
-//       if (this.fx) {
-//         if (speed == null) { return this.fx.speed() } else { this.fx.speed(speed) }
-//       }
-//
-//       return this
-//     }
-//   }
-//
-// })
-//
-// // MorphObj is used whenever no morphable object is given
-// SVG.MorphObj = SVG.invent({
-//
-//   create: function (from, to) {
-//     // prepare color for morphing
-//     if (SVG.Color.isColor(to)) return new SVG.Color(from).morph(to)
-//     // prepare value list for morphing
-//     if (SVG.regex.delimiter.test(from)) return new SVG.Array(from).morph(to)
-//     // prepare number for morphing
-//     if (SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to)
-//
-//     // prepare for plain morphing
-//     this.value = from
-//     this.destination = to
-//   },
-//
-//   extend: {
-//     at: function (pos, real) {
-//       return real < 1 ? this.value : this.destination
-//     },
-//
-//     valueOf: function () {
-//       return this.value
-//     }
-//   }
-//
-// })
-//
-// SVG.extend(SVG.Timeline, {
-//   // Add animatable attributes
-//   attr: function (a, v, relative) {
-//     // apply attributes individually
-//     if (typeof a === 'object') {
-//       for (var key in a) {
-//         this.attr(key, a[key])
-//       }
-//     } else {
-//       this.add(a, v, 'attrs')
-//     }
-//
-//     return this
-//   },
-//   // Add animatable styles
-//   css: function (s, v) {
-//     if (typeof s === 'object') {
-//       for (var key in s) {
-//         this.css(key, s[key])
-//       }
-//     } else {
-//       this.add(s, v, 'styles')
-//     }
-//
-//     return this
-//   },
-//   // Animatable x-axis
-//   x: function (x, relative) {
-//     if (this.target() instanceof SVG.G) {
-//       this.transform({x: x}, relative)
-//       return this
-//     }
-//
-//     var num = new SVG.Number(x)
-//     num.relative = relative
-//     return this.add('x', num)
-//   },
-//   // Animatable y-axis
-//   y: function (y, relative) {
-//     if (this.target() instanceof SVG.G) {
-//       this.transform({y: y}, relative)
-//       return this
-//     }
-//
-//     var num = new SVG.Number(y)
-//     num.relative = relative
-//     return this.add('y', num)
-//   },
-//   // Animatable center x-axis
-//   cx: function (x) {
-//     return this.add('cx', new SVG.Number(x))
-//   },
-//   // Animatable center y-axis
-//   cy: function (y) {
-//     return this.add('cy', new SVG.Number(y))
-//   },
-//   // Add animatable move
-//   move: function (x, y) {
-//     return this.x(x).y(y)
-//   },
-//   // Add animatable center
-//   center: function (x, y) {
-//     return this.cx(x).cy(y)
-//   },
-//   // Add animatable size
-//   size: function (width, height) {
-//     if (this.target() instanceof SVG.Text) {
-//       // animate font size for Text elements
-//       this.attr('font-size', width)
-//     } else {
-//       // animate bbox based size for all other elements
-//       var box
-//
-//       if (!width || !height) {
-//         box = this.target().bbox()
-//       }
-//
-//       if (!width) {
-//         width = box.width / box.height * height
-//       }
-//
-//       if (!height) {
-//         height = box.height / box.width * width
-//       }
-//
-//       this.add('width', new SVG.Number(width))
-//           .add('height', new SVG.Number(height))
-//     }
-//
-//     return this
-//   },
-//   // Add animatable width
-//   width: function (width) {
-//     return this.add('width', new SVG.Number(width))
-//   },
-//   // Add animatable height
-//   height: function (height) {
-//     return this.add('height', new SVG.Number(height))
-//   },
-//   // Add animatable plot
-//   plot: function (a, b, c, d) {
-//     // Lines can be plotted with 4 arguments
-//     if (arguments.length === 4) {
-//       return this.plot([a, b, c, d])
-//     }
-//
-//     return this.add('plot', new (this.target().MorphArray)(a))
-//   },
-//   // Add leading method
-//   leading: function (value) {
-//     return this.target().leading
-//       ? this.add('leading', new SVG.Number(value))
-//       : this
-//   },
-//   // Add animatable viewbox
-//   viewbox: function (x, y, width, height) {
-//     if (this.target() instanceof SVG.Container) {
-//       this.add('viewbox', new SVG.Box(x, y, width, height))
-//     }
-//
-//     return this
-//   },
-//   update: function (o) {
-//     if (this.target() instanceof SVG.Stop) {
-//       if (typeof o === 'number' || o instanceof SVG.Number) {
-//         return this.update({
-//           offset: arguments[0],
-//           color: arguments[1],
-//           opacity: arguments[2]
-//         })
-//       }
-//
-//       if (o.opacity != null) this.attr('stop-opacity', o.opacity)
-//       if (o.color != null) this.attr('stop-color', o.color)
-//       if (o.offset != null) this.attr('offset', o.offset)
-//     }
-//
-//     return this
-//   }
-// })
index b4bddf9eef5f802ef7c83efc40332fdc2cf46ce6..289b59dc155f74f60fd06ca725e98467127d8b58 100644 (file)
@@ -1,29 +1,7 @@
-import {Doc, Point, Element} from './classes.js'
-import {adopt, eid, makeNode} from './tools.js'
+import Point from './Point.js'
+import {eid, makeNode} from './tools.js'
 import {dots, reference} from './regex.js'
-
-export function makeInstance (element, makeNested) {
-  if (element instanceof Element) return element
-
-  if (typeof element === 'object') {
-    return adopt(element)
-  }
-
-  if (element == null) {
-    return new Doc()
-  }
-
-  if (typeof element === 'string' && element.charAt(0) !== '<') {
-    return adopt(document.querySelector(element))
-  }
-
-  var node = makeNode('svg')
-  node.innerHTML = element
-
-  element = adopt(node.firstElementChild)
-
-  return element
-}
+import {adopt} from './adopter.js'
 
 export function isNulledBox (box) {
   return !box.w && !box.h && !box.x && !box.y
@@ -55,7 +33,7 @@ export function arrayClone (arr) {
 }
 
 // tests if a given selector matches an element
-export function matches (el, selector) {
+export function matcher (el, selector) {
   return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector)
 }
 
index bc13196d79d588597065843faa5baea1655e6cdf..76bcfa6591d958a1588ccb2f81e7d2cb87c85c0e 100644 (file)
@@ -1,38 +1,41 @@
-import Element from './Element.js'
 
-extend(Element, {
-  // Remember arbitrary data
-  remember (k, v) {
-    // remember every item in an object individually
-    if (typeof arguments[0] === 'object') {
-      for (var key in k) {
-        this.remember(key, k[key])
-      }
-    } else if (arguments.length === 1) {
-      // retrieve memory
-      return this.memory()[k]
-    } else {
-      // store memory
-      this.memory()[k] = v
+export const name = 'Memory'
+
+export function setup (node) {
+  this._memory = {}
+}
+
+// Remember arbitrary data
+export function remember (k, v) {
+  // remember every item in an object individually
+  if (typeof arguments[0] === 'object') {
+    for (var key in k) {
+      this.remember(key, k[key])
     }
+  } else if (arguments.length === 1) {
+    // retrieve memory
+    return this.memory()[k]
+  } else {
+    // store memory
+    this.memory()[k] = v
+  }
 
-    return this
-  },
+  return this
+},
 
   // Erase a given memory
-  forget () {
-    if (arguments.length === 0) {
-      this._memory = {}
-    } else {
-      for (var i = arguments.length - 1; i >= 0; i--) {
-        delete this.memory()[arguments[i]]
-      }
+export function forget () {
+  if (arguments.length === 0) {
+    this._memory = {}
+  } else {
+    for (var i = arguments.length - 1; i >= 0; i--) {
+      delete this.memory()[arguments[i]]
     }
-    return this
   }
+  return this
+}
 
   // Initialize or return local memory object
-  memory () {
-    return this._memory || (this._memory = {})
-  }
-})
+export function memory () {
+  return this._memory
+}
index c51ad7123e5c0edbfc11c619f9220400422f70e9..9a64dbcf72024979f5b1e43ed843a9b92b45ea4f 100644 (file)
@@ -1,6 +1,22 @@
 import Doc from './Doc.js'
 
-let parser = function () {
+export default function parser () {
+
+  // Reuse cached element if possible
+  if (!parser.nodes) {
+    let svg = new Doc().size(2, 0).css({
+      opacity: 0,
+      position: 'absolute',
+      left: '-100%',
+      top: '-100%',
+      overflow: 'hidden'
+    })
+
+    let path = svg.path().node
+
+    parser.nodes = {svg, path}
+  }
+
   if (!parser.nodes.svg.node.parentNode) {
     let b = document.body || document.documentElement
     parser.nodes.svg.addTo(b)
@@ -8,17 +24,3 @@ let parser = function () {
 
   return parser.nodes
 }
-
-parser.nodes = {
-  svg: new Doc().size(2, 0).css({
-    opacity: 0,
-    position: 'absolute',
-    left: '-100%',
-    top: '-100%',
-    overflow: 'hidden'
-  })
-}
-
-parser.nodes.path = parser.nodes.svg.path().node
-
-export default parser
diff --git a/src/poly.js b/src/poly.js
new file mode 100644 (file)
index 0000000..e8edbed
--- /dev/null
@@ -0,0 +1,30 @@
+// Add polygon-specific functions
+
+// Get array
+export function array () {
+  return this._array || (this._array = new PointArray(this.attr('points')))
+}
+
+// Plot new path
+export function plot (p) {
+  return (p == null) ? this.array()
+    : this.clear().attr('points', typeof p === 'string' ? p
+    : (this._array = new PointArray(p)))
+}
+
+// Clear array cache
+export function clear () {
+  delete this._array
+  return this
+}
+
+// Move by left top corner
+export function move (x, y) {
+  return this.attr('points', this.array().move(x, y))
+}
+
+// Set element size to given width and height
+export function size (width, height) {
+  let p = proportionalSize(this, width, height)
+  return this.attr('points', this.array().size(p.width, p.height))
+}
index a148b56227c310b8526e215a8836976809bb0428..98a3e3b1ddada6366b268f15d2d27f21d85fe8db 100644 (file)
@@ -1,6 +1,6 @@
 import {idFromReference} from './helpers.js'
 import {map} from './utils.js'
-import {adopt} from './tools.js'
+import {adopt} from './adopter.js'
 
 // // Method for getting an element by id
 // SVG.get = function (id) {
@@ -31,13 +31,12 @@ export default function find (query, parent) {
   })
 }
 
-export let mixings = {
-  // Scoped select method
-  select: function (query) {
-    return find(query, this.node)
-  }
+
+export function select (query) {
+  return find(query, this.node)
 }
 
+
 // extend(SVG.Parent, {
 //   // Scoped select method
 //   select: function (query) {
diff --git a/src/set.js b/src/set.js
new file mode 100644 (file)
index 0000000..5352570
--- /dev/null
@@ -0,0 +1,17 @@
+SVG.Set = class extends Set {
+  // constructor (arr) {
+  //   super(arr)
+  // }
+
+  each (cbOrName, ...args) {
+    if (typeof cbOrName === 'function') {
+      this.forEach((el) => { cbOrName.call(el, el) })
+    } else {
+      this.forEach((el) => {
+        el[cbOrName](...args)
+      })
+    }
+
+    return this
+  }
+}
index 6f8fd2dedc4a4104ab28b8f0427d42f657bf94c6..25d034d5d58fc73995fcbc951a1595a10edf8f62 100644 (file)
@@ -1,6 +1,90 @@
-import {makeInstance} from './helpers.js'
+// import {extend} from './tools.js'
+// import * as Element from './Element.js'
+// import Defs from './Defs.js'
+//
+// extend(Defs, [EventTarget, Element, Parent])
+
+import {makeInstance} from './adopter.js'
 import * as Classes from './classes.js'
+import * as adopter from './adopter.js'
 import * as tools from './tools.js'
+import * as containers from './containers.js'
+import * as elements from './elements.js'
+import * as arrange from './arrange.js'
+import {select} from './selector.js'
+import * as css from './css.js'
+import * as transform from './transform.js'
+const extend = tools.extend
+
+import * as EventTarget from './EventTarget.js'
+import * as Element from './Element.js'
+import * as Parent from './Parent.js'
+
+extend([
+  Classes.Doc,
+  Classes.Symbol,
+  Classes.Image,
+  Classes.Pattern,
+  Classes.Marker
+], {viewbox: Classes.Box.constructors.viewbox})
+
+extend([Classes.Line, Classes.Polyline, Classes.Polygon, Classes.Path], {
+  ...Classes.Marker.constructors.marker
+})
+
+extend(Classes.Text, Classes.TextPath.constructors.Text)
+extend(Classes.Path, Classes.TextPath.constructors.Path)
+
+extend(Classes.Defs, {
+  ...Classes.Gradient.constructors.Defs,
+  ...Classes.Marker.constructors.Defs,
+  ...Classes.Pattern.constructors.Defs,
+})
+
+for (let i in containers) {
+  extend(containers[i], {
+    ...Classes.A.constructors.Container,
+    ...Classes.ClipPath.constructors.Container,
+    ...Classes.G.constructors.Container,
+    ...Classes.Gradient.constructors.Container,
+    ...Classes.Line.constructors.Container,
+    ...Classes.Marker.constructors.Container,
+    ...Classes.Mask.constructors.Container,
+    ...Classes.Path.constructors.Container,
+    ...Classes.Pattern.constructors.Container,
+    ...Classes.Polygon.constructors.Container,
+    ...Classes.Polyline.constructors.Container,
+    ...Classes.Rect.constructors.Container,
+    select,
+    ...Classes.Symbol.constructors.Container,
+    ...Classes.Text.constructors.Container,
+    ...Classes.TextPath.constructors.Container,
+    ...Classes.Use.constructors.Container,
+  })
+}
+
+for (let i in elements) {
+  extend(elements[i], {
+    ...EventTarget,
+    ...Element,
+    ...Parent,
+    ...arrange,
+    ...Classes.A.constructors.Element,
+    ...Classes.Box.constructors.Element,
+    ...Classes.Circle.constructors.Element,
+    ...Classes.ClipPath.constructors.Element,
+    ...css,
+    ...Classes.Image.constructors.Element,
+    ...Classes.Mask.constructors.Element,
+    ...Classes.Matrix.constructors.Element,
+    ...Classes.Point.constructors.Element,
+    ...Classes.Runner.constructors.Element,
+    ...Classes.Timeline.constructors.Element,
+    ...transform,
+  })
+}
+
+
 
 // The main wrapping element
 export default function SVG (element) {
@@ -9,3 +93,4 @@ export default function SVG (element) {
 
 Object.assign(SVG, Classes)
 Object.assign(SVG, tools)
+Object.assign(SVG, adopter)
diff --git a/src/textable.js b/src/textable.js
new file mode 100644 (file)
index 0000000..f61f04a
--- /dev/null
@@ -0,0 +1,35 @@
+import Tspan from './Tspan.js'
+
+// Create plain text node
+export function plain (text) {
+  // clear if build mode is disabled
+  if (this._build === false) {
+    this.clear()
+  }
+
+  // create text node
+  this.node.appendChild(document.createTextNode(text))
+
+  return this
+}
+
+  // Create a tspan
+export function tspan (text) {
+  var tspan = new Tspan()
+
+  // clear if build mode is disabled
+  if (!this._build) {
+    this.clear()
+  }
+
+  // add new tspan
+  this.node.appendChild(tspan.node)
+
+  return tspan.text(text)
+}
+
+// FIXME: Does this also work for textpath?
+// Get length of text element
+export function length () {
+  return this.node.getComputedTextLength()
+}
index e77d65331422ea4b94f5b78a390be01685dbd4cd..2733af4d66fb10325fdbca681e2594fdb54429a4 100644 (file)
@@ -1,5 +1,5 @@
 import {ns} from './namespaces.js'
-import {Container, Element, HtmlNode, Doc, Gradient, Parent} from './classes.js'
+import {capitalize} from './helpers.js'
 
 // Element id sequence
 let did = 1000
@@ -23,13 +23,22 @@ export function makeNode (name) {
 export function extend (modules, methods) {
   var key, i
 
+  if (Array.isArray(methods)) {
+    methods.forEach((method) => {
+      extend(modules, method)
+    })
+    return
+  }
+
   modules = Array.isArray(modules) ? modules : [modules]
 
   for (i = modules.length - 1; i >= 0; i--) {
-    if (modules[i]) {
-      for (key in methods) {
-        modules[i].prototype[key] = methods[key]
-      }
+    if (methods.name) {
+      modules[i].extensions = (modules[i].extensions || []).concat(methods)
+    }
+    for (key in methods) {
+      if (modules[i].prototype[key] || key == 'name' || key == 'setup') continue
+      modules[i].prototype[key] = methods[key]
     }
   }
 }
@@ -63,32 +72,3 @@ export function invent (config) {
 
   return initializer
 }
-
-// Adopt existing svg elements
-export function adopt (node) {
-  // check for presence of node
-  if (!node) return null
-
-  // make sure a node isn't already adopted
-  if (node.instance instanceof Element) return node.instance
-
-  if (!(node instanceof window.SVGElement)) {
-    return new HtmlNode(node)
-  }
-
-  // initialize variables
-  var element
-
-  // adopt with element-specific settings
-  if (node.nodeName === 'svg') {
-    element = new Doc(node)
-  } else if (node.nodeName === 'linearGradient' || node.nodeName === 'radialGradient') {
-    element = new Gradient(node)
-  } else if (SVG[capitalize(node.nodeName)]) {
-    element = new SVG[capitalize(node.nodeName)](node)
-  } else {
-    element = new Parent(node)
-  }
-
-  return element
-}
diff --git a/src/utilities.js b/src/utilities.js
deleted file mode 100644 (file)
index 01b8ba5..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-
-SVG.utils = {
-  // Map function
-  map: function (array, block) {
-    var i
-    var il = array.length
-    var result = []
-
-    for (i = 0; i < il; i++) {
-      result.push(block(array[i]))
-    }
-
-    return result
-  },
-
-  // Filter function
-  filter: function (array, block) {
-    var i
-    var il = array.length
-    var result = []
-
-    for (i = 0; i < il; i++) {
-      if (block(array[i])) { result.push(array[i]) }
-    }
-
-    return result
-  },
-
-  // Degrees to radians
-  radians: function (d) {
-    return d % 360 * Math.PI / 180
-  },
-
-  // Radians to degrees
-  degrees: function (r) {
-    return r * 180 / Math.PI % 360
-  },
-
-  filterSVGElements: function (nodes) {
-    return this.filter(nodes, function (el) { return el instanceof window.SVGElement })
-  }
-
-}