aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2018-10-27 20:43:35 +0200
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2018-10-27 20:43:35 +0200
commit1c75fcaf02ceb144152d59557643c6fdd7264065 (patch)
tree5184af75f2fd27ca6b81c24a06b1676d17ca2c76
parentb1b776a710d0ce0a6259043b8ce0665e205195fa (diff)
downloadsvg.js-1c75fcaf02ceb144152d59557643c6fdd7264065.tar.gz
svg.js-1c75fcaf02ceb144152d59557643c6fdd7264065.zip
resolve circular references and make example working again
-rw-r--r--abilities156
-rw-r--r--dirty.html15
-rw-r--r--package.json3
-rw-r--r--src/A.js39
-rw-r--r--src/Animator.js4
-rw-r--r--src/Bare.js23
-rw-r--r--src/Base.js14
-rw-r--r--src/Box.js41
-rw-r--r--src/Circle.js25
-rw-r--r--src/ClipPath.js62
-rw-r--r--src/Color.js9
-rw-r--r--src/Container.js2
-rw-r--r--src/Controller.js1
-rw-r--r--src/Defs.js9
-rw-r--r--src/Doc.js134
-rw-r--r--src/Element.js395
-rw-r--r--src/Ellipse.js19
-rw-r--r--src/EventTarget.js38
-rw-r--r--src/G.js19
-rw-r--r--src/Gradient.js37
-rw-r--r--src/HtmlNode.js7
-rw-r--r--src/Image.js21
-rw-r--r--src/Line.js29
-rw-r--r--src/Marker.js82
-rw-r--r--src/Mask.js58
-rw-r--r--src/Matrix.js53
-rw-r--r--src/Morphable.js25
-rw-r--r--src/Parent.js194
-rw-r--r--src/Path.js22
-rw-r--r--src/PathArray.js85
-rw-r--r--src/Pattern.js47
-rw-r--r--src/Point.js13
-rw-r--r--src/Polygon.js59
-rw-r--r--src/Polyline.js28
-rw-r--r--src/Rect.js25
-rw-r--r--src/Runner.js108
-rw-r--r--src/SVGArray.js8
-rw-r--r--src/SVGNumber.js6
-rw-r--r--src/Shape.js3
-rw-r--r--src/Stop.js6
-rw-r--r--src/Symbol.js17
-rw-r--r--src/Text.js103
-rw-r--r--src/TextPath.js77
-rw-r--r--src/Timeline.js17
-rw-r--r--src/Tspan.js43
-rw-r--r--src/Use.js18
-rw-r--r--src/adopter.js56
-rw-r--r--src/attr.js15
-rw-r--r--src/classes.js5
-rw-r--r--src/containers.js11
-rw-r--r--src/css.js100
-rw-r--r--src/data.js45
-rw-r--r--src/default.js51
-rw-r--r--src/elements.js24
-rw-r--r--src/event.js16
-rw-r--r--src/flatten.js42
-rw-r--r--src/fx.js1368
-rw-r--r--src/helpers.js30
-rw-r--r--src/memory.js61
-rw-r--r--src/parser.js32
-rw-r--r--src/poly.js30
-rw-r--r--src/selector.js11
-rw-r--r--src/set.js17
-rw-r--r--src/svg.js87
-rw-r--r--src/textable.js35
-rw-r--r--src/tools.js48
-rw-r--r--src/utilities.js43
67 files changed, 1641 insertions, 2685 deletions
diff --git a/abilities b/abilities
new file mode 100644
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
diff --git a/dirty.html b/dirty.html
index f3e739a..5f605de 100644
--- a/dirty.html
+++ b/dirty.html
@@ -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>-->
@@ -30,7 +23,11 @@
</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
diff --git a/package.json b/package.json
index c718bec..645cdd3 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/A.js b/src/A.js
index 72e13e4..eb95dc0 100644
--- 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)
}
-})
+}
diff --git a/src/Animator.js b/src/Animator.js
index d49ab12..eca6ee3 100644
--- a/src/Animator.js
+++ b/src/Animator.js
@@ -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
diff --git a/src/Bare.js b/src/Bare.js
index 783fa6a..b70f329 100644
--- a/src/Bare.js
+++ b/src/Bare.js
@@ -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
index 0000000..6b1242b
--- /dev/null
+++ b/src/Base.js
@@ -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)
+ }
+}
diff --git a/src/Box.js b/src/Box.js
index 0c98dd5..36ee7f8 100644
--- a/src/Box.js
+++ b/src/Box.js
@@ -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))
}
-})
+}
diff --git a/src/Circle.js b/src/Circle.js
index fc8be72..6d4b12b 100644
--- a/src/Circle.js
+++ b/src/Circle.js
@@ -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)
+ }
}
-})
+}
diff --git a/src/ClipPath.js b/src/ClipPath.js
index ef820e5..486b30c 100644
--- a/src/ClipPath.js
+++ b/src/ClipPath.js
@@ -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')
+ }
}
-})
+}
diff --git a/src/Color.js b/src/Color.js
index 1e2befb..de65750 100644
--- a/src/Color.js
+++ b/src/Color.js
@@ -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
index 5d6dc43..0000000
--- a/src/Container.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import Parent from './Parent.js'
-export default class Container extends Parent {}
diff --git a/src/Controller.js b/src/Controller.js
index 81f6721..a48d946 100644
--- a/src/Controller.js
+++ b/src/Controller.js
@@ -1,5 +1,6 @@
import {timeline} from './defaults.js'
+import {extend} from './tools.js'
/***
Base Class
diff --git a/src/Defs.js b/src/Defs.js
index 21b6703..e8c5ac2 100644
--- a/src/Defs.js
+++ b/src/Defs.js
@@ -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 }
}
diff --git a/src/Doc.js b/src/Doc.js
index e496996..4b89deb 100644
--- a/src/Doc.js
+++ b/src/Doc.js
@@ -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())
+// }
+// })
diff --git a/src/Element.js b/src/Element.js
index 7667563..bed762c 100644
--- a/src/Element.js
+++ b/src/Element.js
@@ -1,302 +1,271 @@
-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'
diff --git a/src/Ellipse.js b/src/Ellipse.js
index 2a6fc51..cb025bb 100644
--- a/src/Ellipse.js
+++ b/src/Ellipse.js
@@ -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)
+// }
+// })
diff --git a/src/EventTarget.js b/src/EventTarget.js
index c762929..ce18d1f 100644
--- a/src/EventTarget.js
+++ b/src/EventTarget.js
@@ -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
}
diff --git a/src/G.js b/src/G.js
index 2d60cfe..93d2b7f 100644
--- 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())
+ }
}
-})
+}
diff --git a/src/Gradient.js b/src/Gradient.js
index da76666..ab4cc4f 100644
--- a/src/Gradient.js
+++ b/src/Gradient.js
@@ -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)
+ }
}
-})
+}
diff --git a/src/HtmlNode.js b/src/HtmlNode.js
index 4a12d3f..f674d0b 100644
--- a/src/HtmlNode.js
+++ b/src/HtmlNode.js
@@ -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
}
diff --git a/src/Image.js b/src/Image.js
index c70be1d..389a38d 100644
--- a/src/Image.js
+++ b/src/Image.js
@@ -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)
+ }
}
-})
+}
diff --git a/src/Line.js b/src/Line.js
index ddd00e9..930820b 100644
--- a/src/Line.js
+++ b/src/Line.js
@@ -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]
+ )
+ }
}
-})
+}
diff --git a/src/Marker.js b/src/Marker.js
index 298ac6f..dfba967 100644
--- a/src/Marker.js
+++ b/src/Marker.js
@@ -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)
+ }
}
-})
+}
diff --git a/src/Mask.js b/src/Mask.js
index bdd6086..969fe2c 100644
--- a/src/Mask.js
+++ b/src/Mask.js
@@ -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')
+ }
}
-})
+}
diff --git a/src/Matrix.js b/src/Matrix.js
index 5edbc5c..d53db3c 100644
--- a/src/Matrix.js
+++ b/src/Matrix.js
@@ -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) => {
diff --git a/src/Morphable.js b/src/Morphable.js
index 1cb437e..c7e1c0f 100644
--- a/src/Morphable.js
+++ b/src/Morphable.js
@@ -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
}
})
diff --git a/src/Parent.js b/src/Parent.js
index ce22f80..e2c725c 100644
--- a/src/Parent.js
+++ b/src/Parent.js
@@ -1,93 +1,157 @@
-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
}
diff --git a/src/Path.js b/src/Path.js
index 7226977..b06da5b 100644
--- a/src/Path.js
+++ b/src/Path.js
@@ -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())
+ }
}
-})
+}
diff --git a/src/PathArray.js b/src/PathArray.js
index 24d8665..f6c22d1 100644
--- a/src/PathArray.js
+++ b/src/PathArray.js
@@ -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()
diff --git a/src/Pattern.js b/src/Pattern.js
index 00f9de5..add8b71 100644
--- a/src/Pattern.js
+++ b/src/Pattern.js
@@ -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'
+ })
+ }
}
-})
+}
diff --git a/src/Point.js b/src/Point.js
index ff18473..5880168 100644
--- a/src/Point.js
+++ b/src/Point.js
@@ -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())
+ }
}
-})
+}
diff --git a/src/Polygon.js b/src/Polygon.js
index 112d33b..3f7f948 100644
--- a/src/Polygon.js
+++ b/src/Polygon.js
@@ -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)
diff --git a/src/Polyline.js b/src/Polyline.js
index 9c28438..c3c8c0c 100644
--- a/src/Polyline.js
+++ b/src/Polyline.js
@@ -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)
diff --git a/src/Rect.js b/src/Rect.js
index 825adeb..f83243c 100644
--- a/src/Rect.js
+++ b/src/Rect.js
@@ -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)
+ }
}
-})
+}
diff --git a/src/Runner.js b/src/Runner.js
index c29c72c..9633c90 100644
--- a/src/Runner.js
+++ b/src/Runner.js
@@ -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) {
diff --git a/src/SVGArray.js b/src/SVGArray.js
index e7881df..9273833 100644
--- a/src/SVGArray.js
+++ b/src/SVGArray.js
@@ -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))
}
diff --git a/src/SVGNumber.js b/src/SVGNumber.js
index cb1fd28..e07b521 100644
--- a/src/SVGNumber.js
+++ b/src/SVGNumber.js
@@ -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
index d73ffb6..0000000
--- a/src/Shape.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Element from './Element.js'
-
-export default class Shape extends Element { }
diff --git a/src/Stop.js b/src/Stop.js
index 6bce999..1631b4a 100644
--- a/src/Stop.js
+++ b/src/Stop.js
@@ -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
diff --git a/src/Symbol.js b/src/Symbol.js
index 98d10ef..e9936e2 100644
--- a/src/Symbol.js
+++ b/src/Symbol.js
@@ -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())
+ }
}
-})
+}
diff --git a/src/Text.js b/src/Text.js
index 3d9f074..3d7fd1d 100644
--- a/src/Text.js
+++ b/src/Text.js
@@ -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()
}
-})
+}
diff --git a/src/TextPath.js b/src/TextPath.js
index cd1757e..c4685ec 100644
--- a/src/TextPath.js
+++ b/src/TextPath.js
@@ -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
diff --git a/src/Timeline.js b/src/Timeline.js
index 01a8e22..fc19db8 100644
--- a/src/Timeline.js
+++ b/src/Timeline.js
@@ -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
index 0000000..f5030c9
--- /dev/null
+++ b/src/Tspan.js
@@ -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)
diff --git a/src/Use.js b/src/Use.js
index 9cf1711..5c4fe8e 100644
--- a/src/Use.js
+++ b/src/Use.js
@@ -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
index 0000000..81969bd
--- /dev/null
+++ b/src/adopter.js
@@ -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
+}
diff --git a/src/attr.js b/src/attr.js
index a0c95b4..ddf4de2 100644
--- a/src/attr.js
+++ b/src/attr.js
@@ -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)) {
diff --git a/src/classes.js b/src/classes.js
index df65151..feebee2 100644
--- a/src/classes.js
+++ b/src/classes.js
@@ -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
index 0000000..56287de
--- /dev/null
+++ b/src/containers.js
@@ -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'
diff --git a/src/css.js b/src/css.js
index e5b0794..b85c0fe 100644
--- a/src/css.js
+++ b/src/css.js
@@ -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'
+}
diff --git a/src/data.js b/src/data.js
index 530986d..c143772 100644
--- a/src/data.js
+++ b/src/data.js
@@ -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
index e82d1db..0000000
--- a/src/default.js
+++ /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
index 0000000..4709c96
--- /dev/null
+++ b/src/elements.js
@@ -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'
diff --git a/src/event.js b/src/event.js
index 8d54782..c7832aa 100644
--- a/src/event.js
+++ b/src/event.js
@@ -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
index 34a21bb..0000000
--- a/src/flatten.js
+++ /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
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
-// }
-// })
diff --git a/src/helpers.js b/src/helpers.js
index b4bddf9..289b59d 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -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)
}
diff --git a/src/memory.js b/src/memory.js
index bc13196..76bcfa6 100644
--- a/src/memory.js
+++ b/src/memory.js
@@ -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
+}
diff --git a/src/parser.js b/src/parser.js
index c51ad71..9a64dbc 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -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
index 0000000..e8edbed
--- /dev/null
+++ b/src/poly.js
@@ -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))
+}
diff --git a/src/selector.js b/src/selector.js
index a148b56..98a3e3b 100644
--- a/src/selector.js
+++ b/src/selector.js
@@ -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
index 0000000..5352570
--- /dev/null
+++ b/src/set.js
@@ -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
+ }
+}
diff --git a/src/svg.js b/src/svg.js
index 6f8fd2d..25d034d 100644
--- a/src/svg.js
+++ b/src/svg.js
@@ -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
index 0000000..f61f04a
--- /dev/null
+++ b/src/textable.js
@@ -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()
+}
diff --git a/src/tools.js b/src/tools.js
index e77d653..2733af4 100644
--- a/src/tools.js
+++ b/src/tools.js
@@ -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
index 01b8ba5..0000000
--- a/src/utilities.js
+++ /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 })
- }
-
-}