From 2bbfb1c24ce497e7f92dc3de3427f3fc39ef545a Mon Sep 17 00:00:00 2001 From: Saivan Date: Mon, 30 Apr 2018 22:46:28 +1000 Subject: [PATCH] Another collaboration session with @Fuzzyma --- dirty.html | 27 + dist/svg.js | 1238 +++++------------------------- dist/svg.min.js | 4 +- gulpfile.js | 4 +- src/{drawLoop.js => animator.js} | 11 +- src/helpers.js | 58 ++ src/matrix.js | 85 +- src/morph.js | 216 +++++- src/number.js | 7 - src/timeline.js | 220 +++--- 10 files changed, 669 insertions(+), 1201 deletions(-) create mode 100644 dirty.html rename src/{drawLoop.js => animator.js} (91%) diff --git a/dirty.html b/dirty.html new file mode 100644 index 0000000..4896341 --- /dev/null +++ b/dirty.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/dist/svg.js b/dist/svg.js index 1c92b34..7e80e03 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -6,7 +6,7 @@ * @copyright Wout Fierens * @license MIT * -* BUILT: Tue Apr 17 2018 21:58:30 GMT+1000 (AEST) +* BUILT: Mon Apr 30 2018 18:47:43 GMT+1000 (AEST) */; (function(root, factory) { @@ -242,6 +242,13 @@ SVG.utils = { SVG.defaults = { + // Default animation values + timeline: { + duration: 600, + ease: '>', + delay: 0, + }, + // Default attribute values attrs: { @@ -364,89 +371,6 @@ SVG.Queue = SVG.invent({ } }) - -SVG.Draw = { - nextDraw: null, - frames: new SVG.Queue(), - timeouts: new SVG.Queue(), - frameCount: 0, - timeoutCount: 0, - timer: window.performance || window.Date, - - frame: function (fn) { - SVG.Draw.frames.push({ - id: SVG.Draw.frameCount, - run: fn - }) - - if (SVG.Draw.nextDraw === null) { - SVG.Draw.nextDraw = requestAnimationFrame(SVG.Draw._draw) - } - - return ++SVG.Draw.frameCount - }, - - timeout: function (fn, delay) { - delay = delay || 0 - - // Work out when the event should fire - var time = SVG.Draw.timer.now() + delay - - // Add the timeout to the end of the queue - var thisId = SVG.Draw.timeoutCount++ - SVG.Draw.timeouts.push({ - id: thisId, - run: fn, - time: time - }) - - // Request another animation frame if we need one - if (SVG.Draw.nextDraw === null) { - SVG.Draw.nextDraw = requestAnimationFrame(SVG.Draw._draw) - } - - return thisId - }, - - cancelTimeout: function (id) { - // Find the index of the timeout to cancel and remove it - var index = SVG.Draw.timeouts.remove(function (t) { return t.id == id }) - return index - }, - - _draw: function (now) { - // Run all the timeouts we can run, if they are not ready yet, add them - // to the end of the queue immediately! (bad timeouts!!! [sarcasm]) - var tracking = true - var nextTimeout = null - var lastTimeout = SVG.Draw.timeouts.last() - while ((nextTimeout = SVG.Draw.timeouts.shift())) { - // Run the timeout if its time, or push it to the end - if (now > nextTimeout.time) { - nextTimeout.run() - } else { - SVG.Draw.timeouts.push(nextTimeout) - } - - // If we hit the last item, we should stop shifting out more items - if (nextTimeout === lastTimeout) break - } - - // Run all of the frames available up until this point - var lastFrame = SVG.Draw.frames.last() - var lastFrameId = SVG.Draw.frameCount - while (SVG.Draw.frames.first() && SVG.Draw.frames.first().id < lastFrameId) { - var nextFrame = SVG.Draw.frames.shift() - nextFrame.run(now) - } - - // If we have remaining timeouts or frames, draw until we don't anymore - SVG.Draw.nextDraw = SVG.Draw.timeouts.length > 0 || SVG.Draw.frames.length > 0 - ? requestAnimationFrame(SVG.Draw._draw) - : null - } -} - /* globals fullHex, compToHex */ // Module for color convertions @@ -1152,7 +1076,6 @@ SVG.Number = SVG.invent({ .times(pos) .plus(this) } - } }) @@ -1491,912 +1414,6 @@ SVG.Element = SVG.invent({ } }) -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) - } -} - -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 - } -}) - /* global abcdef, arrayToMatrix, closeEnough */ SVG.Matrix = SVG.invent({ @@ -2409,7 +1426,11 @@ SVG.Matrix = SVG.invent({ source = source instanceof SVG.Element ? source.matrixify() : typeof source === 'string' ? arrayToMatrix(source.split(SVG.regex.delimiter).map(parseFloat)) : Array.isArray(source) ? arrayToMatrix(source) - : typeof source === 'object' ? source + : (typeof source === 'object' && ( + source.a != null || source.b != null || source.c != null + || source.d != null || source.e != null || source.f != null + )) ? source + : (typeof source === 'object') ? new SVG.Matrix().transform(source) : arguments.length === 6 ? arrayToMatrix([].slice.call(arguments)) : base @@ -2438,59 +1459,28 @@ SVG.Matrix = SVG.invent({ return newMatrix } - // Get all of the parameters required to form the matrix - var flipX = o.flip && (o.flip === 'x' || o.flip === 'both') ? -1 : 1 - var flipY = o.flip && (o.flip === 'y' || o.flip === 'both') ? -1 : 1 - var skewX = o.skew && o.skew.length ? o.skew[0] - : isFinite(o.skew) ? o.skew - : isFinite(o.skewX) ? o.skewX - : 0 - var skewY = o.skew && o.skew.length ? o.skew[1] - : isFinite(o.skew) ? o.skew - : isFinite(o.skewY) ? o.skewY - : 0 - var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX - : isFinite(o.scale) ? o.scale * flipX - : isFinite(o.scaleX) ? o.scaleX * flipX - : flipX - var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY - : isFinite(o.scale) ? o.scale * flipY - : isFinite(o.scaleY) ? o.scaleY * flipY - : flipY - var shear = o.shear || 0 - var theta = o.rotate || 0 - var origin = new SVG.Point(o.ox == null ? o.origin : o.ox, o.oy) - var ox = origin.x - var oy = origin.y - var position = new SVG.Point(o.px == null - ? o.position : o.px, o.py, {x: null, y: null}) - var px = position.x - var py = position.y - var translate = new SVG.Point(o.tx == null ? o.translate : o.tx, o.ty) - var tx = translate.x - var ty = translate.y - var relative = new SVG.Point(o.rx == null ? o.relative : o.rx, o.ry) - var rx = relative.x - var ry = relative.y + // Get the proposed transformations and the current transformations + var t = formatTransforms(o) var currentTransform = new SVG.Matrix(this) // Construct the resulting matrix var transformer = new SVG.Matrix() - .translate(-ox, -oy) - .scale(scaleX, scaleY) - .skew(skewX, skewY) - .shear(shear) - .rotate(theta) - .translate(ox, oy) - .translate(rx, ry) + .translate(-t.ox, -t.oy) + .scale(t.scaleX, t.scaleY) + .skew(t.skewX, t.skewY) + .shear(t.shear) + .rotate(t.theta) + .translate(t.ox, t.oy) + .translate(t.rx, t.ry) .lmultiply(currentTransform) // If we want the origin at a particular place, we force it there - if (isFinite(px) || isFinite(py)) { + if (isFinite(t.px) || isFinite(t.py)) { + // Figure out where the origin went and the delta to get there - var current = new SVG.Point(ox - rx, oy - ry).transform(transformer) - var dx = px ? px - current.x : 0 - var dy = py ? py - current.y : 0 + var current = new SVG.Point(t.ox - t.rx, t.oy - t.ry).transform(transformer) + var dx = t.px ? t.px - current.x : 0 + var dy = t.py ? t.py - current.y : 0 // Apply another translation transformer = transformer.translate(dx, dy) @@ -2616,7 +1606,31 @@ SVG.Matrix = SVG.invent({ // Inverses matrix inverse: function () { - return new SVG.Matrix(this.native().inverse()) + + // Get the current parameters out of the matrix + var a = this.a + var b = this.b + var c = this.c + var d = this.d + var e = this.e + var f = this.f + + // Invert the 2x2 matrix in the top left + var det = a * d - b * c + if (!det) throw new Error("Cannot invert " + this) + + // Calculate the top 2x2 matrix + var na = d / det + var nb = -b / det + var nc = -c / det + var nd = a / det + + // Apply the inverted matrix to the top right + var ne = - ( na * e + nc * f ) + var nf = - ( nb * e + nd * f ) + + // Construct the inverted matrix + return new SVG.Matrix(na, nb, nc, nd, ne, nf) }, // Translate matrix @@ -5290,6 +4304,64 @@ function closeEnough (a, b, threshold) { return Math.abs(b - a) < (threshold || 1e-6) } +// TODO: Refactor this to a static function of matrix.js +function formatTransforms (o) { + + // Get all of the parameters required to form the matrix + var flipBoth = o.flip === 'both' || o.flip === true + var flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1 + var flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1 + var skewX = o.skew && o.skew.length ? o.skew[0] + : isFinite(o.skew) ? o.skew + : isFinite(o.skewX) ? o.skewX + : 0 + var skewY = o.skew && o.skew.length ? o.skew[1] + : isFinite(o.skew) ? o.skew + : isFinite(o.skewY) ? o.skewY + : 0 + var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX + : isFinite(o.scale) ? o.scale * flipX + : isFinite(o.scaleX) ? o.scaleX * flipX + : flipX + var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY + : isFinite(o.scale) ? o.scale * flipY + : isFinite(o.scaleY) ? o.scaleY * flipY + : flipY + var shear = o.shear || 0 + var theta = o.rotate || 0 + var origin = new SVG.Point(o.ox == null ? o.origin : o.ox, o.oy) + var ox = origin.x + var oy = origin.y + var position = new SVG.Point(o.px == null + ? o.position : o.px, o.py, {x: null, y: null}) + var px = position.x + var py = position.y + var translate = new SVG.Point(o.tx == null ? o.translate : o.tx, o.ty) + var tx = translate.x + var ty = translate.y + var relative = new SVG.Point(o.rx == null ? o.relative : o.rx, o.ry) + var rx = relative.x + var ry = relative.y + + // Populate all of the values + return { + scaleX: scaleX, + scaleY: scaleY, + skewX: skewX, + skewY: skewY, + shear: shear, + theta: theta, + rx: rx, + ry: ry, + tx: tx, + ty: ty, + ox: ox, + oy: oy, + px: px, + py: py, + } +} + /* globals fullBox, domContains, isNulledBox, Exception */ SVG.Box = SVG.invent({ @@ -5453,6 +4525,96 @@ SVG.parser.nodes = { SVG.parser.nodes.path = SVG.parser.nodes.svg.path().node +/* global requestAnimationFrame */ + +SVG.Animator = { + nextDraw: null, + frames: new SVG.Queue(), + timeouts: new SVG.Queue(), + frameCount: 0, + timeoutCount: 0, + timer: window.performance || window.Date, + + frame: function (fn) { + SVG.Animator.frames.push({ + id: SVG.Animator.frameCount, + run: fn + }) + + if (SVG.Animator.nextDraw === null) { + SVG.Animator.nextDraw = requestAnimationFrame(SVG.Animator._draw) + } + + return ++SVG.Animator.frameCount + }, + + timeout: function (fn, delay) { + delay = delay || 0 + + // Work out when the event should fire + var time = SVG.Animator.timer.now() + delay + + // Add the timeout to the end of the queue + var thisId = SVG.Animator.timeoutCount++ + SVG.Animator.timeouts.push({ + id: thisId, + run: fn, + time: time + }) + + // Request another animation frame if we need one + if (SVG.Animator.nextDraw === null) { + SVG.Animator.nextDraw = requestAnimationFrame(SVG.Animator._draw) + } + + return thisId + }, + + cancelTimeout: function (id) { + // Find the index of the timeout to cancel and remove it + var index = SVG.Animator.timeouts.remove( + function (t) { + return t.id === id + } + ) + return index + }, + + _draw: function (now) { + + // Run all the timeouts we can run, if they are not ready yet, add them + // to the end of the queue immediately! (bad timeouts!!! [sarcasm]) + var tracking = true + var nextTimeout = null + var lastTimeout = SVG.Animator.timeouts.last() + while ((nextTimeout = SVG.Animator.timeouts.shift())) { + + // Run the timeout if its time, or push it to the end + if (now > nextTimeout.time) { + nextTimeout.run() + } else { + SVG.Animator.timeouts.push(nextTimeout) + } + + // If we hit the last item, we should stop shifting out more items + if (nextTimeout === lastTimeout) break + } + + // Run all of the frames available up until this point + var lastFrame = SVG.Animator.frames.last() + var lastFrameId = SVG.Animator.frameCount + while (SVG.Animator.frames.first() && SVG.Animator.frames.first().id < lastFrameId) { + var nextFrame = SVG.Animator.frames.shift() + nextFrame.run(now) + } + + // If we have remaining timeouts or frames, draw until we don't anymore + SVG.Animator.nextDraw = SVG.Animator.timeouts.length > 0 || SVG.Animator.frames.length > 0 + ? requestAnimationFrame(SVG.Animator._draw) + : null + } +} + return SVG diff --git a/dist/svg.min.js b/dist/svg.min.js index c589c44..fdf7f1c 100644 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,2 +1,2 @@ -/*! svg.js v3.0.0 MIT*/;!function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t,t.document)}):"object"==typeof exports?module.exports=t.document?e(t,t.document):function(t){return e(t,t.document)}:t.SVG=e(t,t.document)}("undefined"!=typeof window?window:this,function(t,e){function i(t,i){if(t instanceof w.Element)return t;if("object"==typeof t)return w.adopt(t);if(null==t)return new w.Doc;if("string"==typeof t&&"<"!==t.charAt(0))return w.adopt(e.querySelector(t));var n=w.create("svg");return n.innerHTML=t,t=w.adopt(n.firstElementChild)}function n(t){return!(t.w||t.h||t.x||t.y)}function r(t){return(e.documentElement.contains||function(t){for(;t.parentNode;)t=t.parentNode;return t===e}).call(e.documentElement,t)}function s(t,e,i,n){return i+n.replace(w.regex.dots," .")}function o(t){for(var e=t.slice(0),i=e.length;i--;)Array.isArray(e[i])&&(e[i]=o(e[i]));return e}function a(t,e){return t instanceof e}function h(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}function u(t){return t.toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()})}function l(t){return t.charAt(0).toUpperCase()+t.slice(1)}function c(t){return 4===t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t}function f(t){var e=t.toString(16);return 1===e.length?"0"+e:e}function d(t,e,i){if(null==e||null==i){var n=t.bbox();null==e?e=n.width/n.height*i:null==i&&(i=n.height/n.width*e)}return{width:e,height:i}}function p(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function m(t){for(var e=0,i=t.length,n="";e=0;e--)v(t.children[e]);return t.id?w.adopt(t).id(w.eid(t.nodeName)):w.adopt(t)}function x(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function y(t){var e=(t||"").toString().match(w.regex.reference);if(e)return e[1]}function g(t,e,i){return Math.abs(e-t)<(i||1e-6)}if(!e.createElementNS||!e.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect)return{supported:!1};var w=this.SVG=function(t){if(w.supported)return t=i(t)};w.supported=!0,w.ns="http://www.w3.org/2000/svg",w.xmlns="http://www.w3.org/2000/xmlns/",w.xlink="http://www.w3.org/1999/xlink",w.svgjs="http://svgjs.com/svgjs",w.did=1e3,w.eid=function(t){return"Svgjs"+l(t)+w.did++},w.create=function(t){return e.createElementNS(this.ns,t)},w.extend=function(t,e){var i,n;for(t=Array.isArray(t)?t:[t],n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i]},w.invent=function(t){var e="function"==typeof t.create?t.create:function(e){this.constructor(e||w.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&w.extend(e,t.extend),t.construct&&w.extend(t.parent||w.Container,t.construct),e},w.adopt=function(e){if(!e)return null;if(e.instance instanceof w.Element)return e.instance;if(!(e instanceof t.SVGElement))return new w.HtmlNode(e);return"svg"===e.nodeName?new w.Doc(e):"linearGradient"===e.nodeName||"radialGradient"===e.nodeName?new w.Gradient(e):w[l(e.nodeName)]?new(w[l(e.nodeName)])(e):new w.Parent(e)},w.regex={numberAndUnit:/^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,transforms:/\)\s*,?\s*/,whitespace:/\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,isPercent:/^-?[\d.]+%$/,isImage:/\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,delimiter:/[\s,]+/,hyphen:/([^e])-/gi,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,numbersWithDots:/((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi,dots:/\./g},w.utils={map:function(t,e){var i,n=t.length,r=[];for(i=0;ie.time?e.run():w.Draw.timeouts.push(e),e!==i););for(var n=(w.Draw.frames.last(),w.Draw.frameCount);w.Draw.frames.first()&&w.Draw.frames.first().id0||w.Draw.frames.length>0?requestAnimationFrame(w.Draw._draw):null}},w.Color=function(t){var e;this.r=0,this.g=0,this.b=0,t&&("string"==typeof t?w.regex.isRgb.test(t)?(e=w.regex.rgb.exec(t.replace(w.regex.whitespace,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):w.regex.isHex.test(t)&&(e=w.regex.hex.exec(c(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b))},w.extend(w.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+f(this.r)+f(this.g)+f(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new w.Color(t),this},at:function(t){return this.destination?(t=t<0?0:t>1?1:t,new w.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),w.Color.test=function(t){return t+="",w.regex.isHex.test(t)||w.regex.isRgb.test(t)},w.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},w.Color.isColor=function(t){return w.Color.isRgb(t)||w.Color.test(t)},w.Array=function(t,e){t=(t||[]).valueOf(),0===t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},w.extend(w.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!==this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.length=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)n.width&&(this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x),n.height&&(this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.y);return this},bbox:function(){var t=-1/0,e=-1/0,i=1/0,n=1/0;return this.value.forEach(function(r){t=Math.max(r[0],t),e=Math.max(r[1],e),i=Math.min(r[0],i),n=Math.min(r[1],n)}),{x:i,y:n,width:t-i,height:e-n}}});for(var b={M:function(t,e,i){return e.x=i.x=t[0],e.y=i.y=t[1],["M",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],["L",t[0],t[1]]},H:function(t,e){return e.x=t[0],["H",t[0]]},V:function(t,e){return e.y=t[0],["V",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],["C",t[0],t[1],t[2],t[3],t[4],t[5]]},S:function(t,e){return e.x=t[2],e.y=t[3],["S",t[0],t[1],t[2],t[3]]},Q:function(t,e){return e.x=t[2],e.y=t[3],["Q",t[0],t[1],t[2],t[3]]},T:function(t,e){return e.x=t[0],e.y=t[1],["T",t[0],t[1]]},Z:function(t,e,i){return e.x=i.x,e.y=i.y,["Z"]},A:function(t,e){return e.x=t[5],e.y=t[6],["A",t[0],t[1],t[2],t[3],t[4],t[5],t[6]]}},P="mlhvqtcsaz".split(""),A=0,C=P.length;A=0;r--)n=this.value[r][0],"M"===n||"L"===n||"T"===n?(this.value[r][1]+=t,this.value[r][2]+=e):"H"===n?this.value[r][1]+=t:"V"===n?this.value[r][1]+=e:"C"===n||"S"===n||"Q"===n?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"===n&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"===n&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var i,n,r=this.bbox();for(i=this.value.length-1;i>=0;i--)n=this.value[i][0],"M"===n||"L"===n||"T"===n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y):"H"===n?this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x:"V"===n?this.value[i][1]=(this.value[i][1]-r.y)*e/r.height+r.y:"C"===n||"S"===n||"Q"===n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y,this.value[i][3]=(this.value[i][3]-r.x)*t/r.width+r.x,this.value[i][4]=(this.value[i][4]-r.y)*e/r.height+r.y,"C"===n&&(this.value[i][5]=(this.value[i][5]-r.x)*t/r.width+r.x,this.value[i][6]=(this.value[i][6]-r.y)*e/r.height+r.y)):"A"===n&&(this.value[i][1]=this.value[i][1]*t/r.width,this.value[i][2]=this.value[i][2]*e/r.height,this.value[i][6]=(this.value[i][6]-r.x)*t/r.width+r.x,this.value[i][7]=(this.value[i][7]-r.y)*e/r.height+r.y);return this},equalCommands:function(t){var e,i,n;for(t=new w.PathArray(t),n=this.value.length===t.value.length,e=0,i=this.value.length;n&&ea);return n},bbox:function(){return w.parser().path.setAttribute("d",this.toString()),w.parser.nodes.path.getBBox()}}),w.Number=w.invent({create:function(t,e){this.value=0,this.unit=e||"","number"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-3.4e38:3.4e38:"string"==typeof t?(e=t.match(w.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),"%"===e[5]?this.value/=100:"s"===e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof w.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return("%"===this.unit?~~(1e8*this.value)/1e6:"s"===this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new w.Number(t),new w.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new w.Number(t),new w.Number(this-t,this.unit||t.unit)},times:function(t){return t=new w.Number(t),new w.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new w.Number(t),new w.Number(this/t,this.unit||t.unit)},to:function(t){var e=new w.Number(this);return"string"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new w.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new w.Number(this.destination).minus(this).times(t).plus(this):this}}}),w.HtmlNode=w.invent({create:function(t){this.node=t},extend:{add:function(t,e){return t=i(t),t.node!==this.node.children[e]&&this.node.insertBefore(t.node,this.node.children[e]||null),this},put:function(t,e){return this.add(t,e),t}}}),w.Element=w.invent({create:function(t){this.events={},this.dom={},this.node=t,this.node&&(this.type=t.nodeName,this.node.instance=this,this.events=t.events||{},t.hasAttribute("svgjs:data")&&this.setData(JSON.parse(t.getAttribute("svgjs:data"))||{}))},extend:{x:function(t){return this.attr("x",t)},y:function(t){return this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=d(this,t,e);return this.width(new w.Number(i.width)).height(new w.Number(i.height))},clone:function(t){this.writeDataToDom();var e=v(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return i(t).put(this)},putIn:function(t){return i(t).add(this)},id:function(t){return void 0!==t||this.node.id||(this.node.id=w.eid(this.type)),this.attr("id",t)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&t":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return 1-Math.cos(t*Math.PI/2)}},w.morph=function(t){return function(e,i){return new w.MorphObj(e,i).at(t)}},w.Situation=w.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new w.Number(t.duration).valueOf(),this.delay=new w.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),w.Timeline=w.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(t,e,i){"object"==typeof t&&(e=t.ease,i=t.delay,t=t.duration);var n=new w.Situation({duration:t||1e3,delay:i||0,ease:w.easing[e||"-"]||e});return this.queue(n),this},delay:function(t){var e=new w.Situation({duration:t,delay:0,ease:w.easing["-"]});return this.queue(e)},target:function(t){return t&&t instanceof w.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=t.requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){t.cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return("function"==typeof t||t instanceof w.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof w.Situation?this.start():this.situation(this)),this},initAnimations:function(){var t,e,i,n=this.situation;if(n.init)return this;for(t in n.animations)for(i=this.target()[t](),Array.isArray(i)||(i=[i]),Array.isArray(n.animations[t])||(n.animations[t]=[n.animations[t]]),e=i.length;e--;)n.animations[t][e]instanceof w.Number&&(i[e]=new w.Number(i[e])),n.animations[t][e]=i[e].morph(n.animations[t][e]);for(t in n.attrs)n.attrs[t]=new w.MorphObj(this.target().attr(t),n.attrs[t]);for(t in n.styles)n.styles[t]=new w.MorphObj(this.target().css(t),n.styles[t]);return n.initialTransformation=this.target().matrixify(),n.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){var i=this.active;return this.active=!1,e&&this.clearQueue(),t&&this.situation&&(!i&&this.startCurrent(),this.atEnd()),this.stopAnimFrame(),this.clearCurrent()},reset:function(){if(this.situation){var t=this.situation;this.stop(),this.situation=t,this.atStart()}return this},finish:function(){for(this.stop(!0,!1);this.dequeue().situation&&this.stop(!0,!1););return this.clearQueue().clearCurrent(),this},atStart:function(){return this.at(0,!0)},atEnd:function(){return!0===this.situation.loops&&(this.situation.loops=this.situation.loop+1),"number"==typeof this.situation.loops?this.at(this.situation.loops,!0):this.at(1,!0)},at:function(t,e){var i=this.situation.duration/this._speed;return this.absPos=t,e||(this.situation.reversed&&(this.absPos=1-this.absPos),this.absPos+=this.situation.loop),this.situation.start=+new Date-this.absPos*i,this.situation.finish=this.situation.start+i,this.step(!0)},speed:function(t){return 0===t?this.pause():t?(this._speed=t,this.at(this.absPos,!0)):this._speed},loop:function(t,e){var i=this.last();return i.loops=null==t||t,i.loop=0,e&&(i.reversing=!0),this},pause:function(){return this.paused=!0,this.stopAnimFrame(),this},play:function(){return this.paused?(this.paused=!1,this.at(this.absPos,!0)):this},reverse:function(t){var e=this.last();return e.reversed=void 0===t?!e.reversed:t,this},progress:function(t){return t?this.situation.ease(this.pos):this.pos},after:function(t){function e(n){n.detail.situation===i&&(t.call(this,i),this.off("finished.fx",e))}var i=this.last();return this.target().on("finished.fx",e),this._callStart()},during:function(t){function e(e){e.detail.situation===i&&t.call(this,e.detail.pos,w.morph(e.detail.pos),e.detail.eased,i)}var i=this.last();return this.target().off("during.fx",e).on("during.fx",e),this.after(function(){this.off("during.fx",e)}),this._callStart()},afterAll:function(t){var e=function e(i){t.call(this),this.off("allfinished.fx",e)};return this.target().off("allfinished.fx",e).on("allfinished.fx",e),this._callStart()},duringAll:function(t){var e=function(e){t.call(this,e.detail.pos,w.morph(e.detail.pos),e.detail.eased,e.detail.situation)};return this.target().off("during.fx",e).on("during.fx",e),this.afterAll(function(){this.off("during.fx",e)}),this._callStart()},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||"animations"][t]=e,this._callStart()},step:function(t){if(t||(this.absPos=this.timeToAbsPos(+new Date)),!1!==this.situation.loops){var e,i,n;e=Math.max(this.absPos,0),i=Math.floor(e),!0===this.situation.loops||ithis.lastPos&&s<=r&&(this.situation.once[s].call(this.target(),this.pos,r),delete this.situation.once[s]);return this.active&&this.target().fire("during",{pos:this.pos,eased:r,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1===this.pos&&!this.situation.reversed||this.situation.reversed&&0===this.pos?(this.stopAnimFrame(),this.target().fire("finished",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire("allfinished"),this.situations.length||(this.target().off(".fx"),this.active=!1)),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=r,this):this},eachAt:function(){var t,e,i=this,n=this.target(),r=this.situation;for(t in r.animations)e=[].concat(r.animations[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(r.ease(i.pos),i.pos):t}),n[t].apply(n,e);for(t in r.attrs)e=[t].concat(r.attrs[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(r.ease(i.pos),i.pos):t}),n.attr.apply(n,e);for(t in r.styles)e=[t].concat(r.styles[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(r.ease(i.pos),i.pos):t}),n.css.apply(n,e);return r.transforms.length,this},once:function(t,e,i){var n=this.last();return i||(t=n.ease(t)),n.once[t]=e,this},_callStart:function(){return setTimeout(function(){this.start()}.bind(this),0),this}},parent:w.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new w.Timeline(this))).animate(t,e,i)},delay:function(t){return(this.fx||(this.fx=new w.Timeline(this))).delay(t)},stop:function(t,e){return this.fx&&this.fx.stop(t,e),this},finish:function(){return this.fx&&this.fx.finish(),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this},speed:function(t){if(this.fx){if(null==t)return this.fx.speed();this.fx.speed(t)}return this}}}),w.MorphObj=w.invent({create:function(t,e){return w.Color.isColor(e)?new w.Color(t).morph(e):w.regex.delimiter.test(t)?new w.Array(t).morph(e):w.regex.numberAndUnit.test(e)?new w.Number(t).morph(e):(this.value=t,void(this.destination=e))},extend:{at:function(t,e){return e<1?this.value:this.destination},valueOf:function(){return this.value}}}),w.extend(w.Timeline,{attr:function(t,e,i){if("object"==typeof t)for(var n in t)this.attr(n,t[n]);else this.add(t,e,"attrs");return this},css:function(t,e){if("object"==typeof t)for(var i in t)this.css(i,t[i]);else this.add(t,e,"styles");return this},x:function(t,e){if(this.target()instanceof w.G)return this.transform({x:t},e),this;var i=new w.Number(t);return i.relative=e,this.add("x",i)},y:function(t,e){if(this.target()instanceof w.G)return this.transform({y:t},e),this;var i=new w.Number(t);return i.relative=e,this.add("y",i)},cx:function(t){return this.add("cx",new w.Number(t))},cy:function(t){return this.add("cy",new w.Number(t))},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target()instanceof w.Text)this.attr("font-size",t);else{var i;t&&e||(i=this.target().bbox()),t||(t=i.width/i.height*e),e||(e=i.height/i.width*t),this.add("width",new w.Number(t)).add("height",new w.Number(e))}return this},width:function(t){return this.add("width",new w.Number(t))},height:function(t){return this.add("height",new w.Number(t))},plot:function(t,e,i,n){return 4===arguments.length?this.plot([t,e,i,n]):this.add("plot",new(this.target().MorphArray)(t))},leading:function(t){return this.target().leading?this.add("leading",new w.Number(t)):this},viewbox:function(t,e,i,n){return this.target()instanceof w.Container&&this.add("viewbox",new w.Box(t,e,i,n)),this},update:function(t){if(this.target()instanceof w.Stop){if("number"==typeof t||t instanceof w.Number)return this.update({offset:arguments[0],color:arguments[1],opacity:arguments[2]});null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",t.offset)}return this}}),w.Matrix=w.invent({create:function(t){var e,i=p([1,0,0,1,0,0]);for(t=t instanceof w.Element?t.matrixify():"string"==typeof t?p(t.split(w.regex.delimiter).map(parseFloat)):Array.isArray(t)?p(t):"object"==typeof t?t:6===arguments.length?p([].slice.call(arguments)):i,e=N.length-1;e>=0;--e)this[N[e]]=null!=t[N[e]]?t[N[e]]:i[N[e]]},extend:{clone:function(){return new w.Matrix(this)},transform:function(t){if(null!=t.a){var e=new w.Matrix(t);return this.lmultiply(e)}var i=!t.flip||"x"!==t.flip&&"both"!==t.flip?1:-1,n=!t.flip||"y"!==t.flip&&"both"!==t.flip?1:-1,r=t.skew&&t.skew.length?t.skew[0]:isFinite(t.skew)?t.skew:isFinite(t.skewX)?t.skewX:0,s=t.skew&&t.skew.length?t.skew[1]:isFinite(t.skew)?t.skew:isFinite(t.skewY)?t.skewY:0,o=t.scale&&t.scale.length?t.scale[0]*i:isFinite(t.scale)?t.scale*i:isFinite(t.scaleX)?t.scaleX*i:i,a=t.scale&&t.scale.length?t.scale[1]*n:isFinite(t.scale)?t.scale*n:isFinite(t.scaleY)?t.scaleY*n:n,h=t.shear||0,u=t.rotate||0,l=new w.Point(null==t.ox?t.origin:t.ox,t.oy),c=l.x,f=l.y,d=new w.Point(null==t.px?t.position:t.px,t.py,{x:null,y:null}),p=d.x,m=d.y,v=new w.Point(null==t.tx?t.translate:t.tx,t.ty),x=v.x,y=v.y,g=new w.Point(null==t.rx?t.relative:t.rx,t.ry),b=g.x,P=g.y,A=new w.Matrix(this),C=(new w.Matrix).translate(-c,-f).scale(o,a).skew(r,s).shear(h).rotate(u).translate(c,f).translate(b,P).lmultiply(A);if(isFinite(p)||isFinite(m)){var N=new w.Point(c-b,f-P).transform(C),M=p?p-N.x:0,S=m?m-N.y:0;C=C.translate(M,S)}return C=C.translate(x,y)},compose:function(t){var e=t.scaleX||1,i=t.scaleY||1,n=t.shear||0,r=t.rotate||0,s=t.translateX||0,o=t.translateY||0;return(new w.Matrix).scale(e,i).shear(n).rotate(r).translate(s,o).lmultiply(this)},decompose:function(){var t=this.a,e=this.b,i=this.c,n=this.d,r=this.e,s=this.f,o=t*n-e*i,a=o>0?1:-1,h=a*Math.sqrt(t*t+e*e),u=180/Math.PI*Math.atan2(a*e,a*t),l=(t*i+e*n)/o;return{scaleX:h,scaleY:i*h/(l*t-e)||n*h/(l*e+t),shear:l,rotate:u,translateX:r,translateY:s,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f}},morph:function(t){return this.destination=new w.Matrix(t),this},at:function(t){return this.destination?new w.Matrix({a:this.a+(this.destination.a-this.a)*t,b:this.b+(this.destination.b-this.b)*t,c:this.c+(this.destination.c-this.c)*t,d:this.d+(this.destination.d-this.d)*t,e:this.e+(this.destination.e-this.e)*t,f:this.f+(this.destination.f-this.f)*t}):this},multiply:function(t){var e=this,i=new w.Matrix(t),n=e.a*i.a+e.c*i.b,r=e.b*i.a+e.d*i.b,s=e.a*i.c+e.c*i.d,o=e.b*i.c+e.d*i.d,a=e.e+e.a*i.e+e.c*i.f,h=e.f+e.b*i.e+e.d*i.f;return new w.Matrix(n,r,s,o,a,h)},lmultiply:function(t){return new w.Matrix(t).multiply(this)},inverse:function(){return new w.Matrix(this.native().inverse())},translate:function(t,e){var i=new w.Matrix(this);return i.e+=t||0,i.f+=e||0,i},scale:function(t,e,i,n){1===arguments.length?e=t:3===arguments.length&&(n=i,i=e,e=t);var r=new w.Matrix(t,0,0,e,0,0);return this.around(i,n,r)},rotate:function(t,e,i){t=w.utils.radians(t);var n=new w.Matrix(Math.cos(t),Math.sin(t),-Math.sin(t),Math.cos(t),0,0);return this.around(e,i,n)},flip:function(t,e){return"x"===t?this.scale(-1,1,e,0):"y"===t?this.scale(1,-1,0,e):this.scale(-1,-1,t,e||t)},shear:function(t,e,i){var n=new w.Matrix(1,0,t,1,0,0);return this.around(e,i,n)},skew:function(t,e,i,n){1===arguments.length?e=t:3===arguments.length&&(n=i,i=e,e=t),t=w.utils.radians(t),e=w.utils.radians(e) -;var r=new w.Matrix(1,Math.tan(e),Math.tan(t),1,0,0);return this.around(i,n,r)},skewX:function(t,e,i){return this.skew(t,0,e,i)},skewY:function(t,e,i){return this.skew(0,t,e,i)},around:function(t,e,i){var n=t||0,r=e||0;return this.translate(-n,-r).lmultiply(i).translate(n,r)},native:function(){for(var t=w.parser.nodes.svg.node.createSVGMatrix(),e=N.length-1;e>=0;e--)t[N[e]]=this[N[e]];return t},equals:function(t){var e=new w.Matrix(t);return g(this.a,e.a)&&g(this.b,e.b)&&g(this.c,e.c)&&g(this.d,e.d)&&g(this.e,e.e)&&g(this.f,e.f)},toString:function(){return"matrix("+this.a+","+this.b+","+this.c+","+this.d+","+this.e+","+this.f+")"}},parent:w.Element,construct:{ctm:function(){return new w.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof w.Doc&&!this.isRoot()){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new w.Matrix(e)}return new w.Matrix(this.node.getScreenCTM())}}}),w.Point=w.invent({create:function(t,e,i){var n;i=i||{x:0,y:0},n=Array.isArray(t)?{x:t[0],y:t[1]}:"object"==typeof t?{x:t.x,y:t.y}:{x:t,y:e},this.x=null==n.x?i.x:n.x,this.y=null==n.y?i.y:n.y},extend:{clone:function(){return new w.Point(this)},morph:function(t,e){return this.destination=new w.Point(t,e),this},at:function(t){return this.destination?new w.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t}):this},native:function(){var t=w.parser.nodes.svg.node.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new w.Point(this.native().matrixTransform(t.native()))}}}),w.extend(w.Element,{point:function(t,e){return new w.Point(t,e).transform(this.screenCTM().inverse())}}),w.extend(w.Element,{attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=w.regex.isNumber.test(e[i].nodeValue)?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return e=this.node.getAttribute(t),null==e?w.defaults.attrs[t]:w.regex.isNumber.test(e)?parseFloat(e):e;"fill"!==t&&"stroke"!==t||(w.regex.isImage.test(e)&&(e=this.doc().defs().image(e)),e instanceof w.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new w.Number(e):w.Color.isColor(e)?e=new w.Color(e):Array.isArray(e)&&(e=new w.Array(e)),"leading"===t?this.leading&&this.leading(e):"string"==typeof i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!==t&&"x"!==t||this.rebuild(t,e)}return this}}),w.extend(w.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){return(this.attr("transform")||"").split(w.regex.transforms).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(w.regex.delimiter).map(function(t){return parseFloat(t)})]}).reverse().reduce(function(t,e){return"matrix"===e[0]?t.lmultiply(p(e[1])):t[e[0]].apply(t,e[1])},new w.Matrix)},toParent:function(t){if(this===t)return this;var e=this.screenCTM(),i=t.screenCTM().inverse();return this.addTo(t).untransform().transform(i.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),w.extend(w.Element,{transform:function(t,e){var i=this.bbox();if(null==t||"string"==typeof t){var n=new w.Matrix(this).decompose();return n[t]||n}if("string"==typeof t.origin||null==t.origin&&null==t.ox&&null==t.oy){var r="string"==typeof t.origin?t.origin.toLowerCase().trim():"center",s=i.height,o=i.width,a=i.x,h=i.y;t.ox=r.includes("left")?a:r.includes("right")?a+o:a+o/2,t.oy=r.includes("top")?h:r.includes("bottom")?h+s:h+s/2,t.origin=null}var u=!0===e?this:e||!1,l=new w.Matrix(u).transform(t);return this.attr("transform",l)}}),w.extend(w.Timeline,{transform:function(t,e,i){}}),w.extend(w.Element,{css:function(t,e){var i,n,r={};if(0===arguments.length)return this.node.style.cssText.split(/\s*;\s*/).filter(function(t){return!!t.length}).forEach(function(t){i=t.split(/\s*:\s*/),r[i[0]]=i[1]}),r;if(arguments.length<2){if(Array.isArray(t)){for(n=t.length;n--;)r[u(t[n])]=this.node.style[u(t[n])];return r}if("string"==typeof t)return this.node.style[u(t)];if("object"==typeof t)for(n in t)this.node.style[u(n)]=null==t[n]||w.regex.isBlank.test(t[n])?"":t[n]}return 2===arguments.length&&(this.node.style[u(t)]=null==e||w.regex.isBlank.test(e)?"":e),this}}),w.Parent=w.invent({create:function(t){this.constructor(t)},inherit:w.Element,extend:{children:function(){return w.utils.map(this.node.children,function(t){return w.adopt(t)})},add:function(t,e){return t=i(t),t.node!==this.node.children[e]&&this.node.insertBefore(t.node,this.node.children[e]||null),this},put:function(t,e){return this.add(t,e),t.instance||t},has:function(t){return this.index(t)>=0},index:function(t){return[].slice.call(this.node.children).indexOf(t.node)},get:function(t){return w.adopt(this.node.children[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.children.length-1)},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;i0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof w.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent().add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent().add(t,e+1),this}}),w.Mask=w.invent({create:"mask",inherit:w.Container,extend:{remove:function(){return this.targets().forEach(function(t){t.unmask()}),w.Element.prototype.remove.call(this)},targets:function(){return w.select('svg [mask*="'+this.id()+'"]')}},construct:{mask:function(){return this.defs().put(new w.Mask)}}}),w.extend(w.Element,{maskWith:function(t){var e=t instanceof w.Mask?t:this.parent().mask().add(t);return this.attr("mask",'url("#'+e.id()+'")')},unmask:function(){return this.attr("mask",null)},masker:function(){return this.reference("mask")}}),w.ClipPath=w.invent({create:"clipPath",inherit:w.Container,extend:{remove:function(){return this.targets().forEach(function(t){t.unclip()}),w.Element.prototype.remove.call(this)},targets:function(){return w.select('svg [clip-path*="'+this.id()+'"]')}},construct:{clip:function(){return this.defs().put(new w.ClipPath)}}}),w.extend(w.Element,{clipWith:function(t){var e=t instanceof w.ClipPath?t:this.parent().clip().add(t);return this.attr("clip-path",'url("#'+e.id()+'")')},unclip:function(){return this.attr("clip-path",null)},clipper:function(){return this.reference("clip-path")}}),w.Gradient=w.invent({create:function(t){this.constructor("object"==typeof t?t:w.create(t+"Gradient"))},inherit:w.Container,extend:{stop:function(t,e,i){return this.put(new w.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},url:function(){return"url(#"+this.id()+")"},toString:function(){return this.url()},attr:function(t,e,i){return"transform"===t&&(t="gradientTransform"),w.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),w.extend([w.Gradient,w.Timeline],{from:function(t,e){return"radialGradient"===(this._target||this).type?this.attr({fx:new w.Number(t),fy:new w.Number(e)}):this.attr({x1:new w.Number(t),y1:new w.Number(e)})},to:function(t,e){return"radialGradient"===(this._target||this).type?this.attr({cx:new w.Number(t),cy:new w.Number(e)}):this.attr({x2:new w.Number(t),y2:new w.Number(e)})}}),w.extend(w.Defs,{gradient:function(t,e){return this.put(new w.Gradient(t)).update(e)}}),w.Stop=w.invent({create:"stop",inherit:w.Element,extend:{update:function(t){return("number"==typeof t||t instanceof w.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new w.Number(t.offset)),this}}}),w.Pattern=w.invent({create:"pattern",inherit:w.Container,extend:{url:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.url()},attr:function(t,e,i){return"transform"===t&&(t="patternTransform"),w.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),w.extend(w.Defs,{pattern:function(t,e,i){return this.put(new w.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),w.Doc=w.invent({create:function(t){this.constructor(t||w.create("svg")),this.namespace()},inherit:w.Container,extend:{isRoot:function(){return!(this.node.parentNode&&this.node.parentNode instanceof t.SVGElement&&"#document"!==this.node.parentNode.nodeName)},doc:function(){return this.isRoot()?this:w.Element.prototype.doc.call(this)},namespace:function(){return this.isRoot()?this.attr({xmlns:w.ns,version:"1.1"}).attr("xmlns:xlink",w.xlink,w.xmlns).attr("xmlns:svgjs",w.svgjs,w.xmlns):this.doc().namespace()},defs:function(){return this.isRoot()?w.adopt(this.node.getElementsByTagName("defs")[0])||this.put(new w.Defs):this.doc().defs()},parent:function(t){return this.isRoot()?"#document"===this.node.parentNode.nodeName?null:this.node.parentNode:w.Element.prototype.parent.call(this,t)},remove:function(){return this.isRoot()?(this.parent()&&this.parent().removeChild(this.node),this):w.Element.prototype.remove.call(this)},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this}},construct:{nested:function(){return this.put(new w.Doc)}}}),w.Shape=w.invent({create:function(t){this.constructor(t)},inherit:w.Element}),w.Bare=w.invent({create:function(t,e){if(this.constructor(w.create(t)),e)for(var i in e.prototype)"function"==typeof e.prototype[i]&&(this[i]=e.prototype[i])},inherit:w.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),w.extend(w.Parent,{element:function(t,e){return this.put(new w.Bare(t,e))}}),w.Symbol=w.invent({create:"symbol",inherit:w.Container,construct:{symbol:function(){return this.put(new w.Symbol)}}}),w.Use=w.invent({create:"use",inherit:w.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,w.xlink)}},construct:{use:function(t,e){return this.put(new w.Use).element(t,e)}}}),w.Rect=w.invent({create:"rect",inherit:w.Shape,construct:{rect:function(t,e){return this.put(new w.Rect).size(t,e)}}}),w.Circle=w.invent({create:"circle",inherit:w.Shape,construct:{circle:function(t){return this.put(new w.Circle).rx(new w.Number(t).divide(2)).move(0,0)}}}),w.extend([w.Circle,w.Timeline],{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),w.Ellipse=w.invent({create:"ellipse",inherit:w.Shape,construct:{ellipse:function(t,e){return this.put(new w.Ellipse).size(t,e).move(0,0)}}}),w.extend([w.Ellipse,w.Rect,w.Timeline],{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),w.extend([w.Circle,w.Ellipse],{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t)},width:function(t){return null==t?2*this.rx():this.rx(new w.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new w.Number(t).divide(2))},size:function(t,e){var i=d(this,t,e);return this.rx(new w.Number(i.width).divide(2)).ry(new w.Number(i.height).divide(2))}}),w.Line=w.invent({create:"line",inherit:w.Shape,extend:{array:function(){return new w.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:i,y2:n}:new w.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=d(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return w.Line.prototype.plot.apply(this.put(new w.Line),null!=t?[t,e,i,n]:[0,0,0,0])}}}),w.Polyline=w.invent({create:"polyline",inherit:w.Shape,construct:{polyline:function(t){return this.put(new w.Polyline).plot(t||new w.PointArray)}}}),w.Polygon=w.invent({create:"polygon",inherit:w.Shape,construct:{polygon:function(t){return this.put(new w.Polygon).plot(t||new w.PointArray)}}}),w.extend([w.Polyline,w.Polygon],{array:function(){return this._array||(this._array=new w.PointArray(this.attr("points")))},plot:function(t){return null==t?this.array():this.clear().attr("points","string"==typeof t?t:this._array=new w.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=d(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),w.extend([w.Line,w.Polyline,w.Polygon],{MorphArray:w.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),w.Path=w.invent({create:"path",inherit:w.Shape,extend:{MorphArray:w.PathArray,array:function(){return this._array||(this._array=new w.PathArray(this.attr("d")))},plot:function(t){return null==t?this.array():this.clear().attr("d","string"==typeof t?t:this._array=new w.PathArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var i=d(this,t,e);return this.attr("d",this.array().size(i.width,i.height))},width:function(t){return null==t?this.bbox().width:this.size(t,this.bbox().height)},height:function(t){return null==t?this.bbox().height:this.size(this.bbox().width,t)}},construct:{path:function(t){return this.put(new w.Path).plot(t||new w.PathArray)}}}),w.Image=w.invent({create:"image",inherit:w.Shape,extend:{load:function(e,i){if(!e)return this;var n=new t.Image;return w.on(n,"load",function(t){var r=this.parent(w.Pattern);0===this.width()&&0===this.height()&&this.size(n.width,n.height),r instanceof w.Pattern&&0===r.width()&&0===r.height()&&r.size(this.width(),this.height()),"function"==typeof i&&i.call(this,{width:n.width,height:n.height,ratio:n.width/n.height,url:e})},this),w.on(n,"load error",function(){w.off(n)}),this.attr("href",n.src=e,w.xlink)}},construct:{image:function(t,e){return this.put(new w.Image).size(0,0).load(t,e)}}}),w.Text=w.invent({create:function(t){this.constructor(t||w.create("text")),this.dom.leading=new w.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",w.defaults.attrs["font-family"])},inherit:w.Parent,extend:{x:function(t){return null==t?this.attr("x"):this.attr("x",t)},y:function(t){var e=this.attr("y"),i="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-i:e:this.attr("y","number"==typeof t?t+i:t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},text:function(t){if(void 0===t){var e=this.node.childNodes,i=0;t="";for(var n=0,r=e.length;n=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),w.get=function(t){var i=e.getElementById(y(t)||t);return w.adopt(i)},w.select=function(t,i){return w.utils.map((i||e).querySelectorAll(t),function(t){return w.adopt(t)})},w.$$=function(t,i){return w.utils.map((i||e).querySelectorAll(t),function(t){return w.adopt(t)})},w.$=function(t,i){return w.adopt((i||e).querySelector(t))},w.extend(w.Parent,{select:function(t){return w.select(t,this.node)}});var N="abcdef".split("");return w.Box=w.invent({create:function(t){var e=[0,0,0,0];t="string"==typeof t?t.split(w.regex.delimiter).map(parseFloat):Array.isArray(t)?t:"object"==typeof t?[null!=t.left?t.left:t.x,null!=t.top?t.top:t.y,t.width,t.height]:4===arguments.length?[].slice.call(arguments):e,this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3],x(this)},extend:{merge:function(t){var e=Math.min(this.x,t.x),i=Math.min(this.y,t.y);return new w.Box(e,i,Math.max(this.x+this.width,t.x+t.width)-e,Math.max(this.y+this.height,t.y+t.height)-i)},transform:function(t){var e=1/0,i=-1/0,n=1/0,r=-1/0;return[new w.Point(this.x,this.y),new w.Point(this.x2,this.y),new w.Point(this.x,this.y2),new w.Point(this.x2,this.y2)].forEach(function(s){s=s.transform(t),e=Math.min(e,s.x),i=Math.max(i,s.x),n=Math.min(n,s.y),r=Math.max(r,s.y)}),new w.Box(e,n,i-e,r-n)},addOffset:function(){return this.x+=t.pageXOffset,this.y+=t.pageYOffset,this},toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height},morph:function(t,e,i,n){return this.destination=new w.Box(t,e,i,n),this},at:function(t){return this.destination?new w.Box(this.x+(this.destination.x-this.x)*t,this.y+(this.destination.y-this.y)*t,this.width+(this.destination.width-this.width)*t,this.height+(this.destination.height-this.height)*t):this}},parent:w.Element,construct:{bbox:function(){var t;try{if(t=this.node.getBBox(),n(t)&&!r(this.node))throw new Exception("Element not in the dom")}catch(i){try{var e=this.clone(w.parser().svg).show();t=e.node.getBBox(),e.remove()}catch(t){console.warn("Getting a bounding box of this element is not possible")}}return new w.Box(t)},rbox:function(t){try{var e=new w.Box(this.node.getBoundingClientRect());return t?e.transform(t.screenCTM().inverse()):e.addOffset()}catch(t){return new w.Box}}}}),w.extend([w.Doc,w.Symbol,w.Image,w.Pattern,w.Marker,w.ForeignObject,w.View],{viewbox:function(t,e,i,n){return null==t?new w.Box(this.attr("viewBox")):this.attr("viewBox",new w.Box(t,e,i,n))}}),w.parser=function(){var t;return w.parser.nodes.svg.node.parentNode||(t=e.body||e.documentElement,w.parser.nodes.svg.addTo(t)),w.parser.nodes},w.parser.nodes={svg:w().size(2,0).css({opacity:0,position:"absolute",left:"-100%",top:"-100%",overflow:"hidden"})},w.parser.nodes.path=w.parser.nodes.svg.path().node,w}); \ No newline at end of file +/*! svg.js v3.0.0 MIT*/;!function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t,t.document)}):"object"==typeof exports?module.exports=t.document?e(t,t.document):function(t){return e(t,t.document)}:t.SVG=e(t,t.document)}("undefined"!=typeof window?window:this,function(t,e){function n(t,n){if(t instanceof b.Element)return t;if("object"==typeof t)return b.adopt(t);if(null==t)return new b.Doc;if("string"==typeof t&&"<"!==t.charAt(0))return b.adopt(e.querySelector(t));var i=b.create("svg");return i.innerHTML=t,t=b.adopt(i.firstElementChild)}function i(t){return!(t.w||t.h||t.x||t.y)}function r(t){return(e.documentElement.contains||function(t){for(;t.parentNode;)t=t.parentNode;return t===e}).call(e.documentElement,t)}function s(t,e,n,i){return n+i.replace(b.regex.dots," .")}function o(t){for(var e=t.slice(0),n=e.length;n--;)Array.isArray(e[n])&&(e[n]=o(e[n]));return e}function a(t,e){return t instanceof e}function h(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}function u(t){return t.toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()})}function l(t){return t.charAt(0).toUpperCase()+t.slice(1)}function c(t){return 4===t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t}function f(t){var e=t.toString(16);return 1===e.length?"0"+e:e}function d(t,e,n){if(null==e||null==n){var i=t.bbox();null==e?e=i.width/i.height*n:null==n&&(n=i.height/i.width*e)}return{width:e,height:n}}function p(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function m(t){for(var e=0,n=t.length,i="";e=0;e--)x(t.children[e]);return t.id?b.adopt(t).id(b.eid(t.nodeName)):b.adopt(t)}function v(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function y(t){var e=(t||"").toString().match(b.regex.reference);if(e)return e[1]}function g(t,e,n){return Math.abs(e-t)<(n||1e-6)}function w(t){var e="both"===t.flip||!0===t.flip,n=t.flip&&(e||"x"===t.flip)?-1:1,i=t.flip&&(e||"y"===t.flip)?-1:1,r=t.skew&&t.skew.length?t.skew[0]:isFinite(t.skew)?t.skew:isFinite(t.skewX)?t.skewX:0,s=t.skew&&t.skew.length?t.skew[1]:isFinite(t.skew)?t.skew:isFinite(t.skewY)?t.skewY:0,o=t.scale&&t.scale.length?t.scale[0]*n:isFinite(t.scale)?t.scale*n:isFinite(t.scaleX)?t.scaleX*n:n,a=t.scale&&t.scale.length?t.scale[1]*i:isFinite(t.scale)?t.scale*i:isFinite(t.scaleY)?t.scaleY*i:i,h=t.shear||0,u=t.rotate||0,l=new b.Point(null==t.ox?t.origin:t.ox,t.oy),c=l.x,f=l.y,d=new b.Point(null==t.px?t.position:t.px,t.py,{x:null,y:null}),p=d.x,m=d.y,x=new b.Point(null==t.tx?t.translate:t.tx,t.ty),v=x.x,y=x.y,g=new b.Point(null==t.rx?t.relative:t.rx,t.ry);return{scaleX:o,scaleY:a,skewX:r,skewY:s,shear:h,theta:u,rx:g.x,ry:g.y,tx:v,ty:y,ox:c,oy:f,px:p,py:m}}if(!e.createElementNS||!e.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect)return{supported:!1};var b=this.SVG=function(t){if(b.supported)return t=n(t)};b.supported=!0,b.ns="http://www.w3.org/2000/svg",b.xmlns="http://www.w3.org/2000/xmlns/",b.xlink="http://www.w3.org/1999/xlink",b.svgjs="http://svgjs.com/svgjs",b.did=1e3,b.eid=function(t){return"Svgjs"+l(t)+b.did++},b.create=function(t){return e.createElementNS(this.ns,t)},b.extend=function(t,e){var n,i;for(t=Array.isArray(t)?t:[t],i=t.length-1;i>=0;i--)if(t[i])for(n in e)t[i].prototype[n]=e[n]},b.invent=function(t){var e="function"==typeof t.create?t.create:function(e){this.constructor(e||b.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&b.extend(e,t.extend),t.construct&&b.extend(t.parent||b.Container,t.construct),e},b.adopt=function(e){if(!e)return null;if(e.instance instanceof b.Element)return e.instance;if(!(e instanceof t.SVGElement))return new b.HtmlNode(e);return"svg"===e.nodeName?new b.Doc(e):"linearGradient"===e.nodeName||"radialGradient"===e.nodeName?new b.Gradient(e):b[l(e.nodeName)]?new(b[l(e.nodeName)])(e):new b.Parent(e)},b.regex={numberAndUnit:/^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,transforms:/\)\s*,?\s*/,whitespace:/\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,isPercent:/^-?[\d.]+%$/,isImage:/\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,delimiter:/[\s,]+/,hyphen:/([^e])-/gi,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,numbersWithDots:/((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi,dots:/\./g},b.utils={map:function(t,e){var n,i=t.length,r=[];for(n=0;n",delay:0},attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"}},b.Queue=b.invent({create:function(){this._first=null,this._last=null,this.length=0,this.id=0},extend:{push:function(t){var e={id:this.id++,value:t};this._last?this._last=this._last.next=e:this._last=this._first=e,this.length++},shift:function(){if(0!=this.length){var t=this._first;return this._first=t.next,this._last=--this.length?this._last:null,t.value}},first:function(){return this._first&&this._first.value},last:function(){return this._last&&this._last.value},remove:function(t){for(var e=null,n=this._first;n&&!t(n);)e=n,n=n.next;if(n&&n===this._first&&(this._first=this._first.next),n&&n===this._last&&(this._last=e),n)return--this.length,e&&(e.next=n.next),n.item}}}),b.Color=function(t){var e;this.r=0,this.g=0,this.b=0,t&&("string"==typeof t?b.regex.isRgb.test(t)?(e=b.regex.rgb.exec(t.replace(b.regex.whitespace,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):b.regex.isHex.test(t)&&(e=b.regex.hex.exec(c(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b))},b.extend(b.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+f(this.r)+f(this.g)+f(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return this.r/255*.3+this.g/255*.59+this.b/255*.11},morph:function(t){return this.destination=new b.Color(t),this},at:function(t){return this.destination?(t=t<0?0:t>1?1:t,new b.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),b.Color.test=function(t){return t+="",b.regex.isHex.test(t)||b.regex.isRgb.test(t)},b.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},b.Color.isColor=function(t){return b.Color.isRgb(t)||b.Color.test(t)},b.Array=function(t,e){t=(t||[]).valueOf(),0===t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},b.extend(b.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!==this.destination.length){for(var e=this.value[this.value.length-1],n=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(n);for(;this.value.length=0;i--)this.value[i]=[this.value[i][0]+t,this.value[i][1]+e];return this},size:function(t,e){var n,i=this.bbox();for(n=this.value.length-1;n>=0;n--)i.width&&(this.value[n][0]=(this.value[n][0]-i.x)*t/i.width+i.x),i.height&&(this.value[n][1]=(this.value[n][1]-i.y)*e/i.height+i.y);return this},bbox:function(){var t=-1/0,e=-1/0,n=1/0,i=1/0;return this.value.forEach(function(r){t=Math.max(r[0],t),e=Math.max(r[1],e),n=Math.min(r[0],n),i=Math.min(r[1],i)}),{x:n,y:i,width:t-n,height:e-i}}});for(var A={M:function(t,e,n){return e.x=n.x=t[0],e.y=n.y=t[1],["M",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],["L",t[0],t[1]]},H:function(t,e){return e.x=t[0],["H",t[0]]},V:function(t,e){return e.y=t[0],["V",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],["C",t[0],t[1],t[2],t[3],t[4],t[5]]},S:function(t,e){return e.x=t[2],e.y=t[3],["S",t[0],t[1],t[2],t[3]]},Q:function(t,e){return e.x=t[2],e.y=t[3],["Q",t[0],t[1],t[2],t[3]]},T:function(t,e){return e.x=t[0],e.y=t[1],["T",t[0],t[1]]},Z:function(t,e,n){return e.x=n.x,e.y=n.y,["Z"]},A:function(t,e){return e.x=t[5],e.y=t[6],["A",t[0],t[1],t[2],t[3],t[4],t[5],t[6]]}},C="mlhvqtcsaz".split(""),P=0,N=C.length;P=0;r--)i=this.value[r][0],"M"===i||"L"===i||"T"===i?(this.value[r][1]+=t,this.value[r][2]+=e):"H"===i?this.value[r][1]+=t:"V"===i?this.value[r][1]+=e:"C"===i||"S"===i||"Q"===i?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"===i&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"===i&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var n,i,r=this.bbox();for(n=this.value.length-1;n>=0;n--)i=this.value[n][0],"M"===i||"L"===i||"T"===i?(this.value[n][1]=(this.value[n][1]-r.x)*t/r.width+r.x,this.value[n][2]=(this.value[n][2]-r.y)*e/r.height+r.y):"H"===i?this.value[n][1]=(this.value[n][1]-r.x)*t/r.width+r.x:"V"===i?this.value[n][1]=(this.value[n][1]-r.y)*e/r.height+r.y:"C"===i||"S"===i||"Q"===i?(this.value[n][1]=(this.value[n][1]-r.x)*t/r.width+r.x,this.value[n][2]=(this.value[n][2]-r.y)*e/r.height+r.y,this.value[n][3]=(this.value[n][3]-r.x)*t/r.width+r.x,this.value[n][4]=(this.value[n][4]-r.y)*e/r.height+r.y,"C"===i&&(this.value[n][5]=(this.value[n][5]-r.x)*t/r.width+r.x,this.value[n][6]=(this.value[n][6]-r.y)*e/r.height+r.y)):"A"===i&&(this.value[n][1]=this.value[n][1]*t/r.width,this.value[n][2]=this.value[n][2]*e/r.height,this.value[n][6]=(this.value[n][6]-r.x)*t/r.width+r.x,this.value[n][7]=(this.value[n][7]-r.y)*e/r.height+r.y);return this},equalCommands:function(t){var e,n,i;for(t=new b.PathArray(t),i=this.value.length===t.value.length,e=0,n=this.value.length;i&&ea);return i},bbox:function(){return b.parser().path.setAttribute("d",this.toString()),b.parser.nodes.path.getBBox()}}),b.Number=b.invent({create:function(t,e){this.value=0,this.unit=e||"","number"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-3.4e38:3.4e38:"string"==typeof t?(e=t.match(b.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),"%"===e[5]?this.value/=100:"s"===e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof b.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return("%"===this.unit?~~(1e8*this.value)/1e6:"s"===this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new b.Number(t),new b.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new b.Number(t),new b.Number(this-t,this.unit||t.unit)},times:function(t){return t=new b.Number(t),new b.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new b.Number(t),new b.Number(this/t,this.unit||t.unit)},to:function(t){var e=new b.Number(this);return"string"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new b.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new b.Number(this.destination).minus(this).times(t).plus(this):this}}}),b.HtmlNode=b.invent({create:function(t){this.node=t},extend:{add:function(t,e){return t=n(t),t.node!==this.node.children[e]&&this.node.insertBefore(t.node,this.node.children[e]||null),this},put:function(t,e){return this.add(t,e),t}}}),b.Element=b.invent({create:function(t){this.events={},this.dom={},this.node=t,this.node&&(this.type=t.nodeName,this.node.instance=this,this.events=t.events||{},t.hasAttribute("svgjs:data")&&this.setData(JSON.parse(t.getAttribute("svgjs:data"))||{}))},extend:{x:function(t){return this.attr("x",t)},y:function(t){return this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var n=d(this,t,e);return this.width(new b.Number(n.width)).height(new b.Number(n.height))},clone:function(t){this.writeDataToDom();var e=x(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return n(t).put(this)},putIn:function(t){return n(t).add(this)},id:function(t){return void 0!==t||this.node.id||(this.node.id=b.eid(this.type)),this.attr("id",t)},inside:function(t,e){var n=this.bbox();return t>n.x&&e>n.y&&t=0;--e)this[M[e]]=null!=t[M[e]]?t[M[e]]:n[M[e]]},extend:{clone:function(){return new b.Matrix(this)},transform:function(t){if(null!=t.a){var e=new b.Matrix(t);return this.lmultiply(e)}var n=w(t),i=new b.Matrix(this),r=(new b.Matrix).translate(-n.ox,-n.oy).scale(n.scaleX,n.scaleY).skew(n.skewX,n.skewY).shear(n.shear).rotate(n.theta).translate(n.ox,n.oy).translate(n.rx,n.ry).lmultiply(i);if(isFinite(n.px)||isFinite(n.py)){var s=new b.Point(n.ox-n.rx,n.oy-n.ry).transform(r),o=n.px?n.px-s.x:0,a=n.py?n.py-s.y:0;r=r.translate(o,a)}return r=r.translate(tx,ty)},compose:function(t){var e=t.scaleX||1,n=t.scaleY||1,i=t.shear||0,r=t.rotate||0,s=t.translateX||0,o=t.translateY||0;return(new b.Matrix).scale(e,n).shear(i).rotate(r).translate(s,o).lmultiply(this)},decompose:function(){var t=this.a,e=this.b,n=this.c,i=this.d,r=this.e,s=this.f,o=t*i-e*n,a=o>0?1:-1,h=a*Math.sqrt(t*t+e*e),u=180/Math.PI*Math.atan2(a*e,a*t),l=(t*n+e*i)/o;return{scaleX:h,scaleY:n*h/(l*t-e)||i*h/(l*e+t),shear:l,rotate:u,translateX:r,translateY:s,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f}},morph:function(t){return this.destination=new b.Matrix(t),this},at:function(t){return this.destination?new b.Matrix({a:this.a+(this.destination.a-this.a)*t,b:this.b+(this.destination.b-this.b)*t,c:this.c+(this.destination.c-this.c)*t,d:this.d+(this.destination.d-this.d)*t,e:this.e+(this.destination.e-this.e)*t,f:this.f+(this.destination.f-this.f)*t}):this},multiply:function(t){var e=this,n=new b.Matrix(t),i=e.a*n.a+e.c*n.b,r=e.b*n.a+e.d*n.b,s=e.a*n.c+e.c*n.d,o=e.b*n.c+e.d*n.d,a=e.e+e.a*n.e+e.c*n.f,h=e.f+e.b*n.e+e.d*n.f;return new b.Matrix(i,r,s,o,a,h)},lmultiply:function(t){return new b.Matrix(t).multiply(this)},inverse:function(){var t=this.a,e=this.b,n=this.c,i=this.d,r=this.e,s=this.f,o=t*i-e*n;if(!o)throw new Error("Cannot invert "+this);var a=i/o,h=-e/o,u=-n/o,l=t/o,c=-(a*r+u*s),f=-(h*r+l*s);return new b.Matrix(a,h,u,l,c,f)},translate:function(t,e){var n=new b.Matrix(this);return n.e+=t||0,n.f+=e||0,n},scale:function(t,e,n,i){1===arguments.length?e=t:3===arguments.length&&(i=n,n=e,e=t);var r=new b.Matrix(t,0,0,e,0,0);return this.around(n,i,r)},rotate:function(t,e,n){t=b.utils.radians(t);var i=new b.Matrix(Math.cos(t),Math.sin(t),-Math.sin(t),Math.cos(t),0,0);return this.around(e,n,i)},flip:function(t,e){return"x"===t?this.scale(-1,1,e,0):"y"===t?this.scale(1,-1,0,e):this.scale(-1,-1,t,e||t)},shear:function(t,e,n){var i=new b.Matrix(1,0,t,1,0,0);return this.around(e,n,i)},skew:function(t,e,n,i){1===arguments.length?e=t:3===arguments.length&&(i=n,n=e,e=t),t=b.utils.radians(t),e=b.utils.radians(e);var r=new b.Matrix(1,Math.tan(e),Math.tan(t),1,0,0);return this.around(n,i,r)},skewX:function(t,e,n){return this.skew(t,0,e,n)},skewY:function(t,e,n){return this.skew(0,t,e,n)},around:function(t,e,n){var i=t||0,r=e||0;return this.translate(-i,-r).lmultiply(n).translate(i,r)},native:function(){for(var t=b.parser.nodes.svg.node.createSVGMatrix(),e=M.length-1;e>=0;e--)t[M[e]]=this[M[e]];return t},equals:function(t){var e=new b.Matrix(t);return g(this.a,e.a)&&g(this.b,e.b)&&g(this.c,e.c)&&g(this.d,e.d)&&g(this.e,e.e)&&g(this.f,e.f)},toString:function(){return"matrix("+this.a+","+this.b+","+this.c+","+this.d+","+this.e+","+this.f+")"}},parent:b.Element,construct:{ctm:function(){return new b.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof b.Doc&&!this.isRoot()){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new b.Matrix(e)}return new b.Matrix(this.node.getScreenCTM())}}}),b.Point=b.invent({create:function(t,e,n){var i;n=n||{x:0,y:0},i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"==typeof t?{x:t.x,y:t.y}:{x:t,y:e},this.x=null==i.x?n.x:i.x,this.y=null==i.y?n.y:i.y},extend:{clone:function(){return new b.Point(this)},morph:function(t,e){return this.destination=new b.Point(t,e),this},at:function(t){return this.destination?new b.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t}):this},native:function(){var t=b.parser.nodes.svg.node.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new b.Point(this.native().matrixTransform(t.native()))}}}),b.extend(b.Element,{point:function(t,e){return new b.Point(t,e).transform(this.screenCTM().inverse())}}),b.extend(b.Element,{attr:function(t,e,n){if(null==t){for(t={},e=this.node.attributes,n=e.length-1;n>=0;n--)t[e[n].nodeName]=b.regex.isNumber.test(e[n].nodeValue)?parseFloat(e[n].nodeValue):e[n].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return e=this.node.getAttribute(t),null==e?b.defaults.attrs[t]:b.regex.isNumber.test(e)?parseFloat(e):e;"fill"!==t&&"stroke"!==t||(b.regex.isImage.test(e)&&(e=this.doc().defs().image(e)),e instanceof b.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new b.Number(e):b.Color.isColor(e)?e=new b.Color(e):Array.isArray(e)&&(e=new b.Array(e)),"leading"===t?this.leading&&this.leading(e):"string"==typeof n?this.node.setAttributeNS(n,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!==t&&"x"!==t||this.rebuild(t,e)}return this}}),b.extend(b.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){return(this.attr("transform")||"").split(b.regex.transforms).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(b.regex.delimiter).map(function(t){return parseFloat(t)})]}).reverse().reduce(function(t,e){return"matrix"===e[0]?t.lmultiply(p(e[1])):t[e[0]].apply(t,e[1])},new b.Matrix)},toParent:function(t){if(this===t)return this;var e=this.screenCTM(),n=t.screenCTM().inverse();return this.addTo(t).untransform().transform(n.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),b.extend(b.Element,{transform:function(t,e){var n=this.bbox();if(null==t||"string"==typeof t){var i=new b.Matrix(this).decompose();return i[t]||i}if("string"==typeof t.origin||null==t.origin&&null==t.ox&&null==t.oy){var r="string"==typeof t.origin?t.origin.toLowerCase().trim():"center",s=n.height,o=n.width,a=n.x,h=n.y;t.ox=r.includes("left")?a:r.includes("right")?a+o:a+o/2,t.oy=r.includes("top")?h:r.includes("bottom")?h+s:h+s/2,t.origin=null}var u=!0===e?this:e||!1,l=new b.Matrix(u).transform(t);return this.attr("transform",l)}}),b.extend(b.Timeline,{transform:function(t,e,n){}}),b.extend(b.Element,{css:function(t,e){var n,i,r={};if(0===arguments.length)return this.node.style.cssText.split(/\s*;\s*/).filter(function(t){return!!t.length}).forEach(function(t){n=t.split(/\s*:\s*/),r[n[0]]=n[1]}),r;if(arguments.length<2){if(Array.isArray(t)){for(i=t.length;i--;)r[u(t[i])]=this.node.style[u(t[i])];return r}if("string"==typeof t)return this.node.style[u(t)];if("object"==typeof t)for(i in t)this.node.style[u(i)]=null==t[i]||b.regex.isBlank.test(t[i])?"":t[i]}return 2===arguments.length&&(this.node.style[u(t)]=null==e||b.regex.isBlank.test(e)?"":e),this}}),b.Parent=b.invent({create:function(t){this.constructor(t)},inherit:b.Element,extend:{children:function(){return b.utils.map(this.node.children,function(t){return b.adopt(t)})},add:function(t,e){return t=n(t),t.node!==this.node.children[e]&&this.node.insertBefore(t.node,this.node.children[e]||null),this},put:function(t,e){return this.add(t,e),t.instance||t},has:function(t){return this.index(t)>=0},index:function(t){return[].slice.call(this.node.children).indexOf(t.node)},get:function(t){return b.adopt(this.node.children[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.children.length-1)},each:function(t,e){var n,i,r=this.children();for(n=0,i=r.length;n0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof b.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent().add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent().add(t,e+1),this}}),b.Mask=b.invent({create:"mask",inherit:b.Container,extend:{remove:function(){return this.targets().forEach(function(t){t.unmask()}),b.Element.prototype.remove.call(this)},targets:function(){return b.select('svg [mask*="'+this.id()+'"]')}},construct:{mask:function(){return this.defs().put(new b.Mask)}}}),b.extend(b.Element,{maskWith:function(t){var e=t instanceof b.Mask?t:this.parent().mask().add(t);return this.attr("mask",'url("#'+e.id()+'")')},unmask:function(){return this.attr("mask",null)},masker:function(){return this.reference("mask")}}),b.ClipPath=b.invent({create:"clipPath",inherit:b.Container,extend:{remove:function(){return this.targets().forEach(function(t){t.unclip()}),b.Element.prototype.remove.call(this)},targets:function(){return b.select('svg [clip-path*="'+this.id()+'"]')}},construct:{clip:function(){return this.defs().put(new b.ClipPath)}}}),b.extend(b.Element,{clipWith:function(t){var e=t instanceof b.ClipPath?t:this.parent().clip().add(t);return this.attr("clip-path",'url("#'+e.id()+'")')},unclip:function(){return this.attr("clip-path",null)},clipper:function(){return this.reference("clip-path")}}),b.Gradient=b.invent({create:function(t){this.constructor("object"==typeof t?t:b.create(t+"Gradient"))},inherit:b.Container,extend:{stop:function(t,e,n){return this.put(new b.Stop).update(t,e,n)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},url:function(){return"url(#"+this.id()+")"},toString:function(){return this.url()},attr:function(t,e,n){return"transform"===t&&(t="gradientTransform"),b.Container.prototype.attr.call(this,t,e,n)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),b.extend([b.Gradient,b.Timeline],{from:function(t,e){return"radialGradient"===(this._target||this).type?this.attr({ +fx:new b.Number(t),fy:new b.Number(e)}):this.attr({x1:new b.Number(t),y1:new b.Number(e)})},to:function(t,e){return"radialGradient"===(this._target||this).type?this.attr({cx:new b.Number(t),cy:new b.Number(e)}):this.attr({x2:new b.Number(t),y2:new b.Number(e)})}}),b.extend(b.Defs,{gradient:function(t,e){return this.put(new b.Gradient(t)).update(e)}}),b.Stop=b.invent({create:"stop",inherit:b.Element,extend:{update:function(t){return("number"==typeof t||t instanceof b.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new b.Number(t.offset)),this}}}),b.Pattern=b.invent({create:"pattern",inherit:b.Container,extend:{url:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.url()},attr:function(t,e,n){return"transform"===t&&(t="patternTransform"),b.Container.prototype.attr.call(this,t,e,n)}},construct:{pattern:function(t,e,n){return this.defs().pattern(t,e,n)}}}),b.extend(b.Defs,{pattern:function(t,e,n){return this.put(new b.Pattern).update(n).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),b.Doc=b.invent({create:function(t){this.constructor(t||b.create("svg")),this.namespace()},inherit:b.Container,extend:{isRoot:function(){return!(this.node.parentNode&&this.node.parentNode instanceof t.SVGElement&&"#document"!==this.node.parentNode.nodeName)},doc:function(){return this.isRoot()?this:b.Element.prototype.doc.call(this)},namespace:function(){return this.isRoot()?this.attr({xmlns:b.ns,version:"1.1"}).attr("xmlns:xlink",b.xlink,b.xmlns).attr("xmlns:svgjs",b.svgjs,b.xmlns):this.doc().namespace()},defs:function(){return this.isRoot()?b.adopt(this.node.getElementsByTagName("defs")[0])||this.put(new b.Defs):this.doc().defs()},parent:function(t){return this.isRoot()?"#document"===this.node.parentNode.nodeName?null:this.node.parentNode:b.Element.prototype.parent.call(this,t)},remove:function(){return this.isRoot()?(this.parent()&&this.parent().removeChild(this.node),this):b.Element.prototype.remove.call(this)},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this}},construct:{nested:function(){return this.put(new b.Doc)}}}),b.Shape=b.invent({create:function(t){this.constructor(t)},inherit:b.Element}),b.Bare=b.invent({create:function(t,e){if(this.constructor(b.create(t)),e)for(var n in e.prototype)"function"==typeof e.prototype[n]&&(this[n]=e.prototype[n])},inherit:b.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),b.extend(b.Parent,{element:function(t,e){return this.put(new b.Bare(t,e))}}),b.Symbol=b.invent({create:"symbol",inherit:b.Container,construct:{symbol:function(){return this.put(new b.Symbol)}}}),b.Use=b.invent({create:"use",inherit:b.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,b.xlink)}},construct:{use:function(t,e){return this.put(new b.Use).element(t,e)}}}),b.Rect=b.invent({create:"rect",inherit:b.Shape,construct:{rect:function(t,e){return this.put(new b.Rect).size(t,e)}}}),b.Circle=b.invent({create:"circle",inherit:b.Shape,construct:{circle:function(t){return this.put(new b.Circle).rx(new b.Number(t).divide(2)).move(0,0)}}}),b.extend([b.Circle,b.Timeline],{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),b.Ellipse=b.invent({create:"ellipse",inherit:b.Shape,construct:{ellipse:function(t,e){return this.put(new b.Ellipse).size(t,e).move(0,0)}}}),b.extend([b.Ellipse,b.Rect,b.Timeline],{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),b.extend([b.Circle,b.Ellipse],{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t)},width:function(t){return null==t?2*this.rx():this.rx(new b.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new b.Number(t).divide(2))},size:function(t,e){var n=d(this,t,e);return this.rx(new b.Number(n.width).divide(2)).ry(new b.Number(n.height).divide(2))}}),b.Line=b.invent({create:"line",inherit:b.Shape,extend:{array:function(){return new b.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,n,i){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:n,y2:i}:new b.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var n=d(this,t,e);return this.attr(this.array().size(n.width,n.height).toLine())}},construct:{line:function(t,e,n,i){return b.Line.prototype.plot.apply(this.put(new b.Line),null!=t?[t,e,n,i]:[0,0,0,0])}}}),b.Polyline=b.invent({create:"polyline",inherit:b.Shape,construct:{polyline:function(t){return this.put(new b.Polyline).plot(t||new b.PointArray)}}}),b.Polygon=b.invent({create:"polygon",inherit:b.Shape,construct:{polygon:function(t){return this.put(new b.Polygon).plot(t||new b.PointArray)}}}),b.extend([b.Polyline,b.Polygon],{array:function(){return this._array||(this._array=new b.PointArray(this.attr("points")))},plot:function(t){return null==t?this.array():this.clear().attr("points","string"==typeof t?t:this._array=new b.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var n=d(this,t,e);return this.attr("points",this.array().size(n.width,n.height))}}),b.extend([b.Line,b.Polyline,b.Polygon],{MorphArray:b.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),b.Path=b.invent({create:"path",inherit:b.Shape,extend:{MorphArray:b.PathArray,array:function(){return this._array||(this._array=new b.PathArray(this.attr("d")))},plot:function(t){return null==t?this.array():this.clear().attr("d","string"==typeof t?t:this._array=new b.PathArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var n=d(this,t,e);return this.attr("d",this.array().size(n.width,n.height))},width:function(t){return null==t?this.bbox().width:this.size(t,this.bbox().height)},height:function(t){return null==t?this.bbox().height:this.size(this.bbox().width,t)}},construct:{path:function(t){return this.put(new b.Path).plot(t||new b.PathArray)}}}),b.Image=b.invent({create:"image",inherit:b.Shape,extend:{load:function(e,n){if(!e)return this;var i=new t.Image;return b.on(i,"load",function(t){var r=this.parent(b.Pattern);0===this.width()&&0===this.height()&&this.size(i.width,i.height),r instanceof b.Pattern&&0===r.width()&&0===r.height()&&r.size(this.width(),this.height()),"function"==typeof n&&n.call(this,{width:i.width,height:i.height,ratio:i.width/i.height,url:e})},this),b.on(i,"load error",function(){b.off(i)}),this.attr("href",i.src=e,b.xlink)}},construct:{image:function(t,e){return this.put(new b.Image).size(0,0).load(t,e)}}}),b.Text=b.invent({create:function(t){this.constructor(t||b.create("text")),this.dom.leading=new b.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",b.defaults.attrs["font-family"])},inherit:b.Parent,extend:{x:function(t){return null==t?this.attr("x"):this.attr("x",t)},y:function(t){var e=this.attr("y"),n="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-n:e:this.attr("y","number"==typeof t?t+n:t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},text:function(t){if(void 0===t){var e=this.node.childNodes,n=0;t="";for(var i=0,r=e.length;i=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),b.get=function(t){var n=e.getElementById(y(t)||t);return b.adopt(n)},b.select=function(t,n){return b.utils.map((n||e).querySelectorAll(t),function(t){return b.adopt(t)})},b.$$=function(t,n){return b.utils.map((n||e).querySelectorAll(t),function(t){return b.adopt(t)})},b.$=function(t,n){return b.adopt((n||e).querySelector(t))},b.extend(b.Parent,{select:function(t){return b.select(t,this.node)}});var M="abcdef".split("");return b.Box=b.invent({create:function(t){var e=[0,0,0,0];t="string"==typeof t?t.split(b.regex.delimiter).map(parseFloat):Array.isArray(t)?t:"object"==typeof t?[null!=t.left?t.left:t.x,null!=t.top?t.top:t.y,t.width,t.height]:4===arguments.length?[].slice.call(arguments):e,this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3],v(this)},extend:{merge:function(t){var e=Math.min(this.x,t.x),n=Math.min(this.y,t.y);return new b.Box(e,n,Math.max(this.x+this.width,t.x+t.width)-e,Math.max(this.y+this.height,t.y+t.height)-n)},transform:function(t){var e=1/0,n=-1/0,i=1/0,r=-1/0;return[new b.Point(this.x,this.y),new b.Point(this.x2,this.y),new b.Point(this.x,this.y2),new b.Point(this.x2,this.y2)].forEach(function(s){s=s.transform(t),e=Math.min(e,s.x),n=Math.max(n,s.x),i=Math.min(i,s.y),r=Math.max(r,s.y)}),new b.Box(e,i,n-e,r-i)},addOffset:function(){return this.x+=t.pageXOffset,this.y+=t.pageYOffset,this},toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height},morph:function(t,e,n,i){return this.destination=new b.Box(t,e,n,i),this},at:function(t){return this.destination?new b.Box(this.x+(this.destination.x-this.x)*t,this.y+(this.destination.y-this.y)*t,this.width+(this.destination.width-this.width)*t,this.height+(this.destination.height-this.height)*t):this}},parent:b.Element,construct:{bbox:function(){var t;try{if(t=this.node.getBBox(),i(t)&&!r(this.node))throw new Exception("Element not in the dom")}catch(n){try{var e=this.clone(b.parser().svg).show();t=e.node.getBBox(),e.remove()}catch(t){console.warn("Getting a bounding box of this element is not possible")}}return new b.Box(t)},rbox:function(t){try{var e=new b.Box(this.node.getBoundingClientRect());return t?e.transform(t.screenCTM().inverse()):e.addOffset()}catch(t){return new b.Box}}}}),b.extend([b.Doc,b.Symbol,b.Image,b.Pattern,b.Marker,b.ForeignObject,b.View],{viewbox:function(t,e,n,i){return null==t?new b.Box(this.attr("viewBox")):this.attr("viewBox",new b.Box(t,e,n,i))}}),b.parser=function(){var t;return b.parser.nodes.svg.node.parentNode||(t=e.body||e.documentElement,b.parser.nodes.svg.addTo(t)),b.parser.nodes},b.parser.nodes={svg:b().size(2,0).css({opacity:0,position:"absolute",left:"-100%",top:"-100%",overflow:"hidden"})},b.parser.nodes.path=b.parser.nodes.svg.path().node,b.Animator={nextDraw:null,frames:new b.Queue,timeouts:new b.Queue,frameCount:0,timeoutCount:0,timer:t.performance||t.Date,frame:function(t){return b.Animator.frames.push({id:b.Animator.frameCount,run:t}),null===b.Animator.nextDraw&&(b.Animator.nextDraw=requestAnimationFrame(b.Animator._draw)),++b.Animator.frameCount},timeout:function(t,e){e=e||0;var n=b.Animator.timer.now()+e,i=b.Animator.timeoutCount++;return b.Animator.timeouts.push({id:i,run:t,time:n}),null===b.Animator.nextDraw&&(b.Animator.nextDraw=requestAnimationFrame(b.Animator._draw)),i},cancelTimeout:function(t){return b.Animator.timeouts.remove(function(e){return e.id===t})},_draw:function(t){for(var e=null,n=b.Animator.timeouts.last();(e=b.Animator.timeouts.shift())&&(t>e.time?e.run():b.Animator.timeouts.push(e),e!==n););for(var i=(b.Animator.frames.last(),b.Animator.frameCount);b.Animator.frames.first()&&b.Animator.frames.first().id0||b.Animator.frames.length>0?requestAnimationFrame(b.Animator._draw):null}},b}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 5b511ef..5883790 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -40,7 +40,6 @@ var parts = [ 'src/number.js', 'src/HtmlNode.js', 'src/element.js', - 'src/fx.js', 'src/matrix.js', 'src/point.js', 'src/attr.js', @@ -81,7 +80,8 @@ var parts = [ 'src/helpers.js', 'src/polyfill.js', 'src/boxes.js', - 'src/parser.js' + 'src/parser.js', + 'src/animator.js', ] gulp.task('clean', function () { diff --git a/src/drawLoop.js b/src/animator.js similarity index 91% rename from src/drawLoop.js rename to src/animator.js index 6b6e6c3..4d37008 100644 --- a/src/drawLoop.js +++ b/src/animator.js @@ -1,3 +1,4 @@ +/* global requestAnimationFrame */ SVG.Animator = { nextDraw: null, @@ -44,18 +45,24 @@ SVG.Animator = { cancelTimeout: function (id) { // Find the index of the timeout to cancel and remove it - var index = SVG.Animator.timeouts.remove(function (t) { return t.id == id }) + var index = SVG.Animator.timeouts.remove( + function (t) { + return t.id === id + } + ) return index }, _draw: function (now) { + // Run all the timeouts we can run, if they are not ready yet, add them // to the end of the queue immediately! (bad timeouts!!! [sarcasm]) var tracking = true var nextTimeout = null var lastTimeout = SVG.Animator.timeouts.last() while ((nextTimeout = SVG.Animator.timeouts.shift())) { - // Run the timeout if its time, or push it to the end + + // Run the timeout if its time, or push it to the end if (now > nextTimeout.time) { nextTimeout.run() } else { diff --git a/src/helpers.js b/src/helpers.js index ff8746e..fd8b376 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -203,3 +203,61 @@ var abcdef = 'abcdef'.split('') function closeEnough (a, b, threshold) { return Math.abs(b - a) < (threshold || 1e-6) } + +// TODO: Refactor this to a static function of matrix.js +function formatTransforms (o) { + + // Get all of the parameters required to form the matrix + var flipBoth = o.flip === 'both' || o.flip === true + var flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1 + var flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1 + var skewX = o.skew && o.skew.length ? o.skew[0] + : isFinite(o.skew) ? o.skew + : isFinite(o.skewX) ? o.skewX + : 0 + var skewY = o.skew && o.skew.length ? o.skew[1] + : isFinite(o.skew) ? o.skew + : isFinite(o.skewY) ? o.skewY + : 0 + var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX + : isFinite(o.scale) ? o.scale * flipX + : isFinite(o.scaleX) ? o.scaleX * flipX + : flipX + var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY + : isFinite(o.scale) ? o.scale * flipY + : isFinite(o.scaleY) ? o.scaleY * flipY + : flipY + var shear = o.shear || 0 + var theta = o.rotate || 0 + var origin = new SVG.Point(o.ox == null ? o.origin : o.ox, o.oy) + var ox = origin.x + var oy = origin.y + var position = new SVG.Point(o.px == null + ? o.position : o.px, o.py, {x: null, y: null}) + var px = position.x + var py = position.y + var translate = new SVG.Point(o.tx == null ? o.translate : o.tx, o.ty) + var tx = translate.x + var ty = translate.y + var relative = new SVG.Point(o.rx == null ? o.relative : o.rx, o.ry) + var rx = relative.x + var ry = relative.y + + // Populate all of the values + return { + scaleX: scaleX, + scaleY: scaleY, + skewX: skewX, + skewY: skewY, + shear: shear, + theta: theta, + rx: rx, + ry: ry, + tx: tx, + ty: ty, + ox: ox, + oy: oy, + px: px, + py: py, + } +} diff --git a/src/matrix.js b/src/matrix.js index cab657f..97eaab7 100644 --- a/src/matrix.js +++ b/src/matrix.js @@ -43,59 +43,28 @@ SVG.Matrix = SVG.invent({ return newMatrix } - // Get all of the parameters required to form the matrix - var flipX = o.flip && (o.flip === 'x' || o.flip === 'both') ? -1 : 1 - var flipY = o.flip && (o.flip === 'y' || o.flip === 'both') ? -1 : 1 - var skewX = o.skew && o.skew.length ? o.skew[0] - : isFinite(o.skew) ? o.skew - : isFinite(o.skewX) ? o.skewX - : 0 - var skewY = o.skew && o.skew.length ? o.skew[1] - : isFinite(o.skew) ? o.skew - : isFinite(o.skewY) ? o.skewY - : 0 - var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX - : isFinite(o.scale) ? o.scale * flipX - : isFinite(o.scaleX) ? o.scaleX * flipX - : flipX - var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY - : isFinite(o.scale) ? o.scale * flipY - : isFinite(o.scaleY) ? o.scaleY * flipY - : flipY - var shear = o.shear || 0 - var theta = o.rotate || 0 - var origin = new SVG.Point(o.ox == null ? o.origin : o.ox, o.oy) - var ox = origin.x - var oy = origin.y - var position = new SVG.Point(o.px == null - ? o.position : o.px, o.py, {x: null, y: null}) - var px = position.x - var py = position.y - var translate = new SVG.Point(o.tx == null ? o.translate : o.tx, o.ty) - var tx = translate.x - var ty = translate.y - var relative = new SVG.Point(o.rx == null ? o.relative : o.rx, o.ry) - var rx = relative.x - var ry = relative.y + // Get the proposed transformations and the current transformations + var t = formatTransforms(o) var currentTransform = new SVG.Matrix(this) // Construct the resulting matrix var transformer = new SVG.Matrix() - .translate(-ox, -oy) - .scale(scaleX, scaleY) - .skew(skewX, skewY) - .shear(shear) - .rotate(theta) - .translate(ox, oy) - .translate(rx, ry) + .translate(-t.ox, -t.oy) + .scale(t.scaleX, t.scaleY) + .skew(t.skewX, t.skewY) + .shear(t.shear) + .rotate(t.theta) + .translate(t.ox, t.oy) + .translate(t.rx, t.ry) .lmultiply(currentTransform) // If we want the origin at a particular place, we force it there - if (isFinite(px) || isFinite(py)) { + if (isFinite(t.px) || isFinite(t.py)) { + // Figure out where the origin went and the delta to get there - var current = new SVG.Point(ox - rx, oy - ry).transform(transformer) - var dx = px ? px - current.x : 0 - var dy = py ? py - current.y : 0 + var current = new SVG.Point(t.ox - t.rx, t.oy - t.ry).transform(transformer) + var dx = t.px ? t.px - current.x : 0 + var dy = t.py ? t.py - current.y : 0 // Apply another translation transformer = transformer.translate(dx, dy) @@ -221,7 +190,31 @@ SVG.Matrix = SVG.invent({ // Inverses matrix inverse: function () { - return new SVG.Matrix(this.native().inverse()) + + // Get the current parameters out of the matrix + var a = this.a + var b = this.b + var c = this.c + var d = this.d + var e = this.e + var f = this.f + + // Invert the 2x2 matrix in the top left + var det = a * d - b * c + if (!det) throw new Error("Cannot invert " + this) + + // Calculate the top 2x2 matrix + var na = d / det + var nb = -b / det + var nc = -c / det + var nd = a / det + + // Apply the inverted matrix to the top right + var ne = - ( na * e + nc * f ) + var nf = - ( nb * e + nd * f ) + + // Construct the inverted matrix + return new SVG.Matrix(na, nb, nc, nd, ne, nf) }, // Translate matrix diff --git a/src/morph.js b/src/morph.js index 7e99fef..28584d3 100644 --- a/src/morph.js +++ b/src/morph.js @@ -59,11 +59,13 @@ SVG.Morphable = SVG.invent{ var _this = this + modifier = this.modifier || function(el) { return el } + // for(var i = 0, len = this._from.length; i < len; ++i) { // arr.push(this.controller(this._from[i], this._to[i])) // } - return this.type.fromArray(this.modifier(this._from.map(function (i, index) { + return this.type.fromArray(modifier(this._from.map(function (i, index) { return _this.controller(i, _this._to[i], pos) }))) }, @@ -129,6 +131,38 @@ SVG.Morphable.TransformBag = SVG.invent({ } }) + +SVG.Morphable.ObjectBag = SVG.invent({ + create: function (obj) { + this.values = [] + this.keys = [] + + for(var i in obj) { + this.values.push(obj[i]) + this.keys.push(i) + } + }, + + extend: { + valueOf: function () { + return this.values + }, + + toArray: function (){ + return this.values + } + + fromArray: function (arr) { + var obj = {} + + for(var i = 0, len = arr.length; i < len; ++i) { + obj[this.keys[i]] = arr[i] + } + + return obj + } +}) + SVG.MorphableTypes = [SVG.Number, SVG.Color, SVG.Box, SVG.Matrix, SVG.Morphable.NonMorphable, SVG.Morphable.TransformBag] SVG.extend(SVG.MorphableTypes, { to: (item, args) => { @@ -176,6 +210,24 @@ SVG.extend(SVG.MorphableTypes, { // 4. Now you get the delta matrix as a result: D = I * inv(M) +value = null +init ( ) { + if(!morpher.hasFrom()) { + morpher.from(el.whatever()) + } + + value = value == null ? get the value : value + return value + else +} + +// C R x = D C x = A x +// +// (C R inv(C)) C x +// +// +// C R = D C +// D = C R inv(C) el.animate().trasform({rotate: 720, scale: 2}, true) @@ -193,23 +245,80 @@ relative -> start at 0 always - {random stuff} |> modifier |> fromArray -function transform(transforms, relative, affine) { - affine = transforms.affine || affine - relative = transforms.relative || relative +function transform(o, relative, affine) { + affine = transforms.affine || affine || !!transform.a + relative = transforms.relative || relative || false + + var morpher + var el = this.target() + + /** + The default of relative is false + affine defaults to true if transformations are used and to false when a matrix is given + + We end up with 4 possibilities: + false, false: absolute direct matrix morph with SVG.Matrix + true, false: relative direct matrix morph with SVG.Marix or relative whatever was passed transformation with ObjectBag + + false, true: absolute affine transformation with SVG.TransformBag + true, true: relative whatever was passed transformation with ObjectBag + **/ + + + // if we have a relative transformation and its not a matrix + // we morph all parameters directly with the ObjectBag + // the following cases are covered here: + // - true, false with ObjectBag + // - true, true with ObjectBag + if(relative && transforms.a == null) { + morpher = SVG.Morphable.ObjectBag(formatTransforms({})).to(formatTransforms(transforms)) + + return this.queue(function() {}, function (pos) { + el.pushRightTransform(new Matrix(morpher.at(pos))) + }) + } + + // when we have a matrix and its non affine we transform directly + // the following cases are covered here: + // - true, false with SVG.Matrix + // - false, false with SVG.Matrix + /* + // this is covered below now + if(transforms.a != null && !affine) { + var morpher = new SVG.Matrix().to(transforms) + + this.queue(function () {}, function (pos) { + if(!relative) { + var curr = el.currentTransform() + morpher.from(curr) + el.pushLeftTransform(morpher.at(pos).multiply(curr.inverse())) + } else { + el.pushRightTransform(morpher.at(pos)) + } + }) + }*/ + + // what is left is affine morphing for SVG.Matrix and absolute transformations with TransformBag + // the following cases are covered here: + // - false, true with SVG.Matrix + // - false, true with SVG.TransformBag + // - true, false with SVG.Matrix + // - false, false with SVG.Matrix // 1. define the final state (T) and decompose it (once) t = [tx, ty, the, lam, sy, sx] - var morpher = new SVG.Morphable.TransformBag().to(transforms) + var morpher = (transforms.a && !affine) + ? new SVG.Matrix().to(transforms) + : new SVG.Morphable.TransformBag().to(transforms) - // make sure you have an identity matrix defined as default for relative transforms - var morpher.from() - var el = this.target() + // create identity Matrix for relative not affine Matrix transformation + morpher.from() + + this.queue(function() {}, function (pos) { - var initFn = relative ? function() {} : function() { // 2. on every frame: pull the current state of all previous transforms (M - m can change) - morpher.from(el.transform()) - } + var curr = el.currentTransform() + if(!relative) morpher.from(curr) - this.queue(initFn, function (pos) { // 3. Find the interpolated matrix I(pos) = m + pos * (t - m) // - Note I(0) = M // - Note I(1) = T @@ -217,14 +326,91 @@ function transform(transforms, relative, affine) { if(!relative) { // 4. Now you get the delta matrix as a result: D = I * inv(M) - matrix = matrix.multiply(el.transform().inverse()) + var delta = matrix.multiply(curr.inverse()) + el.pushLeftTransform(delta) + } else { + el.pushRightTransform(matrix) } - - el.pushTransform(matrix) }) } + +/** + INIT + - save the current transformation + + ELEMENT TIMELINE (one timeline per el) + - Reads the current transform and save it to the transformation stack + - Runs all available runners, runners will: + - Modify their transformation on the stack + - Mark their transformation as complete + - After each runner, we group the matrix (not for now) + - After running the runners, we bundle all contiguous transformations into + a single transformation + + + - transformtionstack is like this: [RunnerB, Matrix, RunnerC] + - skip merging for now (premature blabla) + + +el.loop({times: 5, swing: true, wait: [20, 50]}) + +el.opacity(0) + .animate(300).opacity(1) + .animate(300, true).scale(5).reverse() + + +for(var i = 0; i < 7; ++i) + circle.clone() + .scale(3).rotate(0) + .loop({swing: false, wait: 500}) + .scale(1) + .rotate(360) + .delay(1000) + .animate(500, 'swingOut') + .scale(3) +} + +fn () => { + el.animate().stroke('dashoffset', 213).scale(1) + .delay(1) + .animate().scale(2) + .after(fn) +} + + + +When you start an element has a base matrix B - which starts as the identity + + If you modify the matrix, then we have: + + T U V W X B x + . . . + + runner.step() + + for all runners in stack: + if(runner is done) repalce with matrix + + if(2 matrix next to eachother are done) { + + } + +What if + +/// RunnerA +el.animate() + .transform({rotate: 30, scale: 2}) + .transform({rotate: 500}, true) + +f| -----A----- +s| --------B--------- +t| ---------C------- + +**/ + + SVG.Morphable.TransformList = Object if(affine) { diff --git a/src/number.js b/src/number.js index e6cecc2..5d787ad 100644 --- a/src/number.js +++ b/src/number.js @@ -99,12 +99,5 @@ SVG.Number = SVG.invent({ .times(pos) .plus(this) } - - - -new SVG.Color('#2a4e5a').morph('#3b4f2a').at(0.4) - -new Morph().from('#2a4e5a').to('#3b4f2a').at(0.3) - } }) diff --git a/src/timeline.js b/src/timeline.js index 5106058..f2ca933 100644 --- a/src/timeline.js +++ b/src/timeline.js @@ -11,7 +11,6 @@ SVG.easing = { function Runner (timeline) { // We store a reference to the function to run and the timeline to use - this.timeline = timeline this.transforms = [] this.functions = [] this.done = false @@ -20,13 +19,18 @@ function Runner (timeline) { this._startTime = timeline._startTime this._duration = timeline._duration this._loop = timeline._loop + this._active = false } +// The runner gets the time from the timeline Runner.prototype = { add: function (initFn, runFn) { - this.initialisers.push(initFn) - this.functions.push(fn) + this.functions.push({ + initialised: false, + initialiser: initFn, + runner: runFn, + }) }, step: function (time) { @@ -34,32 +38,23 @@ Runner.prototype = { // If it is time to do something, act now. var end = this._start + this._duration var running = this._start < time && time < end - if (running && this._running) { + + if (running && !this.timeline._paused) { var position = (time - this._startTime) / this._duration - var toRun = this.functions - for (var i = 0, len = toRun.length, i < len ; ++i) { - toRun[i](position) + for (var i = 0, len = this.functions.length; i < len ; ++i) { + + // If + this.functions[i](position) } } - // 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 - }, - - snap: function () { - - }, - - pause: function () { - + return finished }, } -let time = window.performance || window.Date +var time = window.performance || window.Date SVG.Timeline = SVG.invent({ @@ -74,17 +69,22 @@ SVG.Timeline = SVG.invent({ this._startTime = time.now() this._duration = SVG.defaults.duration this._ease = SVG.defaults.ease + this._speed = 1.0 // Play control variables control how the animation proceeds - this._controller = o instanceof 'function' ? o : null - this._backwards = false + this._controller = null this._reverse = false - this._loops = 0 + this._loops = null + this._waits = null + this._swing = null // Keep track of the running animations and their starting parameters this._baseTransform = null - this._running = true + this._nextFrame = null + this._paused = false + this._runner = null this._runners = [] + this._time = 0 }, extend: { @@ -92,14 +92,13 @@ SVG.Timeline = SVG.invent({ animate (duration, delay, now) { // Clear the controller and the looping parameters - this._controller = null + this._controller = duration instanceof Function ? duration : null this._backwards = false this._swing = false this._loops = 0 // If we have a controller, we will use the declarative animation mode - if(duration instanceof 'function') { - + if(duration instanceof Function) { this._controller = duration // If we have an object we are declaring imperative animations @@ -115,8 +114,14 @@ SVG.Timeline = SVG.invent({ this._startTime = now ? time.now() : (this._startTime + this._duration) this._duration = duration || SVG.defaults.duration - // Make a new runner to take care of the + // Make a new runner to queue all of the animations onto + this._runner = new Runner(this) + this._runners.push(this._runner) + // Step the animation + this._step() + + // Allow for chaining return this }, @@ -133,24 +138,21 @@ SVG.Timeline = SVG.invent({ return this }, - loop (times, swing) { - this._loops = times - this._swing = swing - }, - play () { - this._running = true + this._paused = false this._continue() + return this }, pause () { - this._running = false + this._paused = true + return this }, stop () { this.pause() - // Cancel all of the requested animation frames + // Cancel the next animation frame for this object }, @@ -159,14 +161,14 @@ SVG.Timeline = SVG.invent({ }, speed (newSpeed) { - + this._speed = newSpeed }, seek (dt) { }, - persist (dt || forever) { + persist (dtOrForever) { // 0 by default }, @@ -175,24 +177,44 @@ SVG.Timeline = SVG.invent({ }, queue (initialise, during) { + this._runner.add(initialise, during) + }, - // Make a new runner - var runner = new Runner(this) - this._runners.push() + _step (time) { - }, + // If we are paused, just exit + if (this._paused) return - _step (dt) { + // Get the time delta from the last time + // TODO: Deal with window.blur window.focus to pause animations + // HACK: We keep the time below 16ms to avoid driving declarative crazy + var dt = this._speed * ((time - this._time) || 16) / 1000 + dt = dt < 0.1 ? dt : 0.016 // If we missed alot of time, ignore + this._time += time + + // Run all of the runners directly + var runnersLeft = false + for (var i = 0, i < this._runners.length; ; i++) { + var runner = this._runners[i] + var finished = runner.step(this._time) + if (!finished) + runnersLeft = true + } + // Get the next animation frame to keep the simulation going + if (runnersLeft) + this._nextFrame = draw.frame(this.step.bind(this)) + else this._nextFrame = null + return this }, // Checks if we are running and continues the animation _continue () { if (this._paused) return - - // Go through each of the runners and step them + if (!this._nextFrame) + this._step() + return this }, - }, // Only elements are animatable @@ -205,53 +227,73 @@ SVG.Timeline = SVG.invent({ // Get the current timeline or construct a new one this.timeline = (this.timeline || new SVG.Timeline(this)) .animate(o, delay, now) + this.timeline._loops = null return this.timeline - } - } -} - -// Extend the attribute methods separately to avoid cluttering the main -// Timeline class above -SVG.extend(SVG.Timeline, { - - - attr: function (a, v) { - return this.styleAttr('attr', a, v) - }, + }, - // Add animatable styles - css: function (s, v) { - return this.styleAttr('css', s, v) - }, + loop: function(o) { - styleAttr (type, name, val) { - // apply attributes individually - if (typeof name === 'object') { - for (var key in val) { - this.styleAttr(type, key, val[key]) + /* + { + swing: wether or not the animation should repeat when its done + times: the number of times to loop the animation + wait: [array] a buffer of times to wait between successive animations + delay: defaults to wait } + */ + this.timeline = (this.timeline || new SVG.Timeline(this)) + + // REFACTOR this into an init function + this.timeline._waits = [].concat(o.wait || o.delay || 0) + this.timeline._loops = o.times || Infinity + this.timeline._swing = o.swing || false + return this.timeline } - - var morpher = new Morph(this.controller).to(val) - - this.queue( - function () { - morpher = morpher.from(element[type](name)) - }, - function () { - this.element[type](name, morpher.at(pos)) - } - ) - - return this - }, - - 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)} - ) - return this } }) + +// // Extend the attribute methods separately to avoid cluttering the main +// // Timeline class above +// SVG.extend(SVG.Timeline, { +// +// +// attr: function (a, v) { +// return this.styleAttr('attr', a, v) +// }, +// +// // Add animatable styles +// css: function (s, v) { +// return this.styleAttr('css', s, v) +// }, +// +// styleAttr (type, name, val) { +// // apply attributes individually +// if (typeof name === 'object') { +// for (var key in val) { +// this.styleAttr(type, key, val[key]) +// } +// } +// +// var morpher = new Morph(this.controller).to(val) +// +// this.queue( +// function () { +// morpher = morpher.from(element[type](name)) +// }, +// function () { +// this.element[type](name, morpher.at(pos)) +// } +// ) +// +// return this +// }, +// +// 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)} +// ) +// return this +// } +// }) -- 2.39.5