diff options
author | Saivan <savian@me.com> | 2018-11-25 16:21:53 +1300 |
---|---|---|
committer | Saivan <savian@me.com> | 2018-11-25 16:21:53 +1300 |
commit | 62de7d0a1b994b69032a759b796b486e6bc382e3 (patch) | |
tree | 112b19f2903b4dc5b4cf61ebef0d021c6ca2f14d /src/types | |
parent | 2b37d7ba5b4267b39c86f9aba5fb14a1b376e846 (diff) | |
download | svg.js-62de7d0a1b994b69032a759b796b486e6bc382e3.tar.gz svg.js-62de7d0a1b994b69032a759b796b486e6bc382e3.zip |
Changed the esLint rules to avoid silly ternary operators, and to let code breathe!
This commit modifies some of the eslint rules, to allow our code to be a little bit more
readable. This came about because we had a particularly pesky problem, where the code
was indenting ternary operators. This fixes that, and makes it easy to add new rules
to eslint as we please in the future.
Changes
=======
- Rebuilt the library with new eslint rules
- Changed the eslintrc file to a yaml file by default
Diffstat (limited to 'src/types')
-rw-r--r-- | src/types/ArrayPolyfill.js | 34 | ||||
-rw-r--r-- | src/types/Box.js | 152 | ||||
-rw-r--r-- | src/types/EventTarget.js | 50 | ||||
-rw-r--r-- | src/types/List.js | 58 | ||||
-rw-r--r-- | src/types/Matrix.js | 382 | ||||
-rw-r--r-- | src/types/Morphable.js | 232 | ||||
-rw-r--r-- | src/types/PathArray.js | 338 | ||||
-rw-r--r-- | src/types/Point.js | 36 | ||||
-rw-r--r-- | src/types/PointArray.js | 104 | ||||
-rw-r--r-- | src/types/SVGArray.js | 48 | ||||
-rw-r--r-- | src/types/SVGNumber.js | 94 |
11 files changed, 1022 insertions, 506 deletions
diff --git a/src/types/ArrayPolyfill.js b/src/types/ArrayPolyfill.js index 4d2309f..0ee29a5 100644 --- a/src/types/ArrayPolyfill.js +++ b/src/types/ArrayPolyfill.js @@ -1,8 +1,10 @@ /* eslint no-new-func: "off" */ -export const subClassArray = (function () { +export const subClassArray = ( function () { + try { + // try es6 subclassing - return Function('name', 'baseClass', '_constructor', [ + return Function( 'name', 'baseClass', '_constructor', [ 'baseClass = baseClass || Array', 'return {', ' [name]: class extends baseClass {', @@ -12,25 +14,35 @@ export const subClassArray = (function () { ' }', ' }', '}[name]' - ].join('\n')) - } catch (e) { + ].join( '\n' ) ) + + } catch ( e ) { + // Use es5 approach - return (name, baseClass = Array, _constructor) => { + return ( name, baseClass = Array, _constructor ) => { + const Arr = function () { - baseClass.apply(this, arguments) - _constructor && _constructor.apply(this, arguments) + + baseClass.apply( this, arguments ) + _constructor && _constructor.apply( this, arguments ) + } - Arr.prototype = Object.create(baseClass.prototype) + Arr.prototype = Object.create( baseClass.prototype ) Arr.prototype.constructor = Arr - Arr.prototype.map = function (fn) { + Arr.prototype.map = function ( fn ) { + const arr = new Arr() - arr.push.apply(arr, Array.prototype.map.call(this, fn)) + arr.push.apply( arr, Array.prototype.map.call( this, fn ) ) return arr + } return Arr + } + } -})() + +} )() diff --git a/src/types/Box.js b/src/types/Box.js index e55f114..2fcb923 100644 --- a/src/types/Box.js +++ b/src/types/Box.js @@ -4,33 +4,45 @@ import { globals } from '../utils/window.js' import Point from './Point.js' import parser from '../modules/core/parser.js' -function isNulledBox (box) { +function isNulledBox ( box ) { + return !box.w && !box.h && !box.x && !box.y + } -function domContains (node) { - return (globals.document.documentElement.contains || function (node) { +function domContains ( node ) { + + return ( globals.document.documentElement.contains || function ( node ) { + // This is IE - it does not support contains() for top-level SVGs - while (node.parentNode) { + while ( node.parentNode ) { + node = node.parentNode + } return node === document - }).call(globals.document.documentElement, node) + + } ).call( globals.document.documentElement, node ) + } export default class Box { - constructor (...args) { - this.init(...args) + + 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 - : typeof source === 'object' ? [source.left != null ? source.left - : source.x, source.top != null ? source.top : source.y, source.width, source.height] - : arguments.length === 4 ? [].slice.call(arguments) - : base + init ( source ) { + + var base = [ 0, 0, 0, 0 ] + source = typeof source === 'string' ? source.split( delimiter ).map( parseFloat ) + : Array.isArray( source ) ? source + : typeof source === 'object' ? [ source.left != null ? source.left + : source.x, source.top != null ? source.top : source.y, source.width, source.height ] + : arguments.length === 4 ? [].slice.call( arguments ) + : base this.x = source[0] || 0 this.y = source[1] || 0 @@ -44,105 +56,139 @@ export default class Box { this.cy = this.y + this.h / 2 return this + } // Merge rect box with another, return a new instance - merge (box) { - let x = Math.min(this.x, box.x) - let y = Math.min(this.y, box.y) - let width = Math.max(this.x + this.width, box.x + box.width) - x - let height = Math.max(this.y + this.height, box.y + box.height) - y + merge ( box ) { + + let x = Math.min( this.x, box.x ) + let y = Math.min( this.y, box.y ) + let width = Math.max( this.x + this.width, box.x + box.width ) - x + let height = Math.max( this.y + this.height, box.y + box.height ) - y + + return new Box( x, y, width, height ) - return new Box(x, y, width, height) } - transform (m) { + transform ( m ) { + let xMin = Infinity let xMax = -Infinity let yMin = Infinity let yMax = -Infinity let pts = [ - new Point(this.x, this.y), - new Point(this.x2, this.y), - new Point(this.x, this.y2), - new Point(this.x2, this.y2) + new Point( this.x, this.y ), + new Point( this.x2, this.y ), + new Point( this.x, this.y2 ), + new Point( this.x2, this.y2 ) ] - pts.forEach(function (p) { - p = p.transform(m) - xMin = Math.min(xMin, p.x) - xMax = Math.max(xMax, p.x) - yMin = Math.min(yMin, p.y) - yMax = Math.max(yMax, p.y) - }) + pts.forEach( function ( p ) { + + p = p.transform( m ) + xMin = Math.min( xMin, p.x ) + xMax = Math.max( xMax, p.x ) + yMin = Math.min( yMin, p.y ) + yMax = Math.max( yMax, p.y ) + + } ) return new Box( xMin, yMin, xMax - xMin, yMax - yMin ) + } addOffset () { + // offset by window scroll position, because getBoundingClientRect changes when window is scrolled this.x += globals.window.pageXOffset this.y += globals.window.pageYOffset return this + } toString () { + return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height + } toArray () { - return [this.x, this.y, this.width, this.height] + + return [ this.x, this.y, this.width, this.height ] + } isNulled () { - return isNulledBox(this) + + return isNulledBox( this ) + } + } -function getBox (cb) { +function getBox ( cb ) { + let box try { - box = cb(this.node) - if (isNulledBox(box) && !domContains(this.node)) { - throw new Error('Element not in the dom') + box = cb( this.node ) + + if ( isNulledBox( box ) && !domContains( this.node ) ) { + + throw new Error( 'Element not in the dom' ) + } - } catch (e) { + + } catch ( e ) { + try { - let clone = this.clone().addTo(parser().svg).show() - box = cb(clone.node) + + let clone = this.clone().addTo( parser().svg ).show() + box = cb( clone.node ) clone.remove() - } catch (e) { - throw new Error('Getting a bounding box of element "' + this.node.nodeName + '" is not possible') + + } catch ( e ) { + + throw new Error( 'Getting a bounding box of element "' + this.node.nodeName + '" is not possible' ) + } + } return box + } export function bbox () { - return new Box(getBox.call(this, (node) => node.getBBox())) + + return new Box( getBox.call( this, ( node ) => node.getBBox() ) ) + } -export function rbox (el) { - let box = new Box(getBox.call(this, (node) => node.getBoundingClientRect())) - if (el) return box.transform(el.screenCTM().inverse()) +export function rbox ( el ) { + + let box = new Box( getBox.call( this, ( node ) => node.getBoundingClientRect() ) ) + if ( el ) return box.transform( el.screenCTM().inverse() ) return box.addOffset() + } -registerMethods({ +registerMethods( { viewbox: { - viewbox (x, y, width, height) { + viewbox ( x, y, width, height ) { + // act as getter - if (x == null) return new Box(this.attr('viewBox')) + if ( x == null ) return new Box( this.attr( 'viewBox' ) ) // act as setter - return this.attr('viewBox', new Box(x, y, width, height)) + return this.attr( 'viewBox', new Box( x, y, width, height ) ) + } } -}) +} ) diff --git a/src/types/EventTarget.js b/src/types/EventTarget.js index 5a005fd..3d755bf 100644 --- a/src/types/EventTarget.js +++ b/src/types/EventTarget.js @@ -2,57 +2,79 @@ import { dispatch, off, on } from '../modules/core/event.js' import Base from './Base.js' export default class EventTarget extends Base { - constructor ({ events = {} } = {}) { + + constructor ( { events = {} } = {} ) { + super() this.events = events + } addEventListener () {} - dispatch (event, data) { - return dispatch(this, event, data) + dispatch ( event, data ) { + + return dispatch( this, event, data ) + } - dispatchEvent (event) { + dispatchEvent ( event ) { + const bag = this.getEventHolder().events - if (!bag) return true + if ( !bag ) return true const events = bag[event.type] - for (let i in events) { - for (let j in events[i]) { - events[i][j](event) + for ( let i in events ) { + + for ( let j in events[i] ) { + + events[i][j]( event ) + } + } return !event.defaultPrevented + } // Fire given event - fire (event, data) { - this.dispatch(event, data) + fire ( event, data ) { + + this.dispatch( event, data ) return this + } getEventHolder () { + return this + } getEventTarget () { + return this + } // Unbind event from listener - off (event, listener) { - off(this, event, listener) + off ( event, listener ) { + + off( this, event, listener ) return this + } // Bind given event to listener - on (event, listener, binding, options) { - on(this, event, listener, binding, options) + on ( event, listener, binding, options ) { + + on( this, event, listener, binding, options ) return this + } removeEventListener () {} + } diff --git a/src/types/List.js b/src/types/List.js index 8bd3985..a2d2226 100644 --- a/src/types/List.js +++ b/src/types/List.js @@ -1,38 +1,62 @@ import { extend } from '../utils/adopter.js' import { subClassArray } from './ArrayPolyfill.js' -const List = subClassArray('List', Array, function (arr = []) { +const List = subClassArray( 'List', Array, function ( arr = [] ) { + // This catches the case, that native map tries to create an array with new Array(1) - if (typeof arr === 'number') return this + if ( typeof arr === 'number' ) return this this.length = 0 - this.push(...arr) -}) + this.push( ...arr ) + +} ) export default List -extend(List, { - each (fnOrMethodName, ...args) { - if (typeof fnOrMethodName === 'function') { - this.forEach((el) => { fnOrMethodName.call(el, el) }) +extend( List, { + each ( fnOrMethodName, ...args ) { + + if ( typeof fnOrMethodName === 'function' ) { + + this.forEach( ( el ) => { + + fnOrMethodName.call( el, el ) + + } ) + } else { - return this.map(el => { return el[fnOrMethodName](...args) }) + + return this.map( el => { + + return el[fnOrMethodName]( ...args ) + + } ) + } return this + }, toArray () { - return Array.prototype.concat.apply([], this) + + return Array.prototype.concat.apply( [], this ) + } -}) +} ) + +List.extend = function ( methods ) { + + methods = methods.reduce( ( obj, name ) => { + + obj[name] = function ( ...attrs ) { + + return this.each( name, ...attrs ) -List.extend = function (methods) { - methods = methods.reduce((obj, name) => { - obj[name] = function (...attrs) { - return this.each(name, ...attrs) } return obj - }, {}) - extend(List, methods) + }, {} ) + + extend( List, methods ) + } diff --git a/src/types/Matrix.js b/src/types/Matrix.js index a1eb317..a9a311e 100644 --- a/src/types/Matrix.js +++ b/src/types/Matrix.js @@ -3,27 +3,33 @@ import { radians } from '../utils/utils.js' import Element from '../elements/Element.js' import Point from './Point.js' -function closeEnough (a, b, threshold) { - return Math.abs(b - a) < (threshold || 1e-6) +function closeEnough ( a, b, threshold ) { + + return Math.abs( b - a ) < ( threshold || 1e-6 ) + } export default class Matrix { - constructor (...args) { - this.init(...args) + + constructor ( ...args ) { + + this.init( ...args ) + } // Initialize - init (source) { - var base = Matrix.fromArray([1, 0, 0, 1, 0, 0]) + init ( source ) { + + var base = Matrix.fromArray( [ 1, 0, 0, 1, 0, 0 ] ) // ensure source as object source = source instanceof Element ? source.matrixify() - : typeof source === 'string' ? Matrix.fromArray(source.split(delimiter).map(parseFloat)) - : Array.isArray(source) ? Matrix.fromArray(source) - : (typeof source === 'object' && Matrix.isMatrixLike(source)) ? source - : (typeof source === 'object') ? new Matrix().transform(source) - : arguments.length === 6 ? Matrix.fromArray([].slice.call(arguments)) - : base + : typeof source === 'string' ? Matrix.fromArray( source.split( delimiter ).map( parseFloat ) ) + : Array.isArray( source ) ? Matrix.fromArray( source ) + : ( typeof source === 'object' && Matrix.isMatrixLike( source ) ) ? source + : ( typeof source === 'object' ) ? new Matrix().transform( source ) + : arguments.length === 6 ? Matrix.fromArray( [].slice.call( arguments ) ) + : base // Merge the source matrix with the base matrix this.a = source.a != null ? source.a : base.a @@ -34,56 +40,68 @@ export default class Matrix { this.f = source.f != null ? source.f : base.f return this + } // Clones this matrix clone () { - return new Matrix(this) + + return new Matrix( this ) + } // Transform a matrix into another matrix by manipulating the space - transform (o) { + transform ( o ) { + // Check if o is a matrix and then left multiply it directly - if (Matrix.isMatrixLike(o)) { - var matrix = new Matrix(o) - return matrix.multiplyO(this) + if ( Matrix.isMatrixLike( o ) ) { + + var matrix = new Matrix( o ) + return matrix.multiplyO( this ) + } // Get the proposed transformations and the current transformations - var t = Matrix.formatTransforms(o) + var t = Matrix.formatTransforms( o ) var current = this - let { x: ox, y: oy } = new Point(t.ox, t.oy).transform(current) + let { x: ox, y: oy } = new Point( t.ox, t.oy ).transform( current ) // Construct the resulting matrix var transformer = new Matrix() - .translateO(t.rx, t.ry) - .lmultiplyO(current) - .translateO(-ox, -oy) - .scaleO(t.scaleX, t.scaleY) - .skewO(t.skewX, t.skewY) - .shearO(t.shear) - .rotateO(t.theta) - .translateO(ox, oy) + .translateO( t.rx, t.ry ) + .lmultiplyO( current ) + .translateO( -ox, -oy ) + .scaleO( t.scaleX, t.scaleY ) + .skewO( t.skewX, t.skewY ) + .shearO( t.shear ) + .rotateO( t.theta ) + .translateO( ox, oy ) // If we want the origin at a particular place, we force it there - if (isFinite(t.px) || isFinite(t.py)) { - const origin = new Point(ox, oy).transform(transformer) + if ( isFinite( t.px ) || isFinite( t.py ) ) { + + const origin = new Point( ox, oy ).transform( transformer ) // TODO: Replace t.px with isFinite(t.px) const dx = t.px ? t.px - origin.x : 0 const dy = t.py ? t.py - origin.y : 0 - transformer.translateO(dx, dy) + transformer.translateO( dx, dy ) + } // Translate now after positioning - transformer.translateO(t.tx, t.ty) + transformer.translateO( t.tx, t.ty ) return transformer + } // Applies a matrix defined by its affine parameters - compose (o) { - if (o.origin) { + compose ( o ) { + + if ( o.origin ) { + o.originX = o.origin[0] o.originY = o.origin[1] + } // Get the parameters var ox = o.originX || 0 @@ -97,18 +115,20 @@ export default class Matrix { // Apply the standard matrix var result = new Matrix() - .translateO(-ox, -oy) - .scaleO(sx, sy) - .shearO(lam) - .rotateO(theta) - .translateO(tx, ty) - .lmultiplyO(this) - .translateO(ox, oy) + .translateO( -ox, -oy ) + .scaleO( sx, sy ) + .shearO( lam ) + .rotateO( theta ) + .translateO( tx, ty ) + .lmultiplyO( this ) + .translateO( ox, oy ) return result + } // Decomposes this matrix into its affine parameters - decompose (cx = 0, cy = 0) { + decompose ( cx = 0, cy = 0 ) { + // Get the parameters from the matrix var a = this.a var b = this.b @@ -123,20 +143,20 @@ export default class Matrix { // Since we only shear in x, we can use the x basis to get the x scale // and the rotation of the resulting matrix - var sx = ccw * Math.sqrt(a * a + b * b) - var thetaRad = Math.atan2(ccw * b, ccw * a) + var sx = ccw * Math.sqrt( a * a + b * b ) + var thetaRad = Math.atan2( ccw * b, ccw * a ) var theta = 180 / Math.PI * thetaRad - var ct = Math.cos(thetaRad) - var st = Math.sin(thetaRad) + var ct = Math.cos( thetaRad ) + var st = Math.sin( thetaRad ) // We can then solve the y basis vector simultaneously to get the other // two affine parameters directly from these parameters - var lam = (a * c + b * d) / determinant - var sy = ((c * sx) / (lam * a - b)) || ((d * sx) / (lam * b + a)) + var lam = ( a * c + b * d ) / determinant + var sy = ( ( c * sx ) / ( lam * a - b ) ) || ( ( d * sx ) / ( lam * b + a ) ) // Use the translations - let tx = e - cx + cx * ct * sx + cy * (lam * ct * sx - st * sy) - let ty = f - cy + cx * st * sx + cy * (lam * st * sx + ct * sy) + let tx = e - cx + cx * ct * sx + cy * ( lam * ct * sx - st * sy ) + let ty = f - cy + cx * st * sx + cy * ( lam * st * sx + ct * sy ) // Construct the decomposition and return it return { @@ -158,38 +178,48 @@ export default class Matrix { e: this.e, f: this.f } + } // Left multiplies by the given matrix - multiply (matrix) { - return this.clone().multiplyO(matrix) + multiply ( matrix ) { + + return this.clone().multiplyO( matrix ) + } - multiplyO (matrix) { + multiplyO ( matrix ) { + // Get the matrices var l = this var r = matrix instanceof Matrix ? matrix - : new Matrix(matrix) + : new Matrix( matrix ) + + return Matrix.matrixMultiply( l, r, this ) - return Matrix.matrixMultiply(l, r, this) } - lmultiply (matrix) { - return this.clone().lmultiplyO(matrix) + lmultiply ( matrix ) { + + return this.clone().lmultiplyO( matrix ) + } - lmultiplyO (matrix) { + lmultiplyO ( matrix ) { + var r = this var l = matrix instanceof Matrix ? matrix - : new Matrix(matrix) + : new Matrix( matrix ) + + return Matrix.matrixMultiply( l, r, this ) - return Matrix.matrixMultiply(l, r, this) } // Inverses matrix inverseO () { + // Get the current parameters out of the matrix var a = this.a var b = this.b @@ -200,7 +230,7 @@ export default class Matrix { // Invert the 2x2 matrix in the top left var det = a * d - b * c - if (!det) throw new Error('Cannot invert ' + this) + if ( !det ) throw new Error( 'Cannot invert ' + this ) // Calculate the top 2x2 matrix var na = d / det @@ -209,8 +239,8 @@ export default class Matrix { var nd = a / det // Apply the inverted matrix to the top right - var ne = -(na * e + nc * f) - var nf = -(nb * e + nd * f) + var ne = -( na * e + nc * f ) + var nf = -( nb * e + nd * f ) // Construct the inverted matrix this.a = na @@ -221,34 +251,46 @@ export default class Matrix { this.f = nf return this + } inverse () { + return this.clone().inverseO() + } // Translate matrix - translate (x, y) { - return this.clone().translateO(x, y) + translate ( x, y ) { + + return this.clone().translateO( x, y ) + } - translateO (x, y) { + translateO ( x, y ) { + this.e += x || 0 this.f += y || 0 return this + } // Scale matrix - scale (x, y, cx, cy) { - return this.clone().scaleO(...arguments) + scale ( x, y, cx, cy ) { + + return this.clone().scaleO( ...arguments ) + } - scaleO (x, y = x, cx = 0, cy = 0) { + scaleO ( x, y = x, cx = 0, cy = 0 ) { + // Support uniform scaling - if (arguments.length === 3) { + if ( arguments.length === 3 ) { + cy = cx cx = y y = x + } let { a, b, c, d, e, f } = this @@ -261,19 +303,23 @@ export default class Matrix { this.f = f * y - cy * y + cy return this + } // Rotate matrix - rotate (r, cx, cy) { - return this.clone().rotateO(r, cx, cy) + rotate ( r, cx, cy ) { + + return this.clone().rotateO( r, cx, cy ) + } - rotateO (r, cx = 0, cy = 0) { + rotateO ( r, cx = 0, cy = 0 ) { + // Convert degrees to radians - r = radians(r) + r = radians( r ) - let cos = Math.cos(r) - let sin = Math.sin(r) + let cos = Math.cos( r ) + let sin = Math.sin( r ) let { a, b, c, d, e, f } = this @@ -285,25 +331,33 @@ export default class Matrix { this.f = f * cos + e * sin - cx * sin - cy * cos + cy return this + } // Flip matrix on x or y, at a given offset - flip (axis, around) { - return this.clone().flipO(axis, around) + flip ( axis, around ) { + + return this.clone().flipO( axis, around ) + } - flipO (axis, around) { - return axis === 'x' ? this.scaleO(-1, 1, around, 0) - : axis === 'y' ? this.scaleO(1, -1, 0, around) - : this.scaleO(-1, -1, axis, around || axis) // Define an x, y flip point + flipO ( axis, around ) { + + return axis === 'x' ? this.scaleO( -1, 1, around, 0 ) + : axis === 'y' ? this.scaleO( 1, -1, 0, around ) + : this.scaleO( -1, -1, axis, around || axis ) // Define an x, y flip point + } // Shear matrix - shear (a, cx, cy) { - return this.clone().shearO(a, cx, cy) + shear ( a, cx, cy ) { + + return this.clone().shearO( a, cx, cy ) + } - shearO (lx, cx = 0, cy = 0) { + shearO ( lx, cx = 0, cy = 0 ) { + let { a, b, c, d, e, f } = this this.a = a + b * lx @@ -311,27 +365,33 @@ export default class Matrix { this.e = e + f * lx - cy * lx return this + } // Skew Matrix - skew (x, y, cx, cy) { - return this.clone().skewO(...arguments) + skew ( x, y, cx, cy ) { + + return this.clone().skewO( ...arguments ) + } - skewO (x, y = x, cx = 0, cy = 0) { + skewO ( x, y = x, cx = 0, cy = 0 ) { + // support uniformal skew - if (arguments.length === 3) { + if ( arguments.length === 3 ) { + cy = cx cx = y y = x + } // Convert degrees to radians - x = radians(x) - y = radians(y) + x = radians( x ) + y = radians( y ) - let lx = Math.tan(x) - let ly = Math.tan(y) + let lx = Math.tan( x ) + let ly = Math.tan( y ) let { a, b, c, d, e, f } = this @@ -343,55 +403,75 @@ export default class Matrix { this.f = f + e * ly - cx * ly return this + } // SkewX - skewX (x, cx, cy) { - return this.skew(x, 0, cx, cy) + skewX ( x, cx, cy ) { + + return this.skew( x, 0, cx, cy ) + } - skewXO (x, cx, cy) { - return this.skewO(x, 0, cx, cy) + skewXO ( x, cx, cy ) { + + return this.skewO( x, 0, cx, cy ) + } // SkewY - skewY (y, cx, cy) { - return this.skew(0, y, cx, cy) + skewY ( y, cx, cy ) { + + return this.skew( 0, y, cx, cy ) + } - skewYO (y, cx, cy) { - return this.skewO(0, y, cx, cy) + skewYO ( y, cx, cy ) { + + return this.skewO( 0, y, cx, cy ) + } // Transform around a center point - aroundO (cx, cy, matrix) { + aroundO ( cx, cy, matrix ) { + var dx = cx || 0 var dy = cy || 0 - return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy) + return this.translateO( -dx, -dy ).lmultiplyO( matrix ).translateO( dx, dy ) + } - around (cx, cy, matrix) { - return this.clone().aroundO(cx, cy, matrix) + around ( cx, cy, matrix ) { + + return this.clone().aroundO( cx, cy, matrix ) + } // Check if two matrices are equal - equals (other) { - var comp = new Matrix(other) - return closeEnough(this.a, comp.a) && closeEnough(this.b, comp.b) && - closeEnough(this.c, comp.c) && closeEnough(this.d, comp.d) && - closeEnough(this.e, comp.e) && closeEnough(this.f, comp.f) + equals ( other ) { + + var comp = new Matrix( other ) + return closeEnough( this.a, comp.a ) && closeEnough( this.b, comp.b ) + && closeEnough( this.c, comp.c ) && closeEnough( this.d, comp.d ) + && closeEnough( this.e, comp.e ) && closeEnough( this.f, comp.f ) + } // Convert matrix to string toString () { + return 'matrix(' + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ')' + } toArray () { - return [this.a, this.b, this.c, this.d, this.e, this.f] + + return [ this.a, this.b, this.c, this.d, this.e, this.f ] + } valueOf () { + return { a: this.a, b: this.b, @@ -400,56 +480,62 @@ export default class Matrix { e: this.e, f: this.f } + } - static fromArray (a) { + static fromArray ( a ) { + return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] } + } - static isMatrixLike (o) { + static isMatrixLike ( o ) { + return ( - o.a != null || - o.b != null || - o.c != null || - o.d != null || - o.e != null || - o.f != null + o.a != null + || o.b != null + || o.c != null + || o.d != null + || o.e != null + || o.f != null ) + } - static formatTransforms (o) { + static formatTransforms ( o ) { + // Get all of the parameters required to form the matrix var flipBoth = o.flip === 'both' || o.flip === true - var flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1 - var flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1 + var flipX = o.flip && ( flipBoth || o.flip === 'x' ) ? -1 : 1 + var flipY = o.flip && ( flipBoth || o.flip === 'y' ) ? -1 : 1 var skewX = o.skew && o.skew.length ? o.skew[0] - : isFinite(o.skew) ? o.skew - : isFinite(o.skewX) ? o.skewX - : 0 + : isFinite( o.skew ) ? o.skew + : isFinite( o.skewX ) ? o.skewX + : 0 var skewY = o.skew && o.skew.length ? o.skew[1] - : isFinite(o.skew) ? o.skew - : isFinite(o.skewY) ? o.skewY - : 0 + : isFinite( o.skew ) ? o.skew + : isFinite( o.skewY ) ? o.skewY + : 0 var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX - : isFinite(o.scale) ? o.scale * flipX - : isFinite(o.scaleX) ? o.scaleX * flipX - : flipX + : isFinite( o.scale ) ? o.scale * flipX + : isFinite( o.scaleX ) ? o.scaleX * flipX + : flipX var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY - : isFinite(o.scale) ? o.scale * flipY - : isFinite(o.scaleY) ? o.scaleY * flipY - : flipY + : isFinite( o.scale ) ? o.scale * flipY + : isFinite( o.scaleY ) ? o.scaleY * flipY + : flipY var shear = o.shear || 0 var theta = o.rotate || o.theta || 0 - var origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY) + var origin = new Point( o.origin || o.around || o.ox || o.originX, o.oy || o.originY ) var ox = origin.x var oy = origin.y - var position = new Point(o.position || o.px || o.positionX, o.py || o.positionY) + var position = new Point( o.position || o.px || o.positionX, o.py || o.positionY ) var px = position.x var py = position.y - var translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY) + var translate = new Point( o.translate || o.tx || o.translateX, o.ty || o.translateY ) var tx = translate.x var ty = translate.y - var relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY) + var relative = new Point( o.relative || o.rx || o.relativeX, o.ry || o.relativeY ) var rx = relative.x var ry = relative.y @@ -457,10 +543,12 @@ export default class Matrix { return { scaleX, scaleY, skewX, skewY, shear, theta, rx, ry, tx, ty, ox, oy, px, py } + } // left matrix, right matrix, target matrix which is overwritten - static matrixMultiply (l, r, o) { + static matrixMultiply ( l, r, o ) { + // Work out the product directly var a = l.a * r.a + l.c * r.b var b = l.b * r.a + l.d * r.b @@ -478,23 +566,31 @@ export default class Matrix { o.f = f return o + } + } export function ctm () { - return new Matrix(this.node.getCTM()) + + return new Matrix( this.node.getCTM() ) + } export function screenCTM () { + /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537 This is needed because FF does not return the transformation matrix for the inner coordinate system when getScreenCTM() is called on nested svgs. However all other Browsers do that */ - if (typeof this.isRoot === 'function' && !this.isRoot()) { - var rect = this.rect(1, 1) + if ( typeof this.isRoot === 'function' && !this.isRoot() ) { + + var rect = this.rect( 1, 1 ) var m = rect.node.getScreenCTM() rect.remove() - return new Matrix(m) + return new Matrix( m ) + } - return new Matrix(this.node.getScreenCTM()) + return new Matrix( this.node.getScreenCTM() ) + } diff --git a/src/types/Morphable.js b/src/types/Morphable.js index 703cc00..87fa800 100644 --- a/src/types/Morphable.js +++ b/src/types/Morphable.js @@ -11,135 +11,200 @@ import SVGArray from './SVGArray.js' import SVGNumber from './SVGNumber.js' export default class Morphable { - constructor (stepper) { - this._stepper = stepper || new Ease('-') + + constructor ( stepper ) { + + this._stepper = stepper || new Ease( '-' ) this._from = null this._to = null this._type = null this._context = null this._morphObj = null + } - from (val) { - if (val == null) { + from ( val ) { + + if ( val == null ) { + return this._from + } - this._from = this._set(val) + this._from = this._set( val ) return this + } - to (val) { - if (val == null) { + to ( val ) { + + if ( val == null ) { + return this._to + } - this._to = this._set(val) + this._to = this._set( val ) return this + } - type (type) { + type ( type ) { + // getter - if (type == null) { + if ( type == null ) { + return this._type + } // setter this._type = type return this + } - _set (value) { - if (!this._type) { + _set ( value ) { + + if ( !this._type ) { + var type = typeof value - if (type === 'number') { - this.type(SVGNumber) - } else if (type === 'string') { - if (Color.isColor(value)) { - this.type(Color) - } else if (delimiter.test(value)) { - this.type(pathLetters.test(value) + if ( type === 'number' ) { + + this.type( SVGNumber ) + + } else if ( type === 'string' ) { + + if ( Color.isColor( value ) ) { + + this.type( Color ) + + } else if ( delimiter.test( value ) ) { + + this.type( pathLetters.test( value ) ? PathArray : SVGArray ) - } else if (numberAndUnit.test(value)) { - this.type(SVGNumber) + + } else if ( numberAndUnit.test( value ) ) { + + this.type( SVGNumber ) + } else { - this.type(NonMorphable) + + this.type( NonMorphable ) + } - } else if (morphableTypes.indexOf(value.constructor) > -1) { - this.type(value.constructor) - } else if (Array.isArray(value)) { - this.type(SVGArray) - } else if (type === 'object') { - this.type(ObjectBag) + + } else if ( morphableTypes.indexOf( value.constructor ) > -1 ) { + + this.type( value.constructor ) + + } else if ( Array.isArray( value ) ) { + + this.type( SVGArray ) + + } else if ( type === 'object' ) { + + this.type( ObjectBag ) + } else { - this.type(NonMorphable) + + this.type( NonMorphable ) + } + } - var result = (new this._type(value)).toArray() + var result = ( new this._type( value ) ).toArray() this._morphObj = this._morphObj || new this._type() - this._context = this._context || - Array.apply(null, Array(result.length)).map(Object) + this._context = this._context + || Array.apply( null, Array( result.length ) ).map( Object ) return result + } - stepper (stepper) { - if (stepper == null) return this._stepper + stepper ( stepper ) { + + if ( stepper == null ) return this._stepper this._stepper = stepper return this + } done () { + var complete = this._context - .map(this._stepper.done) - .reduce(function (last, curr) { + .map( this._stepper.done ) + .reduce( function ( last, curr ) { + return last && curr - }, true) + + }, true ) return complete + } - at (pos) { + at ( pos ) { + var _this = this return this._morphObj.fromArray( - this._from.map(function (i, index) { - return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context) - }) + this._from.map( function ( i, index ) { + + return _this._stepper.step( i, _this._to[index], pos, _this._context[index], _this._context ) + + } ) ) + } + } export class NonMorphable { - constructor (...args) { - this.init(...args) + + constructor ( ...args ) { + + this.init( ...args ) + } - init (val) { - val = Array.isArray(val) ? val[0] : val + init ( val ) { + + val = Array.isArray( val ) ? val[0] : val this.value = val return this + } valueOf () { + return this.value + } toArray () { - return [this.value] + + return [ this.value ] + } + } export class TransformBag { - constructor (...args) { - this.init(...args) + + constructor ( ...args ) { + + this.init( ...args ) + } - init (obj) { - if (Array.isArray(obj)) { + init ( obj ) { + + if ( Array.isArray( obj ) ) { + obj = { scaleX: obj[0], scaleY: obj[1], @@ -150,13 +215,16 @@ export class TransformBag { originX: obj[6], originY: obj[7] } + } - Object.assign(this, TransformBag.defaults, obj) + Object.assign( this, TransformBag.defaults, obj ) return this + } toArray () { + var v = this return [ @@ -169,7 +237,9 @@ export class TransformBag { v.originX, v.originY ] + } + } TransformBag.defaults = { @@ -184,40 +254,56 @@ TransformBag.defaults = { } export class ObjectBag { - constructor (...args) { - this.init(...args) + + constructor ( ...args ) { + + this.init( ...args ) + } - init (objOrArr) { + init ( objOrArr ) { + this.values = [] - if (Array.isArray(objOrArr)) { + if ( Array.isArray( objOrArr ) ) { + this.values = objOrArr return + } - var entries = Object.entries(objOrArr || {}).sort((a, b) => { + var entries = Object.entries( objOrArr || {} ).sort( ( a, b ) => { + return a[0] - b[0] - }) - this.values = entries.reduce((last, curr) => last.concat(curr), []) + } ) + + this.values = entries.reduce( ( last, curr ) => last.concat( curr ), [] ) return this + } valueOf () { + var obj = {} var arr = this.values - for (var i = 0, len = arr.length; i < len; i += 2) { + for ( var i = 0, len = arr.length; i < len; i += 2 ) { + obj[arr[i]] = arr[i + 1] + } return obj + } toArray () { + return this.values + } + } const morphableTypes = [ @@ -226,21 +312,29 @@ const morphableTypes = [ ObjectBag ] -export function registerMorphableType (type = []) { - morphableTypes.push(...[].concat(type)) +export function registerMorphableType ( type = [] ) { + + morphableTypes.push( ...[].concat( type ) ) + } export function makeMorphable () { - extend(morphableTypes, { - to (val) { + + extend( morphableTypes, { + to ( val ) { + return new Morphable() - .type(this.constructor) - .from(this.valueOf()) - .to(val) + .type( this.constructor ) + .from( this.valueOf() ) + .to( val ) + }, - fromArray (arr) { - this.init(arr) + fromArray ( arr ) { + + this.init( arr ) return this + } - }) + } ) + } diff --git a/src/types/PathArray.js b/src/types/PathArray.js index 989cd8f..739218d 100644 --- a/src/types/PathArray.js +++ b/src/types/PathArray.js @@ -12,131 +12,182 @@ import Point from './Point.js' import SVGArray from './SVGArray.js' import parser from '../modules/core/parser.js' -const PathArray = subClassArray('PathArray', SVGArray) +const PathArray = subClassArray( 'PathArray', SVGArray ) export default PathArray -export function pathRegReplace (a, b, c, d) { - return c + d.replace(dots, ' .') +export function pathRegReplace ( a, b, c, d ) { + + return c + d.replace( dots, ' .' ) + } -function arrayToString (a) { - for (var i = 0, il = a.length, s = ''; i < il; i++) { +function arrayToString ( a ) { + + for ( var i = 0, il = a.length, s = ''; i < il; i++ ) { + s += a[i][0] - if (a[i][1] != null) { + if ( a[i][1] != null ) { + s += a[i][1] - if (a[i][2] != null) { + if ( a[i][2] != null ) { + s += ' ' s += a[i][2] - if (a[i][3] != null) { + if ( a[i][3] != null ) { + s += ' ' s += a[i][3] s += ' ' s += a[i][4] - if (a[i][5] != null) { + if ( a[i][5] != null ) { + s += ' ' s += a[i][5] s += ' ' s += a[i][6] - if (a[i][7] != null) { + if ( a[i][7] != null ) { + s += ' ' s += a[i][7] + } + } + } + } + } + } return s + ' ' + } const pathHandlers = { - M: function (c, p, p0) { + M: function ( c, p, p0 ) { + p.x = p0.x = c[0] p.y = p0.y = c[1] - return ['M', p.x, p.y] + return [ 'M', p.x, p.y ] + }, - L: function (c, p) { + L: function ( c, p ) { + p.x = c[0] p.y = c[1] - return ['L', c[0], c[1]] + return [ 'L', c[0], c[1] ] + }, - H: function (c, p) { + H: function ( c, p ) { + p.x = c[0] - return ['H', c[0]] + return [ 'H', c[0] ] + }, - V: function (c, p) { + V: function ( c, p ) { + p.y = c[0] - return ['V', c[0]] + return [ 'V', c[0] ] + }, - C: function (c, p) { + C: function ( c, p ) { + p.x = c[4] p.y = c[5] - return ['C', c[0], c[1], c[2], c[3], c[4], c[5]] + return [ 'C', c[0], c[1], c[2], c[3], c[4], c[5] ] + }, - S: function (c, p) { + S: function ( c, p ) { + p.x = c[2] p.y = c[3] - return ['S', c[0], c[1], c[2], c[3]] + return [ 'S', c[0], c[1], c[2], c[3] ] + }, - Q: function (c, p) { + Q: function ( c, p ) { + p.x = c[2] p.y = c[3] - return ['Q', c[0], c[1], c[2], c[3]] + return [ 'Q', c[0], c[1], c[2], c[3] ] + }, - T: function (c, p) { + T: function ( c, p ) { + p.x = c[0] p.y = c[1] - return ['T', c[0], c[1]] + return [ 'T', c[0], c[1] ] + }, - Z: function (c, p, p0) { + Z: function ( c, p, p0 ) { + p.x = p0.x p.y = p0.y - return ['Z'] + return [ 'Z' ] + }, - A: function (c, p) { + A: function ( c, p ) { + p.x = c[5] p.y = c[6] - return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]] + return [ 'A', c[0], c[1], c[2], c[3], c[4], c[5], c[6] ] + } } -let mlhvqtcsaz = 'mlhvqtcsaz'.split('') +let mlhvqtcsaz = 'mlhvqtcsaz'.split( '' ) + +for ( var i = 0, il = mlhvqtcsaz.length; i < il; ++i ) { + + pathHandlers[mlhvqtcsaz[i]] = ( function ( i ) { + + return function ( c, p, p0 ) { + + if ( i === 'H' ) c[0] = c[0] + p.x + else if ( i === 'V' ) c[0] = c[0] + p.y + else if ( i === 'A' ) { -for (var i = 0, il = mlhvqtcsaz.length; i < il; ++i) { - pathHandlers[mlhvqtcsaz[i]] = (function (i) { - return function (c, p, p0) { - if (i === 'H') c[0] = c[0] + p.x - else if (i === 'V') c[0] = c[0] + p.y - else if (i === 'A') { c[5] = c[5] + p.x c[6] = c[6] + p.y + } else { - for (var j = 0, jl = c.length; j < jl; ++j) { - c[j] = c[j] + (j % 2 ? p.y : p.x) + + for ( var j = 0, jl = c.length; j < jl; ++j ) { + + c[j] = c[j] + ( j % 2 ? p.y : p.x ) + } + } - return pathHandlers[i](c, p, p0) + return pathHandlers[i]( c, p, p0 ) + } - })(mlhvqtcsaz[i].toUpperCase()) + + } )( mlhvqtcsaz[i].toUpperCase() ) + } -extend(PathArray, { +extend( PathArray, { // Convert array to string toString () { - return arrayToString(this) + + return arrayToString( this ) + }, // Move path string - move (x, y) { + move ( x, y ) { + // get bounding box of current situation var box = this.bbox() @@ -144,110 +195,154 @@ extend(PathArray, { x -= box.x y -= box.y - if (!isNaN(x) && !isNaN(y)) { + if ( !isNaN( x ) && !isNaN( y ) ) { + // move every point - for (var l, i = this.length - 1; i >= 0; i--) { + for ( var l, i = this.length - 1; i >= 0; i-- ) { + l = this[i][0] - if (l === 'M' || l === 'L' || l === 'T') { + if ( l === 'M' || l === 'L' || l === 'T' ) { + this[i][1] += x this[i][2] += y - } else if (l === 'H') { + + } else if ( l === 'H' ) { + this[i][1] += x - } else if (l === 'V') { + + } else if ( l === 'V' ) { + this[i][1] += y - } else if (l === 'C' || l === 'S' || l === 'Q') { + + } else if ( l === 'C' || l === 'S' || l === 'Q' ) { + this[i][1] += x this[i][2] += y this[i][3] += x this[i][4] += y - if (l === 'C') { + if ( l === 'C' ) { + this[i][5] += x this[i][6] += y + } - } else if (l === 'A') { + + } else if ( l === 'A' ) { + this[i][6] += x this[i][7] += y + } + } + } return this + }, // Resize path string - size (width, height) { + size ( width, height ) { + // get bounding box of current situation var box = this.bbox() var i, l // recalculate position of all points according to new size - for (i = this.length - 1; i >= 0; i--) { + for ( i = this.length - 1; i >= 0; i-- ) { + l = this[i][0] - if (l === 'M' || l === 'L' || l === 'T') { - this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x - this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y - } else if (l === 'H') { - this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x - } else if (l === 'V') { - this[i][1] = ((this[i][1] - box.y) * height) / box.height + box.y - } else if (l === 'C' || l === 'S' || l === 'Q') { - this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x - this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y - this[i][3] = ((this[i][3] - box.x) * width) / box.width + box.x - this[i][4] = ((this[i][4] - box.y) * height) / box.height + box.y - - if (l === 'C') { - this[i][5] = ((this[i][5] - box.x) * width) / box.width + box.x - this[i][6] = ((this[i][6] - box.y) * height) / box.height + box.y + if ( l === 'M' || l === 'L' || l === 'T' ) { + + this[i][1] = ( ( this[i][1] - box.x ) * width ) / box.width + box.x + this[i][2] = ( ( this[i][2] - box.y ) * height ) / box.height + box.y + + } else if ( l === 'H' ) { + + this[i][1] = ( ( this[i][1] - box.x ) * width ) / box.width + box.x + + } else if ( l === 'V' ) { + + this[i][1] = ( ( this[i][1] - box.y ) * height ) / box.height + box.y + + } else if ( l === 'C' || l === 'S' || l === 'Q' ) { + + this[i][1] = ( ( this[i][1] - box.x ) * width ) / box.width + box.x + this[i][2] = ( ( this[i][2] - box.y ) * height ) / box.height + box.y + this[i][3] = ( ( this[i][3] - box.x ) * width ) / box.width + box.x + this[i][4] = ( ( this[i][4] - box.y ) * height ) / box.height + box.y + + if ( l === 'C' ) { + + this[i][5] = ( ( this[i][5] - box.x ) * width ) / box.width + box.x + this[i][6] = ( ( this[i][6] - box.y ) * height ) / box.height + box.y + } - } else if (l === 'A') { + + } else if ( l === 'A' ) { + // resize radii - this[i][1] = (this[i][1] * width) / box.width - this[i][2] = (this[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[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 + this[i][6] = ( ( this[i][6] - box.x ) * width ) / box.width + box.x + this[i][7] = ( ( this[i][7] - box.y ) * height ) / box.height + box.y + } + } return this + }, // Test if the passed path array use the same path data commands as this path array - equalCommands (pathArray) { + equalCommands ( pathArray ) { + var i, il, equalCommands - pathArray = new PathArray(pathArray) + pathArray = new PathArray( pathArray ) equalCommands = this.length === pathArray.length - for (i = 0, il = this.length; equalCommands && i < il; i++) { + for ( i = 0, il = this.length; equalCommands && i < il; i++ ) { + equalCommands = this[i][0] === pathArray[i][0] + } return equalCommands + }, // Make path array morphable - morph (pathArray) { - pathArray = new PathArray(pathArray) + morph ( pathArray ) { + + pathArray = new PathArray( pathArray ) + + if ( this.equalCommands( pathArray ) ) { - if (this.equalCommands(pathArray)) { this.destination = pathArray + } else { + this.destination = null + } return this + }, // Get morphed path array at given position - at (pos) { + at ( pos ) { + // make sure a destination is defined - if (!this.destination) return this + if ( !this.destination ) return this var sourceArray = this var destinationArray = this.destination.value @@ -257,47 +352,61 @@ extend(PathArray, { // Animate has specified in the SVG spec // See: https://www.w3.org/TR/SVG11/paths.html#PathElement - for (i = 0, il = sourceArray.length; i < il; i++) { - array[i] = [sourceArray[i][0]] - for (j = 1, jl = sourceArray[i].length; j < jl; j++) { - array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos + for ( i = 0, il = sourceArray.length; i < il; i++ ) { + + array[i] = [ sourceArray[i][0] ] + for ( j = 1, jl = sourceArray[i].length; j < jl; j++ ) { + + array[i][j] = sourceArray[i][j] + ( destinationArray[i][j] - sourceArray[i][j] ) * pos + } // For the two flags of the elliptical arc command, the SVG spec say: // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true // Elliptical arc command as an array followed by corresponding indexes: // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] // 0 1 2 3 4 5 6 7 - if (array[i][0] === 'A') { - array[i][4] = +(array[i][4] !== 0) - array[i][5] = +(array[i][5] !== 0) + if ( array[i][0] === 'A' ) { + + array[i][4] = +( array[i][4] !== 0 ) + array[i][5] = +( array[i][5] !== 0 ) + } + } // Directly modify the value of a path array, this is done this way for performance pathArray.value = array return pathArray + }, // Absolutize and parse path to array - parse (array = [['M', 0, 0]]) { + parse ( array = [ [ 'M', 0, 0 ] ] ) { + // if it's already a patharray, no need to parse it - if (array instanceof PathArray) return array + if ( array instanceof PathArray ) return array // prepare for parsing var s var paramCnt = { 'M': 2, 'L': 2, 'H': 1, 'V': 1, 'C': 6, 'S': 4, 'Q': 4, 'T': 2, 'A': 7, 'Z': 0 } - if (typeof array === 'string') { + if ( typeof array === 'string' ) { + array = array - .replace(numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123 - .replace(pathLetters, ' $& ') // put some room between letters and numbers - .replace(hyphen, '$1 -') // add space before hyphen + .replace( numbersWithDots, pathRegReplace ) // convert 45.123.123 to 45.123 .123 + .replace( pathLetters, ' $& ' ) // put some room between letters and numbers + .replace( hyphen, '$1 -' ) // add space before hyphen .trim() // trim - .split(delimiter) // split into array + .split( delimiter ) // split into array + } else { - array = array.reduce(function (prev, curr) { - return [].concat.call(prev, curr) - }, []) + + array = array.reduce( function ( prev, curr ) { + + return [].concat.call( prev, curr ) + + }, [] ) + } // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...] @@ -308,30 +417,41 @@ extend(PathArray, { var len = array.length do { + // Test if we have a path letter - if (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 - } else if (s === 'M') { + // If last letter was a move command and we got no new, it defaults to [L]ine + + } else if ( s === 'M' ) { + s = 'L' - } else if (s === 'm') { + + } else if ( s === 'm' ) { + s = 'l' + } - result.push(pathHandlers[s].call(null, - array.slice(index, (index = index + paramCnt[s.toUpperCase()])).map(parseFloat), + result.push( pathHandlers[s].call( null, + array.slice( index, ( index = index + paramCnt[s.toUpperCase()] ) ).map( parseFloat ), p, p0 ) ) - } while (len > index) + + } while ( len > index ) return result + }, // Get bounding box of path bbox () { - parser().path.setAttribute('d', this.toString()) + + parser().path.setAttribute( 'd', this.toString() ) return parser.nodes.path.getBBox() + } -}) +} ) diff --git a/src/types/Point.js b/src/types/Point.js index 27d81ea..16ae44d 100644 --- a/src/types/Point.js +++ b/src/types/Point.js @@ -1,45 +1,59 @@ export default class Point { + // Initialize - constructor (...args) { - this.init(...args) + constructor ( ...args ) { + + this.init( ...args ) + } - init (x, y) { + init ( x, y ) { + let source let base = { x: 0, y: 0 } // ensure source as object - source = Array.isArray(x) ? { x: x[0], y: x[1] } + source = Array.isArray( x ) ? { x: x[0], y: x[1] } : typeof x === 'object' ? { x: x.x, y: x.y } - : { x: x, y: y } + : { x: x, y: y } // merge source this.x = source.x == null ? base.x : source.x this.y = source.y == null ? base.y : source.y return this + } // Clone point clone () { - return new Point(this) + + return new Point( this ) + } // transform point with matrix - transform (m) { + transform ( m ) { + // Perform the matrix multiplication var x = m.a * this.x + m.c * this.y + m.e var y = m.b * this.x + m.d * this.y + m.f // Return the required point - return new Point(x, y) + return new Point( x, y ) + } toArray () { - return [this.x, this.y] + + return [ this.x, this.y ] + } + } -export function point (x, y) { - return new Point(x, y).transform(this.screenCTM().inverse()) +export function point ( x, y ) { + + return new Point( x, y ).transform( this.screenCTM().inverse() ) + } diff --git a/src/types/PointArray.js b/src/types/PointArray.js index b246b2f..581b7dc 100644 --- a/src/types/PointArray.js +++ b/src/types/PointArray.js @@ -3,76 +3,97 @@ import { extend } from '../utils/adopter.js' import { subClassArray } from './ArrayPolyfill.js' import SVGArray from './SVGArray.js' -const PointArray = subClassArray('PointArray', SVGArray) +const PointArray = subClassArray( 'PointArray', SVGArray ) export default PointArray -extend(PointArray, { +extend( PointArray, { // Convert array to string toString () { + // convert to a poly point string - for (var i = 0, il = this.length, array = []; i < il; i++) { - array.push(this[i].join(',')) + for ( var i = 0, il = this.length, array = []; i < il; i++ ) { + + array.push( this[i].join( ',' ) ) + } - return array.join(' ') + return array.join( ' ' ) + }, // Convert array to line object toLine () { + return { x1: this[0][0], y1: this[0][1], x2: this[1][0], y2: this[1][1] } + }, // Get morphed array at given position - at (pos) { + at ( pos ) { + // make sure a destination is defined - if (!this.destination) return this + if ( !this.destination ) return this // generate morphed point string - for (var i = 0, il = this.length, array = []; i < il; i++) { - array.push([ - this[i][0] + (this.destination[i][0] - this[i][0]) * pos, - this[i][1] + (this.destination[i][1] - this[i][1]) * pos - ]) + for ( var i = 0, il = this.length, array = []; i < il; i++ ) { + + array.push( [ + this[i][0] + ( this.destination[i][0] - this[i][0] ) * pos, + this[i][1] + ( this.destination[i][1] - this[i][1] ) * pos + ] ) + } - return new PointArray(array) + return new PointArray( array ) + }, // Parse point string and flat array - parse (array = [[0, 0]]) { + parse ( array = [ [ 0, 0 ] ] ) { + var points = [] // if it is an array - if (array instanceof Array) { + if ( array instanceof Array ) { + // and it is not flat, there is no need to parse it - if (array[0] instanceof Array) { + if ( array[0] instanceof Array ) { + return array + } + } else { // Else, it is considered as a string + // parse points - array = array.trim().split(delimiter).map(parseFloat) + array = array.trim().split( delimiter ).map( parseFloat ) + } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints // Odd number of coordinates is an error. In such cases, drop the last odd coordinate. - if (array.length % 2 !== 0) array.pop() + if ( array.length % 2 !== 0 ) array.pop() // wrap points in two-tuples and parse points as floats - for (var i = 0, len = array.length; i < len; i = i + 2) { - points.push([ array[i], array[i + 1] ]) + for ( var i = 0, len = array.length; i < len; i = i + 2 ) { + + points.push( [ array[i], array[i + 1] ] ) + } return points + }, // Move point string - move (x, y) { + move ( x, y ) { + var box = this.bbox() // get relative offset @@ -80,41 +101,54 @@ extend(PointArray, { y -= box.y // move every point - if (!isNaN(x) && !isNaN(y)) { - for (var i = this.length - 1; i >= 0; i--) { - this[i] = [this[i][0] + x, this[i][1] + y] + if ( !isNaN( x ) && !isNaN( y ) ) { + + for ( var i = this.length - 1; i >= 0; i-- ) { + + this[i] = [ this[i][0] + x, this[i][1] + y ] + } + } return this + }, // Resize poly string - size (width, height) { + size ( width, height ) { + var i var box = this.bbox() // recalculate position of all points according to new size - for (i = this.length - 1; i >= 0; i--) { - if (box.width) this[i][0] = ((this[i][0] - box.x) * width) / box.width + box.x - if (box.height) this[i][1] = ((this[i][1] - box.y) * height) / box.height + box.y + for ( i = this.length - 1; i >= 0; i-- ) { + + if ( box.width ) this[i][0] = ( ( this[i][0] - box.x ) * width ) / box.width + box.x + if ( box.height ) this[i][1] = ( ( this[i][1] - box.y ) * height ) / box.height + box.y + } return this + }, // Get bounding box of points bbox () { + var maxX = -Infinity var maxY = -Infinity var minX = Infinity var minY = Infinity - this.forEach(function (el) { - maxX = Math.max(el[0], maxX) - maxY = Math.max(el[1], maxY) - minX = Math.min(el[0], minX) - minY = Math.min(el[1], minY) - }) + this.forEach( function ( el ) { + + maxX = Math.max( el[0], maxX ) + maxY = Math.max( el[1], maxY ) + minX = Math.min( el[0], minX ) + minY = Math.min( el[1], minY ) + + } ) return { x: minX, y: minY, width: maxX - minX, height: maxY - minY } + } -}) +} ) diff --git a/src/types/SVGArray.js b/src/types/SVGArray.js index 7f27ec4..7d59af1 100644 --- a/src/types/SVGArray.js +++ b/src/types/SVGArray.js @@ -2,49 +2,65 @@ import { delimiter } from '../modules/core/regex.js' import { extend } from '../utils/adopter.js' import { subClassArray } from './ArrayPolyfill.js' -const SVGArray = subClassArray('SVGArray', Array, function (arr) { - this.init(arr) -}) +const SVGArray = subClassArray( 'SVGArray', Array, function ( arr ) { + + this.init( arr ) + +} ) export default SVGArray -extend(SVGArray, { - init (arr) { +extend( SVGArray, { + init ( arr ) { + // This catches the case, that native map tries to create an array with new Array(1) - if (typeof arr === 'number') return this + if ( typeof arr === 'number' ) return this this.length = 0 - this.push(...this.parse(arr)) + this.push( ...this.parse( arr ) ) return this + }, toArray () { - return Array.prototype.concat.apply([], this) + + return Array.prototype.concat.apply( [], this ) + }, toString () { - return this.join(' ') + + return this.join( ' ' ) + }, // Flattens the array if needed valueOf () { + const ret = [] - ret.push(...this) + ret.push( ...this ) return ret + }, // Parse whitespace separated string - parse (array = []) { + parse ( array = [] ) { + // If already is an array, no need to parse it - if (array instanceof Array) return array + if ( array instanceof Array ) return array + + return array.trim().split( delimiter ).map( parseFloat ) - return array.trim().split(delimiter).map(parseFloat) }, clone () { - return new this.constructor(this) + + return new this.constructor( this ) + }, toSet () { - return new Set(this) + + return new Set( this ) + } -}) +} ) diff --git a/src/types/SVGNumber.js b/src/types/SVGNumber.js index ea21cbd..a35ed66 100644 --- a/src/types/SVGNumber.js +++ b/src/types/SVGNumber.js @@ -2,88 +2,126 @@ import { numberAndUnit } from '../modules/core/regex.js' // Module for unit convertions export default class SVGNumber { + // Initialize - constructor (...args) { - this.init(...args) + constructor ( ...args ) { + + this.init( ...args ) + } - init (value, unit) { - unit = Array.isArray(value) ? value[1] : unit - value = Array.isArray(value) ? value[0] : value + init ( value, unit ) { + + unit = Array.isArray( value ) ? value[1] : unit + value = Array.isArray( value ) ? value[0] : value // initialize defaults this.value = 0 this.unit = unit || '' // parse value - if (typeof value === 'number') { + if ( typeof value === 'number' ) { + // ensure a valid numeric value - this.value = isNaN(value) ? 0 : !isFinite(value) ? (value < 0 ? -3.4e+38 : +3.4e+38) : value - } else if (typeof value === 'string') { - unit = value.match(numberAndUnit) + this.value = isNaN( value ) ? 0 : !isFinite( value ) ? ( value < 0 ? -3.4e+38 : +3.4e+38 ) : value + + } else if ( typeof value === 'string' ) { + + unit = value.match( numberAndUnit ) + + if ( unit ) { - if (unit) { // make value numeric - this.value = parseFloat(unit[1]) + this.value = parseFloat( unit[1] ) // normalize - if (unit[5] === '%') { this.value /= 100 } else if (unit[5] === 's') { + if ( unit[5] === '%' ) { + + this.value /= 100 + + } else if ( unit[5] === 's' ) { + this.value *= 1000 + } // store unit this.unit = unit[5] + } + } else { - if (value instanceof SVGNumber) { + + if ( value instanceof SVGNumber ) { + this.value = value.valueOf() this.unit = value.unit + } + } return this + } toString () { - return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 + + return ( this.unit === '%' ? ~~( this.value * 1e8 ) / 1e6 : this.unit === 's' ? this.value / 1e3 - : this.value + : this.value ) + this.unit + } toJSON () { + return this.toString() + } toArray () { - return [this.value, this.unit] + + return [ this.value, this.unit ] + } valueOf () { + return this.value + } // Add number - plus (number) { - number = new SVGNumber(number) - return new SVGNumber(this + number, this.unit || number.unit) + plus ( number ) { + + number = new SVGNumber( number ) + return new SVGNumber( this + number, this.unit || number.unit ) + } // Subtract number - minus (number) { - number = new SVGNumber(number) - return new SVGNumber(this - number, this.unit || number.unit) + minus ( number ) { + + number = new SVGNumber( number ) + return new SVGNumber( this - number, this.unit || number.unit ) + } // Multiply number - times (number) { - number = new SVGNumber(number) - return new SVGNumber(this * number, this.unit || number.unit) + times ( number ) { + + number = new SVGNumber( number ) + return new SVGNumber( this * number, this.unit || number.unit ) + } // Divide number - divide (number) { - number = new SVGNumber(number) - return new SVGNumber(this / number, this.unit || number.unit) + divide ( number ) { + + number = new SVGNumber( number ) + return new SVGNumber( this / number, this.unit || number.unit ) + } + } |