summaryrefslogtreecommitdiffstats
path: root/src/modules/core
diff options
context:
space:
mode:
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2018-11-06 13:48:05 +0100
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2018-11-06 13:48:05 +0100
commita0b13ebcacfd74b9f521110c7225bb404325bcd3 (patch)
treea07c5cc422645e31d7dfef81ce4e54f03f0945f6 /src/modules/core
parent9f2696e8a2cf7e4eebc1cc7e31027fe2070094fa (diff)
downloadsvg.js-a0b13ebcacfd74b9f521110c7225bb404325bcd3.tar.gz
svg.js-a0b13ebcacfd74b9f521110c7225bb404325bcd3.zip
reordered modules, add es6 build
Diffstat (limited to 'src/modules/core')
-rw-r--r--src/modules/core/attr.js80
-rw-r--r--src/modules/core/circled.js64
-rw-r--r--src/modules/core/defaults.js48
-rw-r--r--src/modules/core/event.js119
-rw-r--r--src/modules/core/gradiented.js14
-rw-r--r--src/modules/core/namespaces.js5
-rw-r--r--src/modules/core/parser.js26
-rw-r--r--src/modules/core/pointed.js25
-rw-r--r--src/modules/core/poly.js31
-rw-r--r--src/modules/core/regex.js58
-rw-r--r--src/modules/core/selector.js16
-rw-r--r--src/modules/core/textable.js18
12 files changed, 504 insertions, 0 deletions
diff --git a/src/modules/core/attr.js b/src/modules/core/attr.js
new file mode 100644
index 0000000..ed34dc9
--- /dev/null
+++ b/src/modules/core/attr.js
@@ -0,0 +1,80 @@
+import { isImage, isNumber } from './regex.js'
+import { attrs as defaults } from './defaults.js'
+import Color from '../../types/Color.js'
+import SVGArray from '../../types/SVGArray.js'
+import SVGNumber from '../../types/SVGNumber.js'
+
+// Set svg element attribute
+export default function attr (attr, val, ns) {
+ // act as full getter
+ if (attr == null) {
+ // get an object of attributes
+ attr = {}
+ val = this.node.attributes
+
+ for (let node of val) {
+ attr[node.nodeName] = isNumber.test(node.nodeValue)
+ ? parseFloat(node.nodeValue)
+ : node.nodeValue
+ }
+
+ return attr
+ } else if (Array.isArray(attr)) {
+ // FIXME: implement
+ } else if (typeof attr === 'object') {
+ // apply every attribute individually if an object is passed
+ for (val in attr) this.attr(val, attr[val])
+ } else if (val === null) {
+ // remove value
+ this.node.removeAttribute(attr)
+ } else if (val == null) {
+ // act as a getter if the first and only argument is not an object
+ val = this.node.getAttribute(attr)
+ return val == null ? defaults[attr] // FIXME: do we need to return defaults?
+ : isNumber.test(val) ? parseFloat(val)
+ : val
+ } else {
+ // convert image fill and stroke to patterns
+ if (attr === 'fill' || attr === 'stroke') {
+ if (isImage.test(val)) {
+ val = this.doc().defs().image(val)
+ }
+ }
+
+ // FIXME: This is fine, but what about the lines above?
+ // How does attr know about image()?
+ while (typeof val.attrHook === 'function') {
+ val = val.attrHook(this, attr)
+ }
+
+ // ensure correct numeric values (also accepts NaN and Infinity)
+ if (typeof val === 'number') {
+ val = new SVGNumber(val)
+ } else if (Color.isColor(val)) {
+ // ensure full hex color
+ val = new Color(val)
+ } else if (val.constructor === Array) {
+ // Check for plain arrays and parse array values
+ val = new SVGArray(val)
+ }
+
+ // if the passed attribute is leading...
+ if (attr === 'leading') {
+ // ... call the leading method instead
+ if (this.leading) {
+ this.leading(val)
+ }
+ } else {
+ // set given attribute on node
+ typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString())
+ : this.node.setAttribute(attr, val.toString())
+ }
+
+ // rebuild if required
+ if (this.rebuild && (attr === 'font-size' || attr === 'x')) {
+ this.rebuild()
+ }
+ }
+
+ return this
+}
diff --git a/src/modules/core/circled.js b/src/modules/core/circled.js
new file mode 100644
index 0000000..9a3b1ad
--- /dev/null
+++ b/src/modules/core/circled.js
@@ -0,0 +1,64 @@
+// FIXME: import this to runner
+import { proportionalSize } from '../../utils/utils.js'
+import SVGNumber from '../../types/SVGNumber.js'
+
+// Radius x value
+export function rx (rx) {
+ return this.attr('rx', rx)
+}
+
+// Radius y value
+export function ry (ry) {
+ return this.attr('ry', ry)
+}
+
+// Move over x-axis
+export function x (x) {
+ return x == null
+ ? this.cx() - this.rx()
+ : this.cx(x + this.rx())
+}
+
+// Move over y-axis
+export function y (y) {
+ return y == null
+ ? this.cy() - this.ry()
+ : this.cy(y + this.ry())
+}
+
+// Move by center over x-axis
+export function cx (x) {
+ return x == null
+ ? this.attr('cx')
+ : this.attr('cx', x)
+}
+
+// Move by center over y-axis
+export function cy (y) {
+ return y == null
+ ? this.attr('cy')
+ : this.attr('cy', y)
+}
+
+// Set width of element
+export function width (width) {
+ return width == null
+ ? this.rx() * 2
+ : this.rx(new SVGNumber(width).divide(2))
+}
+
+// Set height of element
+export function height (height) {
+ return height == null
+ ? this.ry() * 2
+ : this.ry(new SVGNumber(height).divide(2))
+}
+
+// Custom size function
+export function size (width, height) {
+ var p = proportionalSize(this, width, height)
+
+ return this
+ .rx(new SVGNumber(p.width).divide(2))
+ .ry(new SVGNumber(p.height).divide(2))
+}
diff --git a/src/modules/core/defaults.js b/src/modules/core/defaults.js
new file mode 100644
index 0000000..0d496bc
--- /dev/null
+++ b/src/modules/core/defaults.js
@@ -0,0 +1,48 @@
+
+export function noop () {}
+
+// Default animation values
+export let timeline = {
+ duration: 400,
+ ease: '>',
+ delay: 0
+}
+
+// Default attribute values
+export let 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/modules/core/event.js b/src/modules/core/event.js
new file mode 100644
index 0000000..2fcaf58
--- /dev/null
+++ b/src/modules/core/event.js
@@ -0,0 +1,119 @@
+import { delimiter } from './regex.js'
+import { makeInstance } from '../../utils/adopter.js'
+
+let listenerId = 0
+
+function getEvents (node) {
+ const n = makeInstance(node).getEventHolder()
+ if (!n.events) n.events = {}
+ return n.events
+}
+
+function getEventTarget (node) {
+ return makeInstance(node).getEventTarget()
+}
+
+function clearEvents (node) {
+ const n = makeInstance(node).getEventHolder()
+ if (n.events) n.events = {}
+}
+
+// Add event binder in the SVG namespace
+export function on (node, events, listener, binding, options) {
+ var l = listener.bind(binding || node)
+ var bag = getEvents(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)
+
+ // add id to listener
+ if (!listener._svgjsListenerId) {
+ listener._svgjsListenerId = ++listenerId
+ }
+
+ events.forEach(function (event) {
+ var ev = event.split('.')[0]
+ var ns = event.split('.')[1] || '*'
+
+ // ensure valid object
+ bag[ev] = bag[ev] || {}
+ bag[ev][ns] = bag[ev][ns] || {}
+
+ // reference listener
+ bag[ev][ns][listener._svgjsListenerId] = l
+
+ // add listener
+ n.addEventListener(ev, l, options || false)
+ })
+}
+
+// Add event unbinder in the SVG namespace
+export function off (node, events, listener, options) {
+ var bag = getEvents(node)
+ var n = getEventTarget(node)
+
+ // listener can be a function or a number
+ if (typeof listener === 'function') {
+ listener = listener._svgjsListenerId
+ if (!listener) return
+ }
+
+ // events can be an array of events or a string or undefined
+ events = Array.isArray(events) ? events : (events || '').split(delimiter)
+
+ events.forEach(function (event) {
+ var ev = event && event.split('.')[0]
+ var ns = event && event.split('.')[1]
+ var namespace, l
+
+ if (listener) {
+ // remove listener reference
+ if (bag[ev] && bag[ev][ns || '*']) {
+ // removeListener
+ n.removeEventListener(ev, bag[ev][ns || '*'][listener], options || false)
+
+ delete bag[ev][ns || '*'][listener]
+ }
+ } else if (ev && ns) {
+ // remove all listeners for a namespaced event
+ if (bag[ev] && bag[ev][ns]) {
+ for (l in bag[ev][ns]) { off(n, [ev, ns].join('.'), l) }
+
+ delete bag[ev][ns]
+ }
+ } else if (ns) {
+ // remove all listeners for a specific namespace
+ for (event in bag) {
+ for (namespace in bag[event]) {
+ if (ns === namespace) { off(n, [event, ns].join('.')) }
+ }
+ }
+ } else if (ev) {
+ // remove all listeners for the event
+ if (bag[ev]) {
+ for (namespace in bag[ev]) { off(n, [ev, namespace].join('.')) }
+
+ delete bag[ev]
+ }
+ } else {
+ // remove all listeners on a given node
+ for (event in bag) { off(n, event) }
+
+ clearEvents(node)
+ }
+ })
+}
+
+export function dispatch (node, event, data) {
+ var n = getEventTarget(node)
+
+ // Dispatch event
+ if (event instanceof window.Event) {
+ n.dispatchEvent(event)
+ } else {
+ event = new window.CustomEvent(event, { detail: data, cancelable: true })
+ n.dispatchEvent(event)
+ }
+ return event
+}
diff --git a/src/modules/core/gradiented.js b/src/modules/core/gradiented.js
new file mode 100644
index 0000000..d34a9fe
--- /dev/null
+++ b/src/modules/core/gradiented.js
@@ -0,0 +1,14 @@
+// FIXME: add to runner
+import SVGNumber from '../../types/SVGNumber.js'
+
+export function from (x, y) {
+ return (this._element || this).type === 'radialGradient'
+ ? this.attr({ fx: new SVGNumber(x), fy: new SVGNumber(y) })
+ : this.attr({ x1: new SVGNumber(x), y1: new SVGNumber(y) })
+}
+
+export function to (x, y) {
+ return (this._element || this).type === 'radialGradient'
+ ? this.attr({ cx: new SVGNumber(x), cy: new SVGNumber(y) })
+ : this.attr({ x2: new SVGNumber(x), y2: new SVGNumber(y) })
+}
diff --git a/src/modules/core/namespaces.js b/src/modules/core/namespaces.js
new file mode 100644
index 0000000..3791298
--- /dev/null
+++ b/src/modules/core/namespaces.js
@@ -0,0 +1,5 @@
+// Default namespaces
+export let ns = 'http://www.w3.org/2000/svg'
+export let xmlns = 'http://www.w3.org/2000/xmlns/'
+export let xlink = 'http://www.w3.org/1999/xlink'
+export let svgjs = 'http://svgjs.com/svgjs'
diff --git a/src/modules/core/parser.js b/src/modules/core/parser.js
new file mode 100644
index 0000000..7a656ef
--- /dev/null
+++ b/src/modules/core/parser.js
@@ -0,0 +1,26 @@
+import Doc from '../../elements/Doc.js'
+
+export default function parser () {
+ // Reuse cached element if possible
+ if (!parser.nodes) {
+ let svg = new Doc().size(2, 0)
+ svg.node.cssText = [
+ 'opacity: 0',
+ 'position: absolute',
+ 'left: -100%',
+ 'top: -100%',
+ 'overflow: hidden'
+ ].join(';')
+
+ 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)
+ }
+
+ return parser.nodes
+}
diff --git a/src/modules/core/pointed.js b/src/modules/core/pointed.js
new file mode 100644
index 0000000..95e6819
--- /dev/null
+++ b/src/modules/core/pointed.js
@@ -0,0 +1,25 @@
+import PointArray from '../../types/PointArray.js'
+
+export let MorphArray = PointArray
+
+// Move by left top corner over x-axis
+export function x (x) {
+ return x == null ? this.bbox().x : this.move(x, this.bbox().y)
+}
+
+// Move by left top corner over y-axis
+export function y (y) {
+ return y == null ? this.bbox().y : this.move(this.bbox().x, y)
+}
+
+// Set width of element
+export function width (width) {
+ let b = this.bbox()
+ return width == null ? b.width : this.size(width, b.height)
+}
+
+// Set height of element
+export function height (height) {
+ let b = this.bbox()
+ return height == null ? b.height : this.size(b.width, height)
+}
diff --git a/src/modules/core/poly.js b/src/modules/core/poly.js
new file mode 100644
index 0000000..ad12020
--- /dev/null
+++ b/src/modules/core/poly.js
@@ -0,0 +1,31 @@
+import { proportionalSize } from '../../utils/utils.js'
+import PointArray from '../../types/PointArray.js'
+
+// 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/modules/core/regex.js b/src/modules/core/regex.js
new file mode 100644
index 0000000..1056554
--- /dev/null
+++ b/src/modules/core/regex.js
@@ -0,0 +1,58 @@
+// Parse unit value
+export let numberAndUnit = /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i
+
+// Parse hex value
+export let hex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
+
+// Parse rgb value
+export let rgb = /rgb\((\d+),(\d+),(\d+)\)/
+
+// Parse reference id
+export let reference = /(#[a-z0-9\-_]+)/i
+
+// splits a transformation chain
+export let transforms = /\)\s*,?\s*/
+
+// Whitespace
+export let whitespace = /\s/g
+
+// Test hex value
+export let isHex = /^#[a-f0-9]{3,6}$/i
+
+// Test rgb value
+export let isRgb = /^rgb\(/
+
+// Test css declaration
+export let isCss = /[^:]+:[^;]+;?/
+
+// Test for blank string
+export let isBlank = /^(\s+)?$/
+
+// Test for numeric string
+export let isNumber = /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i
+
+// Test for percent value
+export let isPercent = /^-?[\d.]+%$/
+
+// Test for image url
+export let isImage = /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i
+
+// split at whitespace and comma
+export let delimiter = /[\s,]+/
+
+// The following regex are used to parse the d attribute of a path
+
+// Matches all hyphens which are not after an exponent
+export let hyphen = /([^e])-/gi
+
+// Replaces and tests for all path letters
+export let pathLetters = /[MLHVCSQTAZ]/gi
+
+// yes we need this one, too
+export let isPathLetter = /[MLHVCSQTAZ]/i
+
+// matches 0.154.23.45
+export let numbersWithDots = /((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi
+
+// matches .
+export let dots = /\./g
diff --git a/src/modules/core/selector.js b/src/modules/core/selector.js
new file mode 100644
index 0000000..1e0b55e
--- /dev/null
+++ b/src/modules/core/selector.js
@@ -0,0 +1,16 @@
+import { adopt } from '../../utils/adopter.js'
+import { map } from '../../utils/utils.js'
+import { registerMethods } from '../../utils/methods.js'
+
+export default function baseFind (query, parent) {
+ return map((parent || document).querySelectorAll(query), function (node) {
+ return adopt(node)
+ })
+}
+
+// Scoped find method
+export function find (query) {
+ return baseFind(query, this.node)
+}
+
+registerMethods('Dom', { find })
diff --git a/src/modules/core/textable.js b/src/modules/core/textable.js
new file mode 100644
index 0000000..c9a90db
--- /dev/null
+++ b/src/modules/core/textable.js
@@ -0,0 +1,18 @@
+// 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
+}
+
+// FIXME: Does this also work for textpath?
+// Get length of text element
+export function length () {
+ return this.node.getComputedTextLength()
+}