From 74040b36296aa463fa85a43974a4c92c564894a7 Mon Sep 17 00:00:00 2001 From: Rémi Tétreault Date: Thu, 20 Oct 2016 01:04:12 -0400 Subject: Make the code in the FX module simpler Here are the changes that have been made: - The loop counter is now incrementing (from 0 to loops) - The loop counter increment even when loops is true - Add absPos, the absolute position of an animation which is its position in the context of its complete duration (including delay and loops) - Make the methods speed, pause/resume affect the delay - The method step no longer needs to recalculate the value of situation.start - Add a second parameter to the method at to allow it to receive an absolute position - Fix the bug where the method at invert the passed position while the animation is running backward Also, I modified the tests of the FX module that required timing to use Jasmine Clock, so now it takes less than 1 second to run all the tests! --- dist/svg.js | 122 ++++-- dist/svg.min.js | 4 +- spec/spec/fx.js | 1316 ++++++++++++++++++++++++++++++++++++++++++++++--------- src/fx.js | 120 +++-- 4 files changed, 1279 insertions(+), 283 deletions(-) diff --git a/dist/svg.js b/dist/svg.js index dd5cb64..e94fe38 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -6,7 +6,7 @@ * @copyright Wout Fierens * @license MIT * -* BUILT: Tue Nov 01 2016 17:15:59 GMT-0400 (EDT) +* BUILT: Tue Nov 01 2016 20:04:39 GMT-0400 (EDT) */; (function(root, factory) { if (typeof define === 'function' && define.amd) { @@ -1240,7 +1240,9 @@ SVG.Situation = SVG.invent({ this.finish = this.start + this.duration this.ease = o.ease - this.loop = false + // 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 = { @@ -1286,6 +1288,9 @@ SVG.FX = SVG.invent({ 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 } @@ -1342,14 +1347,14 @@ SVG.FX = SVG.invent({ return this._target } - // returns the position at a given time - , timeToPos: function(timestamp){ + // 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 positon - , posToTime: function(pos){ - return this.situation.duration/this._speed * pos + this.situation.start + // returns the timestamp from a given absolute positon + , absPosToTime: function(absPos){ + return this.situation.duration/this._speed * absPos + this.situation.start } // starts the animationloop @@ -1367,7 +1372,7 @@ SVG.FX = SVG.invent({ , start: function(){ // dont start if already started if(!this.active && this.situation){ - this.situation.start = +new Date + this.situation.delay + this.situation.start = +new Date + this.situation.delay/this._speed this.situation.finish = this.situation.start + this.situation.duration/this._speed this.initAnimations() @@ -1407,7 +1412,7 @@ SVG.FX = SVG.invent({ var fn = function(){ if(this.situation instanceof SVG.Situation) - this.initAnimations().at(0) + this.initAnimations().atStart() else if(this.situation instanceof SVG.Delay) this.dequeue() else @@ -1498,15 +1503,7 @@ SVG.FX = SVG.invent({ this.active = false if(jumpToEnd && this.situation){ - - this.situation.loop = false - - if(this.situation.loops % 2 == 0 && this.situation.reversing){ - this.situation.reversed = true - } - - this.at(1) - + this.atEnd() } this.stopAnimFrame() @@ -1523,7 +1520,7 @@ SVG.FX = SVG.invent({ var temp = this.situation this.stop() this.situation = temp - this.at(0) + this.atStart() } return this } @@ -1540,13 +1537,40 @@ SVG.FX = SVG.invent({ 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 + return this.at(this.situation.loop+1, true) + } else 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 - , at: function(pos){ + // if isAbsPos is true, pos is treated as an absolute position + , at: function(pos, isAbsPos){ var durDivSpd = this.situation.duration/this._speed - this.pos = pos - this.situation.start = +new Date - pos * durDivSpd + 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) } @@ -1560,7 +1584,8 @@ SVG.FX = SVG.invent({ if (speed) { this._speed = speed - return this.at(this.situation.reversed ? 1-this.pos : this.pos) + // 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 } @@ -1568,8 +1593,9 @@ SVG.FX = SVG.invent({ , loop: function(times, reverse) { var c = this.last() - // store current loop and total loops - c.loop = c.loops = times || true + // store total loops + c.loops = (times != null) ? times : true + c.loop = 0 if(reverse) c.reversing = true return this @@ -1587,7 +1613,8 @@ SVG.FX = SVG.invent({ , play: function(){ if(!this.paused) return this this.paused = false - return this.at(this.pos) + // We use an absolute position here so that the delay before the animation can be paused + return this.at(this.absPos, true) } /** @@ -1692,22 +1719,45 @@ SVG.FX = SVG.invent({ */ , step: function(ignoreTime){ - // convert current time to position - if(!ignoreTime) this.pos = this.timeToPos(+new Date) - - if(this.pos >= 1 && (this.situation.loop === true || (typeof this.situation.loop == 'number' && --this.situation.loop))){ + // 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){ - this.situation.reversed = !this.situation.reversed + 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) } - return this.at(this.pos-1) + + } 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 - // correct position - if(this.pos > 1)this.pos = 1 - if(this.pos < 0)this.pos = 0 // apply easing var eased = this.situation.ease(this.pos) diff --git a/dist/svg.min.js b/dist/svg.min.js index d6b4ca4..2d15761 100644 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,2 +1,2 @@ -/*! svg.js v2.3.6 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,e){return t instanceof e}function n(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}function r(t){return t.toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()})}function s(t){return t.charAt(0).toUpperCase()+t.slice(1)}function a(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 o(t){var e=t.toString(16);return 1==e.length?"0"+e:e}function h(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 u(t,e,i){return{x:e*t.a+i*t.c+0,y:e*t.b+i*t.d+0}}function l(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function c(t){return t instanceof y.Matrix||(t=new y.Matrix(t)),t}function f(t,e){t.cx=null==t.cx?e.bbox().cx:t.cx,t.cy=null==t.cy?e.bbox().cy:t.cy}function d(t){return t=t.replace(y.regex.whitespace,"").replace(y.regex.matrix,"").split(y.regex.matrixElements),l(y.utils.map(t,function(t){return parseFloat(t)}))}function p(t){for(var e=0,i=t.length,n="";e=0;e--)t.childNodes[e]instanceof SVGElement&&m(t.childNodes[e]);return y.adopt(t).id(y.eid(t.nodeName))}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 v(t){var e=t.toString().match(y.regex.reference);if(e)return e[1]}var y=this.SVG=function(t){if(y.supported)return t=new y.Doc(t),y.parser.draw||y.prepare(),t};if(y.ns="http://www.w3.org/2000/svg",y.xmlns="http://www.w3.org/2000/xmlns/",y.xlink="http://www.w3.org/1999/xlink",y.svgjs="http://svgjs.com/svgjs",y.supported=function(){return!!e.createElementNS&&!!e.createElementNS(y.ns,"svg").createSVGRect}(),!y.supported)return!1;y.did=1e3,y.eid=function(t){return"Svgjs"+s(t)+y.did++},y.create=function(t){var i=e.createElementNS(this.ns,t);return i.setAttribute("id",this.eid(t)),i},y.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];y.Set&&y.Set.inherit&&y.Set.inherit()},y.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,y.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&y.extend(e,t.extend),t.construct&&y.extend(t.parent||y.Container,t.construct),e},y.adopt=function(t){if(!t)return null;if(t.instance)return t.instance;var e;return e="svg"==t.nodeName?t.parentNode instanceof SVGElement?new y.Nested:new y.Doc:"linearGradient"==t.nodeName?new y.Gradient("linear"):"radialGradient"==t.nodeName?new y.Gradient("radial"):y[s(t.nodeName)]?new(y[s(t.nodeName)]):new y.Element(t),e.type=t.nodeName,e.node=t,t.instance=e,e instanceof y.Doc&&e.namespace().defs(),e.setData(JSON.parse(t.getAttribute("svgjs:data"))||{}),e},y.prepare=function(){var t=e.getElementsByTagName("body")[0],i=(t?new y.Doc(t):new y.Doc(e.documentElement).nested()).size(2,0);y.parser={body:t||e.documentElement,draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polyline().node,path:i.path().node,native:y.create("svg")}},y.parser={native:y.create("svg")},e.addEventListener("DOMContentLoaded",function(){y.parser.draw||y.prepare()},!1),y.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,matrix:/matrix\(|\)/g,matrixElements:/,*\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,negExp:/e\-/gi,comma:/,/g,hyphen:/\-/g,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,whitespaces:/\s+/,X:/X/g},y.utils={map:function(t,e){var i,n=t.length,r=[];for(i=0;i1?1:t,new y.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}}),y.Color.test=function(t){return t+="",y.regex.isHex.test(t)||y.regex.isRgb.test(t)},y.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},y.Color.isColor=function(t){return y.Color.isRgb(t)||y.Color.test(t)},y.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},y.extend(y.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--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.y;return this},bbox:function(){return y.parser.poly.setAttribute("points",this.toString()),y.parser.poly.getBBox()}}),y.PathArray=function(t,e){this.constructor.call(this,t,e||[["M",0,0]])},y.PathArray.prototype=new y.Array,y.extend(y.PathArray,{toString:function(){return p(this.value)},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n,r=this.value.length-1;r>=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},parse:function(t){if(t instanceof y.PathArray)return t.valueOf();var e,i,n,r,s,a,o=0,h=0,u={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7};if("string"==typeof t){for(t=t.replace(y.regex.negExp,"X").replace(y.regex.pathLetters," $& ").replace(y.regex.hyphen," -").replace(y.regex.comma," ").replace(y.regex.X,"e-").trim().split(y.regex.whitespaces),e=t.length;--e;)if(t[e].indexOf(".")!=t[e].lastIndexOf(".")){var l=t[e].split("."),c=[l.shift(),l.shift()].join(".");t.splice.apply(t,[e,1].concat(c,l.map(function(t){return"."+t})))}}else t=t.reduce(function(t,e){return[].concat.apply(t,e)},[]);var a=[];do{for(y.regex.isPathLetter.test(t[0])?(r=t[0],t.shift()):"M"==r?r="L":"m"==r&&(r="l"),s=[r.toUpperCase()],e=0;ei.x&&e>i.y&&t/,"").replace(/<\/svg>$/,"");i.innerHTML=""+t.replace(/\n/,"").replace(/<(\w+)([^<]+?)\/>/g,"<$1$2>")+"";for(var n=0,r=i.firstChild.childNodes.length;n":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return-Math.cos(t*Math.PI/2)+1}},y.morph=function(t){return function(e,i){return new y.MorphObj(e,i).at(t)}},y.Situation=y.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new y.Number(t.duration).valueOf(),this.delay=new y.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=!1,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),y.Delay=function(t){this.delay=new y.Number(t).valueOf()},y.FX=y.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._speed=1},extend:{animate:function(t,e,i){"object"==typeof t&&(e=t.ease,i=t.delay,t=t.duration);var n=new y.Situation({duration:t||1e3,delay:i||0,ease:y.easing[e||"-"]||e});return this.queue(n),this},delay:function(t){var t=new y.Delay(t);return this.queue(t)},target:function(t){return t&&t instanceof y.Element?(this._target=t,this):this._target},timeToPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},posToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.situation.start=+new Date+this.situation.delay,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations(),this.active=!0,this.startAnimFrame()),this},queue:function(t){return("function"==typeof t||t instanceof y.Situation||t instanceof y.Delay)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){if(this.situation&&this.situation.stop&&this.situation.stop(),this.situation=this.situations.shift(),this.situation){var t=function(){this.situation instanceof y.Situation?this.initAnimations().at(0):this.situation instanceof y.Delay?this.dequeue():this.situation.call(this)}.bind(this);this.situation.delay?setTimeout(function(){t()},this.situation.delay):t()}return this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(t in e.animations)"viewbox"==t?e.animations[t]=this.target().viewbox().morph(e.animations[t]):(e.animations[t].value="plot"==t?this.target().array().value:this.target()[t](),e.animations[t].value.value&&(e.animations[t].value=e.animations[t].value.value),e.animations[t].relative&&(e.animations[t].destination.value=e.animations[t].destination.value+e.animations[t].value));for(t in e.attrs)if(e.attrs[t]instanceof y.Color){var i=new y.Color(this.target().attr(t));e.attrs[t].r=i.r,e.attrs[t].g=i.g,e.attrs[t].b=i.b}else e.attrs[t].value=this.target().attr(t);for(t in e.styles)e.styles[t].value=this.target().style(t);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){return this.active||this.start(),e&&this.clearQueue(),this.active=!1,t&&this.situation&&(this.situation.loop=!1,this.situation.loops%2==0&&this.situation.reversing&&(this.situation.reversed=!0),this.at(1)),this.stopAnimFrame(),clearTimeout(this.timeout),this.clearCurrent()},reset:function(){if(this.situation){var t=this.situation;this.stop(),this.situation=t,this.at(0)}return this},finish:function(){for(this.stop(!0,!1);this.dequeue().situation&&this.stop(!0,!1););return this.clearQueue().clearCurrent(),this},at:function(t){var e=this.situation.duration/this._speed;return this.pos=t,this.situation.start=+new Date-t*e,this.situation.finish=this.situation.start+e,this.step(!0)},speed:function(t){return 0===t?this.pause():t?(this._speed=t,this.at(this.situation.reversed?1-this.pos:this.pos)):this._speed},loop:function(t,e){var i=this.last();return i.loop=i.loops=t||!0,e&&(i.reversing=!0),this},pause:function(){return this.paused=!0,this.stopAnimFrame(),clearTimeout(this.timeout),this},play:function(){return this.paused?(this.paused=!1,this.at(this.pos)):this},reverse:function(t){var e=this.last();return"undefined"==typeof t?e.reversed=!e.reversed:e.reversed=t,this},progress:function(t){return t?this.situation.ease(this.pos):this.pos},after:function(t){var e=this.last(),i=function i(n){n.detail.situation==e&&(t.call(this,e),this.off("finished.fx",i))};return this.target().on("finished.fx",i),this},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,y.morph(i.detail.pos),i.detail.eased,e)};return this.target().off("during.fx",i).on("during.fx",i),this.after(function(){this.off("during.fx",i)})},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},duringAll:function(t){var e=function(e){t.call(this,e.detail.pos,y.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)})},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,setTimeout(function(){this.start()}.bind(this),0),this},step:function(t){if(t||(this.pos=this.timeToPos(+new Date)),this.pos>=1&&(this.situation.loop===!0||"number"==typeof this.situation.loop&&--this.situation.loop))return this.situation.reversing&&(this.situation.reversed=!this.situation.reversed),this.at(this.pos-1);this.situation.reversed&&(this.pos=1-this.pos),this.pos>1&&(this.pos=1),this.pos<0&&(this.pos=0);var e=this.situation.ease(this.pos);for(var i in this.situation.once)i>this.lastPos&&i<=e&&(this.situation.once[i].call(this.target(),this.pos,e),delete this.situation.once[i]);return this.active&&this.target().fire("during",{pos:this.pos,eased:e,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.target().off(".fx"),this.active=!1),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=e,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 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 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 t.at?t.at(r.ease(i.pos),i.pos):t}),n.style.apply(n,e);if(r.transforms.length){e=r.initialTransformation;for(t in r.transforms){var s=r.transforms[t];s instanceof y.Matrix?e=s.relative?e.multiply(s.at(r.ease(this.pos))):e.morph(s).at(r.ease(this.pos)):(s.relative||s.undo(e.extract()),e=e.multiply(s.at(r.ease(this.pos))))}n.matrix(e)}return this},once:function(t,e,i){return i||(t=this.situation.ease(t)),this.situation.once[t]=e,this}},parent:y.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new y.FX(this))).animate(t,e,i)},delay:function(t){return(this.fx||(this.fx=new y.FX(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}}}),y.MorphObj=y.invent({create:function(t,e){return y.Color.isColor(e)?new y.Color(t).morph(e):y.regex.numberAndUnit.test(e)?new y.Number(t).morph(e):(this.value=0,void(this.destination=e))},extend:{at:function(t,e){return e<1?this.value:this.destination},valueOf:function(){return this.value}}}),y.extend(y.FX,{attr:function(t,e,i){if("object"==typeof t)for(var n in t)this.attr(n,t[n]);else this.add(t,new y.MorphObj(null,e),"attrs");return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.add(t,new y.MorphObj(null,e),"styles");return this},x:function(t,e){if(this.target()instanceof y.G)return this.transform({x:t},e),this;var i=(new y.Number).morph(t);return i.relative=e,this.add("x",i)},y:function(t,e){if(this.target()instanceof y.G)return this.transform({y:t},e),this;var i=(new y.Number).morph(t);return i.relative=e,this.add("y",i)},cx:function(t){return this.add("cx",(new y.Number).morph(t))},cy:function(t){return this.add("cy",(new y.Number).morph(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 y.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 y.Number).morph(t)).add("height",(new y.Number).morph(e))}return this},plot:function(t){return this.add("plot",this.target().array().morph(t))},leading:function(t){return this.target().leading?this.add("leading",(new y.Number).morph(t)):this},viewbox:function(t,e,i,n){return this.target()instanceof y.Container&&this.add("viewbox",new y.ViewBox(t,e,i,n)),this},update:function(t){if(this.target()instanceof y.Stop){if("number"==typeof t||t instanceof y.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}}),y.BBox=y.invent({create:function(t){if(t){var i;try{if(!e.documentElement.contains(t.node))throw new Exception("Element not in the dom");i=t.node.getBBox()}catch(e){if(t instanceof y.Shape){var n=t.clone(y.parser.draw).show();i=n.bbox(),n.remove()}else i={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=i.x,this.y=i.y,this.width=i.width,this.height=i.height}x(this)},parent:y.Element,construct:{bbox:function(){return new y.BBox(this)}}}),y.TBox=y.invent({create:function(t){if(t){var e=t.ctm().extract(),i=t.bbox();this.width=i.width*e.scaleX,this.height=i.height*e.scaleY,this.x=i.x+e.x,this.y=i.y+e.y}x(this)},parent:y.Element,construct:{tbox:function(){return new y.TBox(this)}}}),y.RBox=y.invent({create:function(e){if(e){var i=e.doc().parent(),n=e.node.getBoundingClientRect(),r=1;for(this.x=n.left,this.y=n.top,this.x-=i.offsetLeft,this.y-=i.offsetTop;i=i.offsetParent;)this.x-=i.offsetLeft,this.y-=i.offsetTop;for(i=e;i.parent&&(i=i.parent());)i.viewbox&&(r*=i.viewbox().zoom,this.x-=i.x()||0,this.y-=i.y()||0);this.width=n.width/=r,this.height=n.height/=r}x(this),this.x+=t.pageXOffset,this.y+=t.pageYOffset},parent:y.Element,construct:{rbox:function(){return new y.RBox(this)}}}),[y.BBox,y.TBox,y.RBox].forEach(function(t){y.extend(t,{merge:function(e){var i=new t;return i.x=Math.min(this.x,e.x),i.y=Math.min(this.y,e.y),i.width=Math.max(this.x+this.width,e.x+e.width)-i.x,i.height=Math.max(this.y+this.height,e.y+e.height)-i.y,x(i)}})}),y.Matrix=y.invent({create:function(t){var e,i=l([1,0,0,1,0,0]);for(t=t instanceof y.Element?t.matrixify():"string"==typeof t?d(t):6==arguments.length?l([].slice.call(arguments)):"object"==typeof t?t:i,e=w.length-1;e>=0;--e)this[w[e]]=t&&"number"==typeof t[w[e]]?t[w[e]]:i[w[e]]},extend:{extract:function(){var t=u(this,0,1),e=u(this,1,0),i=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(i*Math.PI/180)+this.f*Math.sin(i*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(i*Math.PI/180)+this.e*Math.sin(-i*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),skewX:-i,skewY:180/Math.PI*Math.atan2(e.y,e.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:i,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new y.Matrix(this)}},clone:function(){return new y.Matrix(this)},morph:function(t){return this.destination=new y.Matrix(t),this},at:function(t){if(!this.destination)return this;var e=new y.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});if(this.param&&this.param.to){var i={rotation:this.param.from.rotation+(this.param.to.rotation-this.param.from.rotation)*t,cx:this.param.from.cx,cy:this.param.from.cy};e=e.rotate((this.param.to.rotation-2*this.param.from.rotation)*t,i.cx,i.cy),e.param=i}return e},multiply:function(t){return new y.Matrix(this.native().multiply(c(t).native()))},inverse:function(){return new y.Matrix(this.native().inverse())},translate:function(t,e){return new y.Matrix(this.native().translate(t||0,e||0))},scale:function(t,e,i,n){return 1!=arguments.length&&3!=arguments.length||(e=t),3==arguments.length&&(n=i,i=e),this.around(i,n,new y.Matrix(t,0,0,e,0,0))},rotate:function(t,e,i){return t=y.utils.radians(t),this.around(e,i,new y.Matrix(Math.cos(t),Math.sin(t),(-Math.sin(t)),Math.cos(t),0,0))},flip:function(t,e){return"x"==t?this.scale(-1,1,e,0):this.scale(1,-1,0,e)},skew:function(t,e,i,n){return this.around(i,n,this.native().skewX(t||0).skewY(e||0))},skewX:function(t,e,i){return this.around(e,i,this.native().skewX(t||0))},skewY:function(t,e,i){return this.around(e,i,this.native().skewY(t||0))},around:function(t,e,i){return this.multiply(new y.Matrix(1,0,0,1,t||0,e||0)).multiply(i).multiply(new y.Matrix(1,0,0,1,-t||0,-e||0))},native:function(){for(var t=y.parser.native.createSVGMatrix(),e=w.length-1;e>=0;e--)t[w[e]]=this[w[e]];return t},toString:function(){return"matrix("+this.a+","+this.b+","+this.c+","+this.d+","+this.e+","+this.f+")"}},parent:y.Element,construct:{ctm:function(){return new y.Matrix(this.node.getCTM())},screenCTM:function(){return new y.Matrix(this.node.getScreenCTM())}}}),y.Point=y.invent({create:function(t,e){var i,n={x:0,y:0};i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"==typeof t?{x:t.x,y:t.y}:null!=e?{x:t,y:e}:n,this.x=i.x,this.y=i.y},extend:{clone:function(){return new y.Point(this)},morph:function(t){return this.destination=new y.Point(t),this},at:function(t){if(!this.destination)return this;var e=new y.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t});return e},native:function(){var t=y.parser.native.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new y.Point(this.native().matrixTransform(t.native()))}}}),y.extend(y.Element,{point:function(t,e){return new y.Point(t,e).transform(this.screenCTM().inverse())}}),y.extend(y.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]=y.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?y.defaults.attrs[t]:y.regex.isNumber.test(e)?parseFloat(e):e;"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),"fill"!=t&&"stroke"!=t||(y.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof y.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new y.Number(e):y.Color.isColor(e)?e=new y.Color(e):Array.isArray(e)?e=new y.Array(e):e instanceof y.Matrix&&e.param&&(this.param=e.param),"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}}),y.extend(y.Element,{transform:function(t,e){ -var i,n=this;if("object"!=typeof t)return i=new y.Matrix(n).extract(),"string"==typeof t?i[t]:i;if(i=new y.Matrix(n),e=!!e||!!t.relative,null!=t.a)i=e?i.multiply(new y.Matrix(t)):new y.Matrix(t);else if(null!=t.rotation)f(t,n),i=e?i.rotate(t.rotation,t.cx,t.cy):i.rotate(t.rotation-i.extract().rotation,t.cx,t.cy);else if(null!=t.scale||null!=t.scaleX||null!=t.scaleY){if(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,!e){var r=i.extract();t.scaleX=1*t.scaleX/r.scaleX,t.scaleY=1*t.scaleY/r.scaleY}i=i.scale(t.scaleX,t.scaleY,t.cx,t.cy)}else if(null!=t.skewX||null!=t.skewY){if(f(t,n),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,!e){var r=i.extract();i=i.multiply((new y.Matrix).skew(r.skewX,r.skewY,t.cx,t.cy).inverse())}i=i.skew(t.skewX,t.skewY,t.cx,t.cy)}else t.flip?i=i.flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset):null==t.x&&null==t.y||(e?i=i.translate(t.x,t.y):(null!=t.x&&(i.e=t.x),null!=t.y&&(i.f=t.y)));return this.attr("transform",i)}}),y.extend(y.FX,{transform:function(t,e){var i,n=this.target();return"object"!=typeof t?(i=new y.Matrix(n).extract(),"string"==typeof t?i[t]:i):(e=!!e||!!t.relative,null!=t.a?i=new y.Matrix(t):null!=t.rotation?(f(t,n),i=new y.Rotate(t.rotation,t.cx,t.cy)):null!=t.scale||null!=t.scaleX||null!=t.scaleY?(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,i=new y.Scale(t.scaleX,t.scaleY,t.cx,t.cy)):null!=t.skewX||null!=t.skewY?(f(t,n),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,i=new y.Skew(t.skewX,t.skewY,t.cx,t.cy)):t.flip?i=(new y.Matrix).morph((new y.Matrix).flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset)):null==t.x&&null==t.y||(i=new y.Translate(t.x,t.y)),i?(i.relative=e,this.last().transforms.push(i),setTimeout(function(){this.start()}.bind(this),0),this):this)}}),y.extend(y.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){var t=(this.attr("transform")||"").split(/\)\s*,?\s*/).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(y.regex.matrixElements).map(function(t){return parseFloat(t)})]}).reduce(function(t,e){return"matrix"==e[0]?t.multiply(l(e[1])):t[e[0]].apply(t,e[1])},new y.Matrix);return t},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.rect(1,1),n=i.screenCTM().inverse();return i.remove(),this.addTo(t).untransform().transform(n.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),y.Transformation=y.invent({create:function(t,e){if(arguments.length>1&&"boolean"!=typeof e)return this.create([].slice.call(arguments));if("object"==typeof t)for(var i=0,n=this.arguments.length;i=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return y.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;in/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)}else t="string"==typeof t?t.match(f).map(function(t){return parseFloat(t)}):Array.isArray(t)?t:"object"==typeof t?[t.x,t.y,t.width,t.height]:4==arguments.length?[].slice.call(arguments):u,this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3]},extend:{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height},morph:function(t){var t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments);return this.destination=new y.ViewBox(t),this},at:function(t){return this.destination?new y.ViewBox([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:y.Container,construct:{viewbox:function(t){return 0==arguments.length?new y.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){y.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),y.listeners=[],y.handlerMap=[],y.listenerId=0,y.on=function(t,e,i,n){var r=i.bind(n||t.instance||t),s=(y.handlerMap.indexOf(t)+1||y.handlerMap.push(t))-1,a=e.split(".")[0],o=e.split(".")[1]||"*";y.listeners[s]=y.listeners[s]||{},y.listeners[s][a]=y.listeners[s][a]||{},y.listeners[s][a][o]=y.listeners[s][a][o]||{},i._svgjsListenerId||(i._svgjsListenerId=++y.listenerId),y.listeners[s][a][o][i._svgjsListenerId]=r,t.addEventListener(a,r,!1)},y.off=function(t,e,i){var n=y.handlerMap.indexOf(t),r=e&&e.split(".")[0],s=e&&e.split(".")[1];if(n!=-1)if(i){if("function"==typeof i&&(i=i._svgjsListenerId),!i)return;y.listeners[n][r]&&y.listeners[n][r][s||"*"]&&(t.removeEventListener(r,y.listeners[n][r][s||"*"][i],!1),delete y.listeners[n][r][s||"*"][i])}else if(s&&r){if(y.listeners[n][r]&&y.listeners[n][r][s]){for(i in y.listeners[n][r][s])y.off(t,[r,s].join("."),i);delete y.listeners[n][r][s]}}else if(s)for(e in y.listeners[n])for(namespace in y.listeners[n][e])s===namespace&&y.off(t,[e,s].join("."));else if(r){if(y.listeners[n][r]){for(namespace in y.listeners[n][r])y.off(t,[r,namespace].join("."));delete y.listeners[n][r]}}else{for(e in y.listeners[n])y.off(t,e);delete y.listeners[n]}},y.extend(y.Element,{on:function(t,e,i){return y.on(this.node,t,e,i),this},off:function(t,e){return y.off(this.node,t,e),this},fire:function(t,e){return t instanceof Event?this.node.dispatchEvent(t):this.node.dispatchEvent(new b(t,{detail:e})),this}}),y.Defs=y.invent({create:"defs",inherit:y.Container}),y.G=y.invent({create:"g",inherit:y.Container,extend:{x:function(t){return null==t?this.transform("x"):this.transform({x:t-this.x()},!0)},y:function(t){return null==t?this.transform("y"):this.transform({y:t-this.y()},!0)},cx:function(t){return null==t?this.gbox().cx:this.x(t-this.gbox().width/2)},cy:function(t){return null==t?this.gbox().cy:this.y(t-this.gbox().height/2)},gbox:function(){var t=this.bbox(),e=this.transform();return t.x+=e.x,t.x2+=e.x,t.cx+=e.x,t.y+=e.y,t.y2+=e.y,t.cy+=e.y,t}},construct:{group:function(){return this.put(new y.G)}}}),y.extend(y.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,e=this.parent();return e.removeElement(this).add(this,t),e instanceof y.Doc&&e.node.appendChild(e.defs().node),this},backward:function(){var t=this.position();return t>0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof y.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}}),y.Mask=y.invent({create:function(){this.constructor.call(this,y.create("mask")),this.targets=[]},inherit:y.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return this.targets=[],this.parent().removeElement(this),this}},construct:{mask:function(){return this.defs().put(new y.Mask)}}}),y.extend(y.Element,{maskWith:function(t){return this.masker=t instanceof y.Mask?t:this.parent().mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),y.ClipPath=y.invent({create:function(){this.constructor.call(this,y.create("clipPath")),this.targets=[]},inherit:y.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return this.targets=[],this.parent().removeElement(this),this}},construct:{clip:function(){return this.defs().put(new y.ClipPath)}}}),y.extend(y.Element,{clipWith:function(t){return this.clipper=t instanceof y.ClipPath?t:this.parent().clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),y.Gradient=y.invent({create:function(t){this.constructor.call(this,y.create(t+"Gradient")),this.type=t},inherit:y.Container,extend:{at:function(t,e,i){return this.put(new y.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="gradientTransform"),y.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),y.extend(y.Gradient,y.FX,{from:function(t,e){return"radial"==(this._target||this).type?this.attr({fx:new y.Number(t),fy:new y.Number(e)}):this.attr({x1:new y.Number(t),y1:new y.Number(e)})},to:function(t,e){return"radial"==(this._target||this).type?this.attr({cx:new y.Number(t),cy:new y.Number(e)}):this.attr({x2:new y.Number(t),y2:new y.Number(e)})}}),y.extend(y.Defs,{gradient:function(t,e){return this.put(new y.Gradient(t)).update(e)}}),y.Stop=y.invent({create:"stop",inherit:y.Element,extend:{update:function(t){return("number"==typeof t||t instanceof y.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 y.Number(t.offset)),this}}}),y.Pattern=y.invent({create:"pattern",inherit:y.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="patternTransform"),y.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),y.extend(y.Defs,{pattern:function(t,e,i){return this.put(new y.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),y.Doc=y.invent({create:function(t){t&&(t="string"==typeof t?e.getElementById(t):t,"svg"==t.nodeName?this.constructor.call(this,t):(this.constructor.call(this,y.create("svg")),t.appendChild(this.node),this.size("100%","100%")),this.namespace().defs())},inherit:y.Container,extend:{namespace:function(){return this.attr({xmlns:y.ns,version:"1.1"}).attr("xmlns:xlink",y.xlink,y.xmlns).attr("xmlns:svgjs",y.svgjs,y.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=y.adopt(t):this._defs=new y.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return"#document"==this.node.parentNode.nodeName?null:this.node.parentNode},spof:function(t){var e=this.node.getScreenCTM();return e&&this.style("left",-e.e%1+"px").style("top",-e.f%1+"px"),this},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this}}}),y.Shape=y.invent({create:function(t){this.constructor.call(this,t)},inherit:y.Element}),y.Bare=y.invent({create:function(t,e){if(this.constructor.call(this,y.create(t)),e)for(var i in e.prototype)"function"==typeof e.prototype[i]&&(this[i]=e.prototype[i])},inherit:y.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),y.extend(y.Parent,{element:function(t,e){return this.put(new y.Bare(t,e))},symbol:function(){return this.defs().element("symbol",y.Container)}}),y.Use=y.invent({create:"use",inherit:y.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,y.xlink)}},construct:{use:function(t,e){return this.put(new y.Use).element(t,e)}}}),y.Rect=y.invent({create:"rect",inherit:y.Shape,construct:{rect:function(t,e){return this.put(new y.Rect).size(t,e)}}}),y.Circle=y.invent({create:"circle",inherit:y.Shape,construct:{circle:function(t){return this.put(new y.Circle).rx(new y.Number(t).divide(2)).move(0,0)}}}),y.extend(y.Circle,y.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),y.Ellipse=y.invent({create:"ellipse",inherit:y.Shape,construct:{ellipse:function(t,e){return this.put(new y.Ellipse).size(t,e).move(0,0)}}}),y.extend(y.Ellipse,y.Rect,y.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),y.extend(y.Circle,y.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 y.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new y.Number(t).divide(2))},size:function(t,e){var i=h(this,t,e);return this.rx(new y.Number(i.width).divide(2)).ry(new y.Number(i.height).divide(2))}}),y.Line=y.invent({create:"line",inherit:y.Shape,extend:{array:function(){return new y.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return t="undefined"!=typeof e?{x1:t,y1:e,x2:i,y2:n}:new y.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=h(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return this.put(new y.Line).plot(t,e,i,n)}}}),y.Polyline=y.invent({create:"polyline",inherit:y.Shape,construct:{polyline:function(t){return this.put(new y.Polyline).plot(t)}}}),y.Polygon=y.invent({create:"polygon",inherit:y.Shape,construct:{polygon:function(t){return this.put(new y.Polygon).plot(t)}}}),y.extend(y.Polyline,y.Polygon,{array:function(){return this._array||(this._array=new y.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new y.PointArray(t))},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=h(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),y.extend(y.Line,y.Polyline,y.Polygon,{morphArray:y.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)}}),y.Path=y.invent({create:"path",inherit:y.Shape,extend:{morphArray:y.PathArray,array:function(){return this._array||(this._array=new y.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new y.PathArray(t))},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=h(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 y.Path).plot(t)}}}),y.Image=y.invent({create:"image",inherit:y.Shape,extend:{load:function(t){if(!t)return this;var i=this,n=e.createElement("img");return n.onload=function(){var e=i.parent(y.Pattern);null!==e&&(0==i.width()&&0==i.height()&&i.size(n.width,n.height),e&&0==e.width()&&0==e.height()&&e.size(i.width(),i.height()),"function"==typeof i._loaded&&i._loaded.call(i,{width:n.width,height:n.height,ratio:n.width/n.height,url:t}))},n.onerror=function(t){"function"==typeof i._error&&i._error.call(i,t)},this.attr("href",n.src=this.src=t,y.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new y.Image).load(t).size(e||0,i||e||0)}}}),y.Text=y.invent({create:function(){this.constructor.call(this,y.create("text")),this.dom.leading=new y.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",y.defaults.attrs["font-family"])},inherit:y.Shape,extend:{x:function(t){return null==t?this.attr("x"):(this.textPath||this.lines().each(function(){this.dom.newLined&&this.x(t)}),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("undefined"==typeof t){for(var t="",e=this.node.childNodes,i=0,n=e.length;i=0;e--)null!=i[g[t][e]]&&this.attr(g.prefix(t,g[t][e]),i[g[t][e]]);return this},y.extend(y.Element,y.FX,i)}),y.extend(y.Element,y.FX,{rotate:function(t,e,i){return this.transform({rotation:t,cx:e,cy:i})},skew:function(t,e,i,n){return this.transform({skewX:t,skewY:e,cx:i,cy:n})},scale:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({scale:t,cx:e,cy:i}):this.transform({scaleX:t,scaleY:e,cx:i,cy:n})},translate:function(t,e){return this.transform({x:t,y:e})},flip:function(t,e){return this.transform({flip:t,offset:e})},matrix:function(t){return this.attr("transform",new y.Matrix(t))},opacity:function(t){return this.attr("opacity",t)},dx:function(t){return this.x((this instanceof y.FX?0:this.x())+t,!0)},dy:function(t){return this.y((this instanceof y.FX?0:this.y())+t,!0)},dmove:function(t,e){return this.dx(t).dy(e)}}),y.extend(y.Rect,y.Ellipse,y.Circle,y.Gradient,y.FX,{radius:function(t,e){var i=(this._target||this).type;return"radial"==i||"circle"==i?this.attr("r",new y.Number(t)):this.rx(t).ry(null==e?t:e)}}),y.extend(y.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),y.extend(y.Parent,y.Text,y.FX,{font:function(t){for(var e in t)"leading"==e?this.leading(t[e]):"anchor"==e?this.attr("text-anchor",t[e]):"size"==e||"family"==e||"weight"==e||"stretch"==e||"variant"==e||"style"==e?this.attr("font-"+e,t[e]):this.attr(e,t[e]);return this}}),y.Set=y.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;t-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new y.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new y.Set(t)}}}),y.FX.Set=y.invent({create:function(t){this.set=t}}),y.Set.inherit=function(){var t,e=[];for(var t in y.Shape.prototype)"function"==typeof y.Shape.prototype[t]&&"function"!=typeof y.Set.prototype[t]&&e.push(t);e.forEach(function(t){y.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),y.get=function(t){var i=e.getElementById(v(t)||t);return y.adopt(i)},y.select=function(t,i){return new y.Set(y.utils.map((i||e).querySelectorAll(t),function(t){return y.adopt(t)}))},y.extend(y.Parent,{select:function(t){return y.select(t,this.node)}});var w="abcdef".split("");if("function"!=typeof b){var b=function(t,i){i=i||{bubbles:!1,cancelable:!1,detail:void 0};var n=e.createEvent("CustomEvent");return n.initCustomEvent(t,i.bubbles,i.cancelable,i.detail),n};b.prototype=t.Event.prototype,t.CustomEvent=b}return function(e){for(var i=0,n=["moz","webkit"],r=0;r=0;e--)t.childNodes[e]instanceof SVGElement&&m(t.childNodes[e]);return y.adopt(t).id(y.eid(t.nodeName))}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 v(t){var e=t.toString().match(y.regex.reference);if(e)return e[1]}var y=this.SVG=function(t){if(y.supported)return t=new y.Doc(t),y.parser.draw||y.prepare(),t};if(y.ns="http://www.w3.org/2000/svg",y.xmlns="http://www.w3.org/2000/xmlns/",y.xlink="http://www.w3.org/1999/xlink",y.svgjs="http://svgjs.com/svgjs",y.supported=function(){return!!e.createElementNS&&!!e.createElementNS(y.ns,"svg").createSVGRect}(),!y.supported)return!1;y.did=1e3,y.eid=function(t){return"Svgjs"+r(t)+y.did++},y.create=function(t){var i=e.createElementNS(this.ns,t);return i.setAttribute("id",this.eid(t)),i},y.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];y.Set&&y.Set.inherit&&y.Set.inherit()},y.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,y.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&y.extend(e,t.extend),t.construct&&y.extend(t.parent||y.Container,t.construct),e},y.adopt=function(t){if(!t)return null;if(t.instance)return t.instance;var e;return e="svg"==t.nodeName?t.parentNode instanceof SVGElement?new y.Nested:new y.Doc:"linearGradient"==t.nodeName?new y.Gradient("linear"):"radialGradient"==t.nodeName?new y.Gradient("radial"):y[r(t.nodeName)]?new(y[r(t.nodeName)]):new y.Element(t),e.type=t.nodeName,e.node=t,t.instance=e,e instanceof y.Doc&&e.namespace().defs(),e.setData(JSON.parse(t.getAttribute("svgjs:data"))||{}),e},y.prepare=function(){var t=e.getElementsByTagName("body")[0],i=(t?new y.Doc(t):new y.Doc(e.documentElement).nested()).size(2,0);y.parser={body:t||e.documentElement,draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polyline().node,path:i.path().node,native:y.create("svg")}},y.parser={native:y.create("svg")},e.addEventListener("DOMContentLoaded",function(){y.parser.draw||y.prepare()},!1),y.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,matrix:/matrix\(|\)/g,matrixElements:/,*\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,negExp:/e\-/gi,comma:/,/g,hyphen:/\-/g,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,whitespaces:/\s+/,X:/X/g},y.utils={map:function(t,e){var i,n=t.length,s=[];for(i=0;i1?1:t,new y.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}}),y.Color.test=function(t){return t+="",y.regex.isHex.test(t)||y.regex.isRgb.test(t)},y.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},y.Color.isColor=function(t){return y.Color.isRgb(t)||y.Color.test(t)},y.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},y.extend(y.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--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.y;return this},bbox:function(){return y.parser.poly.setAttribute("points",this.toString()),y.parser.poly.getBBox()}}),y.PathArray=function(t,e){this.constructor.call(this,t,e||[["M",0,0]])},y.PathArray.prototype=new y.Array,y.extend(y.PathArray,{toString:function(){return p(this.value)},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n,s=this.value.length-1;s>=0;s--)n=this.value[s][0],"M"==n||"L"==n||"T"==n?(this.value[s][1]+=t,this.value[s][2]+=e):"H"==n?this.value[s][1]+=t:"V"==n?this.value[s][1]+=e:"C"==n||"S"==n||"Q"==n?(this.value[s][1]+=t,this.value[s][2]+=e,this.value[s][3]+=t,this.value[s][4]+=e,"C"==n&&(this.value[s][5]+=t,this.value[s][6]+=e)):"A"==n&&(this.value[s][6]+=t,this.value[s][7]+=e);return this},size:function(t,e){var i,n,s=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]-s.x)*t/s.width+s.x,this.value[i][2]=(this.value[i][2]-s.y)*e/s.height+s.y):"H"==n?this.value[i][1]=(this.value[i][1]-s.x)*t/s.width+s.x:"V"==n?this.value[i][1]=(this.value[i][1]-s.y)*e/s.height+s.y:"C"==n||"S"==n||"Q"==n?(this.value[i][1]=(this.value[i][1]-s.x)*t/s.width+s.x,this.value[i][2]=(this.value[i][2]-s.y)*e/s.height+s.y,this.value[i][3]=(this.value[i][3]-s.x)*t/s.width+s.x,this.value[i][4]=(this.value[i][4]-s.y)*e/s.height+s.y,"C"==n&&(this.value[i][5]=(this.value[i][5]-s.x)*t/s.width+s.x,this.value[i][6]=(this.value[i][6]-s.y)*e/s.height+s.y)):"A"==n&&(this.value[i][1]=this.value[i][1]*t/s.width,this.value[i][2]=this.value[i][2]*e/s.height,this.value[i][6]=(this.value[i][6]-s.x)*t/s.width+s.x,this.value[i][7]=(this.value[i][7]-s.y)*e/s.height+s.y);return this},parse:function(t){if(t instanceof y.PathArray)return t.valueOf();var e,i,n,s,r,a,o=0,h=0,u={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7};if("string"==typeof t){for(t=t.replace(y.regex.negExp,"X").replace(y.regex.pathLetters," $& ").replace(y.regex.hyphen," -").replace(y.regex.comma," ").replace(y.regex.X,"e-").trim().split(y.regex.whitespaces),e=t.length;--e;)if(t[e].indexOf(".")!=t[e].lastIndexOf(".")){var l=t[e].split("."),c=[l.shift(),l.shift()].join(".");t.splice.apply(t,[e,1].concat(c,l.map(function(t){return"."+t})))}}else t=t.reduce(function(t,e){return[].concat.apply(t,e)},[]);var a=[];do{for(y.regex.isPathLetter.test(t[0])?(s=t[0],t.shift()):"M"==s?s="L":"m"==s&&(s="l"),r=[s.toUpperCase()],e=0;ei.x&&e>i.y&&t/,"").replace(/<\/svg>$/,"");i.innerHTML=""+t.replace(/\n/,"").replace(/<(\w+)([^<]+?)\/>/g,"<$1$2>")+"";for(var n=0,s=i.firstChild.childNodes.length;n":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return-Math.cos(t*Math.PI/2)+1}},y.morph=function(t){return function(e,i){return new y.MorphObj(e,i).at(t)}},y.Situation=y.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new y.Number(t.duration).valueOf(),this.delay=new y.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={}}}),y.Delay=function(t){this.delay=new y.Number(t).valueOf()},y.FX=y.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 y.Situation({duration:t||1e3,delay:i||0,ease:y.easing[e||"-"]||e});return this.queue(n),this},delay:function(t){var t=new y.Delay(t);return this.queue(t)},target:function(t){return t&&t instanceof y.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=requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations(),this.active=!0,this.startAnimFrame()),this},queue:function(t){return("function"==typeof t||t instanceof y.Situation||t instanceof y.Delay)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){if(this.situation&&this.situation.stop&&this.situation.stop(),this.situation=this.situations.shift(),this.situation){var t=function(){this.situation instanceof y.Situation?this.initAnimations().atStart():this.situation instanceof y.Delay?this.dequeue():this.situation.call(this)}.bind(this);this.situation.delay?setTimeout(function(){t()},this.situation.delay):t()}return this},initAnimations:function(){var t,e=this.situation;if(e.init)return this;for(t in e.animations)"viewbox"==t?e.animations[t]=this.target().viewbox().morph(e.animations[t]):(e.animations[t].value="plot"==t?this.target().array().value:this.target()[t](),e.animations[t].value.value&&(e.animations[t].value=e.animations[t].value.value),e.animations[t].relative&&(e.animations[t].destination.value=e.animations[t].destination.value+e.animations[t].value));for(t in e.attrs)if(e.attrs[t]instanceof y.Color){var i=new y.Color(this.target().attr(t));e.attrs[t].r=i.r,e.attrs[t].g=i.g,e.attrs[t].b=i.b}else e.attrs[t].value=this.target().attr(t);for(t in e.styles)e.styles[t].value=this.target().style(t);return e.initialTransformation=this.target().matrixify(),e.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){return this.active||this.start(),e&&this.clearQueue(),this.active=!1,t&&this.situation&&this.atEnd(),this.stopAnimFrame(),clearTimeout(this.timeout),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 this.situation.loops===!0?this.at(this.situation.loop+1,!0):"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(),clearTimeout(this.timeout),this},play:function(){return this.paused?(this.paused=!1,this.at(this.absPos,!0)):this},reverse:function(t){var e=this.last();return"undefined"==typeof t?e.reversed=!e.reversed:e.reversed=t,this},progress:function(t){return t?this.situation.ease(this.pos):this.pos},after:function(t){var e=this.last(),i=function i(n){n.detail.situation==e&&(t.call(this,e),this.off("finished.fx",i))};return this.target().on("finished.fx",i),this},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,y.morph(i.detail.pos),i.detail.eased,e)};return this.target().off("during.fx",i).on("during.fx",i),this.after(function(){this.off("during.fx",i)})},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},duringAll:function(t){var e=function(e){t.call(this,e.detail.pos,y.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)})},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,setTimeout(function(){this.start()}.bind(this),0),this},step:function(t){if(t||(this.absPos=this.timeToAbsPos(+new Date)),this.situation.loops!==!1){var e,i,n;e=Math.max(this.absPos,0),i=Math.floor(e),this.situation.loops===!0||ithis.lastPos&&r<=s&&(this.situation.once[r].call(this.target(),this.pos,s),delete this.situation.once[r]);return this.active&&this.target().fire("during",{pos:this.pos,eased:s,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.target().off(".fx"),this.active=!1),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=s,this):this},eachAt:function(){var t,e,i=this,n=this.target(),s=this.situation;for(t in s.animations)e=[].concat(s.animations[t]).map(function(t){return t.at?t.at(s.ease(i.pos),i.pos):t}),n[t].apply(n,e);for(t in s.attrs)e=[t].concat(s.attrs[t]).map(function(t){return t.at?t.at(s.ease(i.pos),i.pos):t}),n.attr.apply(n,e);for(t in s.styles)e=[t].concat(s.styles[t]).map(function(t){return t.at?t.at(s.ease(i.pos),i.pos):t}),n.style.apply(n,e);if(s.transforms.length){e=s.initialTransformation;for(t in s.transforms){var r=s.transforms[t];r instanceof y.Matrix?e=r.relative?e.multiply(r.at(s.ease(this.pos))):e.morph(r).at(s.ease(this.pos)):(r.relative||r.undo(e.extract()),e=e.multiply(r.at(s.ease(this.pos))))}n.matrix(e)}return this},once:function(t,e,i){return i||(t=this.situation.ease(t)),this.situation.once[t]=e,this}},parent:y.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new y.FX(this))).animate(t,e,i)},delay:function(t){return(this.fx||(this.fx=new y.FX(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}}}),y.MorphObj=y.invent({create:function(t,e){return y.Color.isColor(e)?new y.Color(t).morph(e):y.regex.numberAndUnit.test(e)?new y.Number(t).morph(e):(this.value=0,void(this.destination=e))},extend:{at:function(t,e){return e<1?this.value:this.destination},valueOf:function(){return this.value}}}),y.extend(y.FX,{attr:function(t,e,i){if("object"==typeof t)for(var n in t)this.attr(n,t[n]);else this.add(t,new y.MorphObj(null,e),"attrs");return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.add(t,new y.MorphObj(null,e),"styles");return this},x:function(t,e){if(this.target()instanceof y.G)return this.transform({x:t},e),this;var i=(new y.Number).morph(t);return i.relative=e,this.add("x",i)},y:function(t,e){if(this.target()instanceof y.G)return this.transform({y:t},e),this;var i=(new y.Number).morph(t);return i.relative=e,this.add("y",i)},cx:function(t){return this.add("cx",(new y.Number).morph(t))},cy:function(t){return this.add("cy",(new y.Number).morph(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 y.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 y.Number).morph(t)).add("height",(new y.Number).morph(e))}return this},plot:function(t){return this.add("plot",this.target().array().morph(t))},leading:function(t){return this.target().leading?this.add("leading",(new y.Number).morph(t)):this},viewbox:function(t,e,i,n){return this.target()instanceof y.Container&&this.add("viewbox",new y.ViewBox(t,e,i,n)),this},update:function(t){if(this.target()instanceof y.Stop){if("number"==typeof t||t instanceof y.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}}),y.BBox=y.invent({create:function(t){if(t){var i;try{if(!e.documentElement.contains(t.node))throw new Exception("Element not in the dom");i=t.node.getBBox()}catch(e){if(t instanceof y.Shape){var n=t.clone(y.parser.draw).show();i=n.bbox(),n.remove()}else i={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=i.x,this.y=i.y,this.width=i.width,this.height=i.height}x(this)},parent:y.Element,construct:{bbox:function(){return new y.BBox(this)}}}),y.TBox=y.invent({create:function(t){if(t){var e=t.ctm().extract(),i=t.bbox();this.width=i.width*e.scaleX,this.height=i.height*e.scaleY,this.x=i.x+e.x,this.y=i.y+e.y}x(this)},parent:y.Element,construct:{tbox:function(){return new y.TBox(this)}}}),y.RBox=y.invent({create:function(e){if(e){var i=e.doc().parent(),n=e.node.getBoundingClientRect(),s=1;for(this.x=n.left,this.y=n.top,this.x-=i.offsetLeft,this.y-=i.offsetTop;i=i.offsetParent;)this.x-=i.offsetLeft,this.y-=i.offsetTop;for(i=e;i.parent&&(i=i.parent());)i.viewbox&&(s*=i.viewbox().zoom,this.x-=i.x()||0,this.y-=i.y()||0);this.width=n.width/=s,this.height=n.height/=s}x(this),this.x+=t.pageXOffset,this.y+=t.pageYOffset},parent:y.Element,construct:{rbox:function(){return new y.RBox(this)}}}),[y.BBox,y.TBox,y.RBox].forEach(function(t){y.extend(t,{merge:function(e){var i=new t;return i.x=Math.min(this.x,e.x),i.y=Math.min(this.y,e.y),i.width=Math.max(this.x+this.width,e.x+e.width)-i.x,i.height=Math.max(this.y+this.height,e.y+e.height)-i.y,x(i)}})}),y.Matrix=y.invent({create:function(t){var e,i=l([1,0,0,1,0,0]);for(t=t instanceof y.Element?t.matrixify():"string"==typeof t?d(t):6==arguments.length?l([].slice.call(arguments)):"object"==typeof t?t:i,e=w.length-1;e>=0;--e)this[w[e]]=t&&"number"==typeof t[w[e]]?t[w[e]]:i[w[e]]},extend:{extract:function(){var t=u(this,0,1),e=u(this,1,0),i=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(i*Math.PI/180)+this.f*Math.sin(i*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(i*Math.PI/180)+this.e*Math.sin(-i*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),skewX:-i,skewY:180/Math.PI*Math.atan2(e.y,e.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:i,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new y.Matrix(this)}},clone:function(){return new y.Matrix(this)},morph:function(t){return this.destination=new y.Matrix(t),this},at:function(t){if(!this.destination)return this;var e=new y.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});if(this.param&&this.param.to){var i={rotation:this.param.from.rotation+(this.param.to.rotation-this.param.from.rotation)*t,cx:this.param.from.cx,cy:this.param.from.cy};e=e.rotate((this.param.to.rotation-2*this.param.from.rotation)*t,i.cx,i.cy),e.param=i}return e},multiply:function(t){return new y.Matrix(this.native().multiply(c(t).native()))},inverse:function(){return new y.Matrix(this.native().inverse())},translate:function(t,e){return new y.Matrix(this.native().translate(t||0,e||0))},scale:function(t,e,i,n){return 1!=arguments.length&&3!=arguments.length||(e=t),3==arguments.length&&(n=i,i=e),this.around(i,n,new y.Matrix(t,0,0,e,0,0))},rotate:function(t,e,i){return t=y.utils.radians(t),this.around(e,i,new y.Matrix(Math.cos(t),Math.sin(t),(-Math.sin(t)),Math.cos(t),0,0))},flip:function(t,e){return"x"==t?this.scale(-1,1,e,0):this.scale(1,-1,0,e)},skew:function(t,e,i,n){return this.around(i,n,this.native().skewX(t||0).skewY(e||0))},skewX:function(t,e,i){return this.around(e,i,this.native().skewX(t||0))},skewY:function(t,e,i){return this.around(e,i,this.native().skewY(t||0))},around:function(t,e,i){return this.multiply(new y.Matrix(1,0,0,1,t||0,e||0)).multiply(i).multiply(new y.Matrix(1,0,0,1,-t||0,-e||0))},native:function(){for(var t=y.parser.native.createSVGMatrix(),e=w.length-1;e>=0;e--)t[w[e]]=this[w[e]];return t},toString:function(){return"matrix("+this.a+","+this.b+","+this.c+","+this.d+","+this.e+","+this.f+")"}},parent:y.Element,construct:{ctm:function(){return new y.Matrix(this.node.getCTM())},screenCTM:function(){return new y.Matrix(this.node.getScreenCTM())}}}),y.Point=y.invent({create:function(t,e){var i,n={x:0,y:0};i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"==typeof t?{x:t.x,y:t.y}:null!=e?{x:t,y:e}:n,this.x=i.x,this.y=i.y},extend:{clone:function(){return new y.Point(this)},morph:function(t){return this.destination=new y.Point(t),this},at:function(t){if(!this.destination)return this;var e=new y.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t});return e},native:function(){var t=y.parser.native.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new y.Point(this.native().matrixTransform(t.native()))}}}),y.extend(y.Element,{point:function(t,e){return new y.Point(t,e).transform(this.screenCTM().inverse())}}),y.extend(y.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]=y.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?y.defaults.attrs[t]:y.regex.isNumber.test(e)?parseFloat(e):e;"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),"fill"!=t&&"stroke"!=t||(y.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)), +e instanceof y.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new y.Number(e):y.Color.isColor(e)?e=new y.Color(e):Array.isArray(e)?e=new y.Array(e):e instanceof y.Matrix&&e.param&&(this.param=e.param),"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}}),y.extend(y.Element,{transform:function(t,e){var i,n=this;if("object"!=typeof t)return i=new y.Matrix(n).extract(),"string"==typeof t?i[t]:i;if(i=new y.Matrix(n),e=!!e||!!t.relative,null!=t.a)i=e?i.multiply(new y.Matrix(t)):new y.Matrix(t);else if(null!=t.rotation)f(t,n),i=e?i.rotate(t.rotation,t.cx,t.cy):i.rotate(t.rotation-i.extract().rotation,t.cx,t.cy);else if(null!=t.scale||null!=t.scaleX||null!=t.scaleY){if(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,!e){var s=i.extract();t.scaleX=1*t.scaleX/s.scaleX,t.scaleY=1*t.scaleY/s.scaleY}i=i.scale(t.scaleX,t.scaleY,t.cx,t.cy)}else if(null!=t.skewX||null!=t.skewY){if(f(t,n),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,!e){var s=i.extract();i=i.multiply((new y.Matrix).skew(s.skewX,s.skewY,t.cx,t.cy).inverse())}i=i.skew(t.skewX,t.skewY,t.cx,t.cy)}else t.flip?i=i.flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset):null==t.x&&null==t.y||(e?i=i.translate(t.x,t.y):(null!=t.x&&(i.e=t.x),null!=t.y&&(i.f=t.y)));return this.attr("transform",i)}}),y.extend(y.FX,{transform:function(t,e){var i,n=this.target();return"object"!=typeof t?(i=new y.Matrix(n).extract(),"string"==typeof t?i[t]:i):(e=!!e||!!t.relative,null!=t.a?i=new y.Matrix(t):null!=t.rotation?(f(t,n),i=new y.Rotate(t.rotation,t.cx,t.cy)):null!=t.scale||null!=t.scaleX||null!=t.scaleY?(f(t,n),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,i=new y.Scale(t.scaleX,t.scaleY,t.cx,t.cy)):null!=t.skewX||null!=t.skewY?(f(t,n),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,i=new y.Skew(t.skewX,t.skewY,t.cx,t.cy)):t.flip?i=(new y.Matrix).morph((new y.Matrix).flip(t.flip,null==t.offset?n.bbox()["c"+t.flip]:t.offset)):null==t.x&&null==t.y||(i=new y.Translate(t.x,t.y)),i?(i.relative=e,this.last().transforms.push(i),setTimeout(function(){this.start()}.bind(this),0),this):this)}}),y.extend(y.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){var t=(this.attr("transform")||"").split(/\)\s*,?\s*/).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(y.regex.matrixElements).map(function(t){return parseFloat(t)})]}).reduce(function(t,e){return"matrix"==e[0]?t.multiply(l(e[1])):t[e[0]].apply(t,e[1])},new y.Matrix);return t},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.rect(1,1),n=i.screenCTM().inverse();return i.remove(),this.addTo(t).untransform().transform(n.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),y.Transformation=y.invent({create:function(t,e){if(arguments.length>1&&"boolean"!=typeof e)return this.create([].slice.call(arguments));if("object"==typeof t)for(var i=0,n=this.arguments.length;i=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return y.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){var i,n,s=this.children();for(i=0,n=s.length;in/s?this.height/s:this.width/n,this.x=e,this.y=i,this.width=n,this.height=s)}else t="string"==typeof t?t.match(f).map(function(t){return parseFloat(t)}):Array.isArray(t)?t:"object"==typeof t?[t.x,t.y,t.width,t.height]:4==arguments.length?[].slice.call(arguments):u,this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3]},extend:{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height},morph:function(t){var t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments);return this.destination=new y.ViewBox(t),this},at:function(t){return this.destination?new y.ViewBox([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:y.Container,construct:{viewbox:function(t){return 0==arguments.length?new y.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){y.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),y.listeners=[],y.handlerMap=[],y.listenerId=0,y.on=function(t,e,i,n){var s=i.bind(n||t.instance||t),r=(y.handlerMap.indexOf(t)+1||y.handlerMap.push(t))-1,a=e.split(".")[0],o=e.split(".")[1]||"*";y.listeners[r]=y.listeners[r]||{},y.listeners[r][a]=y.listeners[r][a]||{},y.listeners[r][a][o]=y.listeners[r][a][o]||{},i._svgjsListenerId||(i._svgjsListenerId=++y.listenerId),y.listeners[r][a][o][i._svgjsListenerId]=s,t.addEventListener(a,s,!1)},y.off=function(t,e,i){var n=y.handlerMap.indexOf(t),s=e&&e.split(".")[0],r=e&&e.split(".")[1];if(n!=-1)if(i){if("function"==typeof i&&(i=i._svgjsListenerId),!i)return;y.listeners[n][s]&&y.listeners[n][s][r||"*"]&&(t.removeEventListener(s,y.listeners[n][s][r||"*"][i],!1),delete y.listeners[n][s][r||"*"][i])}else if(r&&s){if(y.listeners[n][s]&&y.listeners[n][s][r]){for(i in y.listeners[n][s][r])y.off(t,[s,r].join("."),i);delete y.listeners[n][s][r]}}else if(r)for(e in y.listeners[n])for(namespace in y.listeners[n][e])r===namespace&&y.off(t,[e,r].join("."));else if(s){if(y.listeners[n][s]){for(namespace in y.listeners[n][s])y.off(t,[s,namespace].join("."));delete y.listeners[n][s]}}else{for(e in y.listeners[n])y.off(t,e);delete y.listeners[n]}},y.extend(y.Element,{on:function(t,e,i){return y.on(this.node,t,e,i),this},off:function(t,e){return y.off(this.node,t,e),this},fire:function(t,e){return t instanceof Event?this.node.dispatchEvent(t):this.node.dispatchEvent(new b(t,{detail:e})),this}}),y.Defs=y.invent({create:"defs",inherit:y.Container}),y.G=y.invent({create:"g",inherit:y.Container,extend:{x:function(t){return null==t?this.transform("x"):this.transform({x:t-this.x()},!0)},y:function(t){return null==t?this.transform("y"):this.transform({y:t-this.y()},!0)},cx:function(t){return null==t?this.gbox().cx:this.x(t-this.gbox().width/2)},cy:function(t){return null==t?this.gbox().cy:this.y(t-this.gbox().height/2)},gbox:function(){var t=this.bbox(),e=this.transform();return t.x+=e.x,t.x2+=e.x,t.cx+=e.x,t.y+=e.y,t.y2+=e.y,t.cy+=e.y,t}},construct:{group:function(){return this.put(new y.G)}}}),y.extend(y.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,e=this.parent();return e.removeElement(this).add(this,t),e instanceof y.Doc&&e.node.appendChild(e.defs().node),this},backward:function(){var t=this.position();return t>0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof y.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}}),y.Mask=y.invent({create:function(){this.constructor.call(this,y.create("mask")),this.targets=[]},inherit:y.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return this.targets=[],this.parent().removeElement(this),this}},construct:{mask:function(){return this.defs().put(new y.Mask)}}}),y.extend(y.Element,{maskWith:function(t){return this.masker=t instanceof y.Mask?t:this.parent().mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),y.ClipPath=y.invent({create:function(){this.constructor.call(this,y.create("clipPath")),this.targets=[]},inherit:y.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return this.targets=[],this.parent().removeElement(this),this}},construct:{clip:function(){return this.defs().put(new y.ClipPath)}}}),y.extend(y.Element,{clipWith:function(t){return this.clipper=t instanceof y.ClipPath?t:this.parent().clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),y.Gradient=y.invent({create:function(t){this.constructor.call(this,y.create(t+"Gradient")),this.type=t},inherit:y.Container,extend:{at:function(t,e,i){return this.put(new y.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="gradientTransform"),y.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),y.extend(y.Gradient,y.FX,{from:function(t,e){return"radial"==(this._target||this).type?this.attr({fx:new y.Number(t),fy:new y.Number(e)}):this.attr({x1:new y.Number(t),y1:new y.Number(e)})},to:function(t,e){return"radial"==(this._target||this).type?this.attr({cx:new y.Number(t),cy:new y.Number(e)}):this.attr({x2:new y.Number(t),y2:new y.Number(e)})}}),y.extend(y.Defs,{gradient:function(t,e){return this.put(new y.Gradient(t)).update(e)}}),y.Stop=y.invent({create:"stop",inherit:y.Element,extend:{update:function(t){return("number"==typeof t||t instanceof y.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 y.Number(t.offset)),this}}}),y.Pattern=y.invent({create:"pattern",inherit:y.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="patternTransform"),y.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),y.extend(y.Defs,{pattern:function(t,e,i){return this.put(new y.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),y.Doc=y.invent({create:function(t){t&&(t="string"==typeof t?e.getElementById(t):t,"svg"==t.nodeName?this.constructor.call(this,t):(this.constructor.call(this,y.create("svg")),t.appendChild(this.node),this.size("100%","100%")),this.namespace().defs())},inherit:y.Container,extend:{namespace:function(){return this.attr({xmlns:y.ns,version:"1.1"}).attr("xmlns:xlink",y.xlink,y.xmlns).attr("xmlns:svgjs",y.svgjs,y.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=y.adopt(t):this._defs=new y.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return"#document"==this.node.parentNode.nodeName?null:this.node.parentNode},spof:function(t){var e=this.node.getScreenCTM();return e&&this.style("left",-e.e%1+"px").style("top",-e.f%1+"px"),this},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this}}}),y.Shape=y.invent({create:function(t){this.constructor.call(this,t)},inherit:y.Element}),y.Bare=y.invent({create:function(t,e){if(this.constructor.call(this,y.create(t)),e)for(var i in e.prototype)"function"==typeof e.prototype[i]&&(this[i]=e.prototype[i])},inherit:y.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),y.extend(y.Parent,{element:function(t,e){return this.put(new y.Bare(t,e))},symbol:function(){return this.defs().element("symbol",y.Container)}}),y.Use=y.invent({create:"use",inherit:y.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,y.xlink)}},construct:{use:function(t,e){return this.put(new y.Use).element(t,e)}}}),y.Rect=y.invent({create:"rect",inherit:y.Shape,construct:{rect:function(t,e){return this.put(new y.Rect).size(t,e)}}}),y.Circle=y.invent({create:"circle",inherit:y.Shape,construct:{circle:function(t){return this.put(new y.Circle).rx(new y.Number(t).divide(2)).move(0,0)}}}),y.extend(y.Circle,y.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),y.Ellipse=y.invent({create:"ellipse",inherit:y.Shape,construct:{ellipse:function(t,e){return this.put(new y.Ellipse).size(t,e).move(0,0)}}}),y.extend(y.Ellipse,y.Rect,y.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),y.extend(y.Circle,y.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 y.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new y.Number(t).divide(2))},size:function(t,e){var i=h(this,t,e);return this.rx(new y.Number(i.width).divide(2)).ry(new y.Number(i.height).divide(2))}}),y.Line=y.invent({create:"line",inherit:y.Shape,extend:{array:function(){return new y.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return t="undefined"!=typeof e?{x1:t,y1:e,x2:i,y2:n}:new y.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=h(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return this.put(new y.Line).plot(t,e,i,n)}}}),y.Polyline=y.invent({create:"polyline",inherit:y.Shape,construct:{polyline:function(t){return this.put(new y.Polyline).plot(t)}}}),y.Polygon=y.invent({create:"polygon",inherit:y.Shape,construct:{polygon:function(t){return this.put(new y.Polygon).plot(t)}}}),y.extend(y.Polyline,y.Polygon,{array:function(){return this._array||(this._array=new y.PointArray(this.attr("points")))},plot:function(t){return this.attr("points",this._array=new y.PointArray(t))},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=h(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),y.extend(y.Line,y.Polyline,y.Polygon,{morphArray:y.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)}}),y.Path=y.invent({create:"path",inherit:y.Shape,extend:{morphArray:y.PathArray,array:function(){return this._array||(this._array=new y.PathArray(this.attr("d")))},plot:function(t){return this.attr("d",this._array=new y.PathArray(t))},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=h(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 y.Path).plot(t)}}}),y.Image=y.invent({create:"image",inherit:y.Shape,extend:{load:function(t){if(!t)return this;var i=this,n=e.createElement("img");return n.onload=function(){var e=i.parent(y.Pattern);null!==e&&(0==i.width()&&0==i.height()&&i.size(n.width,n.height),e&&0==e.width()&&0==e.height()&&e.size(i.width(),i.height()),"function"==typeof i._loaded&&i._loaded.call(i,{width:n.width,height:n.height,ratio:n.width/n.height,url:t}))},n.onerror=function(t){"function"==typeof i._error&&i._error.call(i,t)},this.attr("href",n.src=this.src=t,y.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new y.Image).load(t).size(e||0,i||e||0)}}}),y.Text=y.invent({create:function(){this.constructor.call(this,y.create("text")),this.dom.leading=new y.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",y.defaults.attrs["font-family"])},inherit:y.Shape,extend:{x:function(t){return null==t?this.attr("x"):(this.textPath||this.lines().each(function(){this.dom.newLined&&this.x(t)}),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("undefined"==typeof t){for(var t="",e=this.node.childNodes,i=0,n=e.length;i=0;e--)null!=i[g[t][e]]&&this.attr(g.prefix(t,g[t][e]),i[g[t][e]]);return this},y.extend(y.Element,y.FX,i)}),y.extend(y.Element,y.FX,{rotate:function(t,e,i){return this.transform({rotation:t,cx:e,cy:i})},skew:function(t,e,i,n){return this.transform({skewX:t,skewY:e,cx:i,cy:n})},scale:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({scale:t,cx:e,cy:i}):this.transform({scaleX:t,scaleY:e,cx:i,cy:n})},translate:function(t,e){return this.transform({x:t,y:e})},flip:function(t,e){return this.transform({flip:t,offset:e})},matrix:function(t){return this.attr("transform",new y.Matrix(t))},opacity:function(t){return this.attr("opacity",t)},dx:function(t){return this.x((this instanceof y.FX?0:this.x())+t,!0)},dy:function(t){return this.y((this instanceof y.FX?0:this.y())+t,!0)},dmove:function(t,e){return this.dx(t).dy(e)}}),y.extend(y.Rect,y.Ellipse,y.Circle,y.Gradient,y.FX,{radius:function(t,e){var i=(this._target||this).type;return"radial"==i||"circle"==i?this.attr("r",new y.Number(t)):this.rx(t).ry(null==e?t:e)}}),y.extend(y.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),y.extend(y.Parent,y.Text,y.FX,{font:function(t){for(var e in t)"leading"==e?this.leading(t[e]):"anchor"==e?this.attr("text-anchor",t[e]):"size"==e||"family"==e||"weight"==e||"stretch"==e||"variant"==e||"style"==e?this.attr("font-"+e,t[e]):this.attr(e,t[e]);return this}}),y.Set=y.invent({create:function(t){Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;t-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){var t=new y.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(t){return new y.Set(t)}}}),y.FX.Set=y.invent({create:function(t){this.set=t}}),y.Set.inherit=function(){var t,e=[];for(var t in y.Shape.prototype)"function"==typeof y.Shape.prototype[t]&&"function"!=typeof y.Set.prototype[t]&&e.push(t);e.forEach(function(t){y.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),y.get=function(t){var i=e.getElementById(v(t)||t);return y.adopt(i)},y.select=function(t,i){return new y.Set(y.utils.map((i||e).querySelectorAll(t),function(t){return y.adopt(t)}))},y.extend(y.Parent,{select:function(t){return y.select(t,this.node)}});var w="abcdef".split("");if("function"!=typeof b){var b=function(t,i){i=i||{bubbles:!1,cancelable:!1,detail:void 0};var n=e.createEvent("CustomEvent");return n.initCustomEvent(t,i.bubbles,i.cancelable,i.detail),n};b.prototype=t.Event.prototype,t.CustomEvent=b}return function(e){for(var i=0,n=["moz","webkit"],s=0;s 0.9){ - rect.off('.fx') - fx.stop() + switch(++called) { + case 1: + expect(pos).toBeCloseTo(0.25) + break + + case 2: + expect(pos).toBeCloseTo(0.5) + break + + case 3: + expect(pos).toBeCloseTo(0.65) + break - done() + case 4: + expect(pos).toBe(1) + break } + + expect(morph(0, 100)).toBeCloseTo(pos*100) + }) + + jasmine.clock().tick(125) + fx.step() + expect(called).toBe(1) + + jasmine.clock().tick(125) // 250 ms have passed + fx.step() + expect(called).toBe(2) + + jasmine.clock().tick(75) // 325 ms have passed + fx.step() + expect(called).toBe(3) + + jasmine.clock().tick(175) // 500 ms have passed + fx.step() + expect(called).toBe(4) }) }) + describe('duringAll()', function() { - it('adds a callback which is called on every animation step for the whole chain', function(done) { + it('adds a callback which is called on every animation step for the whole chain', function() { fx.finish() rect.off('.fx') @@ -341,218 +931,523 @@ describe('FX', function() { var pos1 = false var pos2 = false - setTimeout(function(){ - pos1 = true - }, 300) - - setTimeout(function(){ - pos2 = true - }, 800) - fx.duringAll(function(pos, morph, eased, situation){ if(pos1){ pos1 = false sit = situation - expect(this.fx.pos).toBeGreaterThan(0.5) + expect(this.fx.pos).toBeCloseTo(0.6) } if(pos2){ pos2 = null expect(situation).not.toBe(sit) - expect(this.fx.pos).toBeGreaterThan(0.5) - done() + expect(this.fx.pos).toBeCloseTo(0.75) } }) - setTimeout(function(){ - if(pos2 === null) return + pos1 = true + jasmine.clock().tick(300) + fx.step() + + jasmine.clock().tick(200) // End of the first animation + fx.step() + + pos2 = true + jasmine.clock().tick(375) + fx.step() + + if(pos1 || pos2) { fail('Not enough situations called') - done() - }, 1200) + } }) }) + describe('once()', function() { - it('adds a callback which is called once at the specified position', function(done) { + it('adds a callback which is called once at the specified position', function() { + var called = false fx.start().once(0.5, function(pos, eased){ - expect(pos).toBeGreaterThan(0.49) - done() + called = true + expect(pos).toBeCloseTo(0.5) }) + + jasmine.clock().tick(125) + fx.step() + expect(called).toBe(false) + + jasmine.clock().tick(125) // 250 ms have passed + fx.step() + expect(called).toBe(true) }) }) + describe('loop()', function() { - it('should create an eternal loop when no arguments are given', function(done) { + it('should create an eternal loop when no arguments are given', function() { + var time = 10523, dur = fx.situation.duration + fx.loop() - expect(fx.situation.loop).toBe(true) + expect(fx.situation.loop).toBe(0) expect(fx.situation.loops).toBe(true) + expect(fx.pos).toBe(0) + expect(fx.absPos).toBe(0) - fx.start() - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(true) - expect(fx.situation.loops).toBe(true) - expect(fx.pos).toBeCloseTo(0.6, 1) - done() - }, 800) + fx.start().step() + jasmine.clock().tick(time) + fx.step() + + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe( Math.floor(time/dur) ) + expect(fx.situation.loops).toBe(true) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) }) - it('should create an eternal loop when the first argument is true', function(done) { + it('should create an eternal loop when the first argument is true', function() { + var time = 850452, dur = fx.situation.duration + fx.loop(true) - expect(fx.situation.loop).toBe(true) + expect(fx.situation.loop).toBe(0) expect(fx.situation.loops).toBe(true) + expect(fx.pos).toBe(0) + expect(fx.absPos).toBe(0) - fx.start() - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(true) - expect(fx.situation.loops).toBe(true) - expect(fx.pos).toBeCloseTo(0.3, 1) - done() - }, 650) + fx.start().step() + jasmine.clock().tick(time) + fx.step() + + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe( Math.floor(time/dur) ) + expect(fx.situation.loops).toBe(true) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) }) - it('should loop for the specified number of times', function(done) { + it('should loop for the specified number of times', function() { + var time = 0, dur = fx.situation.duration + fx.loop(3) - expect(fx.situation.loop).toBe(3) + expect(fx.situation.loop).toBe(0) expect(fx.situation.loops).toBe(3) + expect(fx.pos).toBe(0) + expect(fx.absPos).toBe(0) - fx.start() - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(3) - expect(fx.situation.loops).toBe(3) - expect(fx.pos).toBeCloseTo(0.4, 1) - }, 200) - - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(2) - expect(fx.situation.loops).toBe(3) - expect(fx.pos).toBeCloseTo(0.5, 1) - }, 750) - - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(1) - expect(fx.situation.loops).toBe(3) - expect(fx.pos).toBeCloseTo(0.64, 1) - }, 1320) - - setTimeout(function(){ - expect(fx.active).toBe(false) - expect(fx.situation).toBeNull() - expect(fx.pos).toBe(1) - done() - }, 1600) + fx.start().step() + jasmine.clock().tick(200) + time = 200 + + fx.step() + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe(0) + expect(fx.situation.loops).toBe(3) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) + + jasmine.clock().tick(550) + time += 550 // time at 750 + + fx.step() + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe(1) + expect(fx.situation.loops).toBe(3) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) + + jasmine.clock().tick(570) + time += 570 // time at 1320 + + fx.step() + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe(2) + expect(fx.situation.loops).toBe(3) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) + + jasmine.clock().tick(180) + time += 180 // time at 1500 + + fx.step() + expect(fx.active).toBe(false) + expect(fx.situation).toBeNull() + expect(fx.pos).toBe(1) + expect(fx.absPos).toBe(3) }) - it('should go from beginning to end and start over again (0->1.0->1.0->1.) by default', function(done) { + it('should go from beginning to end and start over again (0->1.0->1.0->1.) by default', function() { + var time = 0, dur = fx.situation.duration + fx.loop(2) - expect(fx.situation.loop).toBe(2) + expect(fx.situation.loop).toBe(0) expect(fx.situation.loops).toBe(2) expect(fx.situation.reversing).toBe(false) expect(fx.situation.reversed).toBe(false) + expect(fx.pos).toBe(0) + expect(fx.absPos).toBe(0) - fx.start() - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(2) - expect(fx.situation.loops).toBe(2) - expect(fx.situation.reversing).toBe(false) - expect(fx.situation.reversed).toBe(false) - expect(fx.pos).toBeCloseTo(0.65, 1) - }, 325) - - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(1) - expect(fx.situation.loops).toBe(2) - expect(fx.situation.reversing).toBe(false) - expect(fx.situation.reversed).toBe(false) - expect(fx.pos).toBeCloseTo(0.8, 1) - }, 900) + fx.start().step() + jasmine.clock().tick(325) + time = 325 - setTimeout(function(){ - expect(fx.active).toBe(false) - expect(fx.situation).toBeNull() - expect(fx.pos).toBe(1) - done() - }, 1100) + fx.step() + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe(0) + expect(fx.situation.loops).toBe(2) + expect(fx.situation.reversing).toBe(false) + expect(fx.situation.reversed).toBe(false) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) + + jasmine.clock().tick(575) + time += 575 // time at 900 + + fx.step() + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe(1) + expect(fx.situation.loops).toBe(2) + expect(fx.situation.reversing).toBe(false) + expect(fx.situation.reversed).toBe(false) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) + + jasmine.clock().tick(200) + time += 200 // time at 1100 + + fx.step() + expect(fx.active).toBe(false) + expect(fx.situation).toBeNull() + expect(fx.pos).toBe(1) + expect(fx.absPos).toBe(2) }) - it('should be completely reversed before starting over (0->1->0->1->0->1.) when the reverse flag is passed', function(done) { + it('should be completely reversed before starting over (0->1->0->1->0->1.) when the reverse flag is passed', function() { + var time = 0, dur = fx.situation.duration + fx.loop(2, true) - expect(fx.situation.loop).toBe(2) + expect(fx.situation.loop).toBe(0) expect(fx.situation.loops).toBe(2) expect(fx.situation.reversing).toBe(true) expect(fx.situation.reversed).toBe(false) + expect(fx.pos).toBe(0) + expect(fx.absPos).toBe(0) - fx.start() - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(2) - expect(fx.situation.loops).toBe(2) - expect(fx.situation.reversing).toBe(true) - expect(fx.situation.reversed).toBe(false) - expect(fx.pos).toBeCloseTo(0.65, 1) - }, 325) - - setTimeout(function(){ - expect(fx.active).toBe(true) - expect(fx.situation.loop).toBe(1) - expect(fx.situation.loops).toBe(2) - expect(fx.situation.reversing).toBe(true) - expect(fx.situation.reversed).toBe(true) - expect(fx.pos).toBeCloseTo(0.2, 1) - }, 900) + fx.start().step() + jasmine.clock().tick(325) + time = 325 - setTimeout(function(){ - expect(fx.active).toBe(false) - expect(fx.situation).toBeNull() - expect(fx.pos).toBe(0) - done() - }, 1100) + fx.step() + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe(0) + expect(fx.situation.loops).toBe(2) + expect(fx.situation.reversing).toBe(true) + expect(fx.situation.reversed).toBe(false) + expect(fx.pos).toBeCloseTo((time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) + + jasmine.clock().tick(575) + time += 575 // time at 900 + + fx.step() + expect(fx.active).toBe(true) + expect(fx.situation.loop).toBe(1) + expect(fx.situation.loops).toBe(2) + expect(fx.situation.reversing).toBe(true) + expect(fx.situation.reversed).toBe(true) + expect(fx.pos).toBeCloseTo(1 - (time/dur) % 1) + expect(fx.absPos).toBeCloseTo(time/dur) + + jasmine.clock().tick(200) + time += 200 // time at 1100 + + fx.step() + expect(fx.active).toBe(false) + expect(fx.situation).toBeNull() + expect(fx.pos).toBe(0) + expect(fx.absPos).toBe(2) }) it('should be applied on the last situation', function() { fx.loop(5) - expect(fx.situation.loop).toBe(5) + expect(fx.situation.loop).toBe(0) expect(fx.situation.loops).toBe(5) expect(fx.situation.reversing).toBe(false) fx.animate().loop(3, true) - expect(fx.situation.loop).toBe(5) + expect(fx.situation.loop).toBe(0) expect(fx.situation.loops).toBe(5) expect(fx.situation.reversing).toBe(false) var c = fx.last() - expect(c.loop).toBe(3) + expect(c.loop).toBe(0) expect(c.loops).toBe(3) expect(c.reversing).toBe(true) }) + + it('should be possible to call it with false as the first argument', function() { + fx.situation.loops = true + fx.loop(false) + expect(fx.situation.loops).toBe(false) + }) }) - it('animates the x/y-attr', function(done) { + + describe('step()', function() { + it('should not recalculate the absolute position if the first parameter is true', function() { + var absPos + + // We shift start to help us see if the absolute position get recalculated + // If it get recalculated, the result would be 0.5 + fx.situation.start -= 250 + + absPos = 0.4 + fx.absPos = absPos + expect(fx.step(true).absPos).toBe(absPos) + + absPos = 0 + fx.absPos = absPos + expect(fx.step(true).absPos).toBe(absPos) + + absPos = -3.7 + fx.absPos = absPos + expect(fx.step(true).absPos).toBe(absPos) + + absPos = 1 + fx.absPos = absPos + expect(fx.step(true).absPos).toBe(absPos) + }) + + it('should not allow an absolute position to be above the end', function() { + var absPos, loops + + // With no loops, absolute position should not go above 1 + absPos = 4.26 + fx.absPos = absPos + expect(fx.step(true).absPos).toBe(1) + expect(fx.situation).toBeNull() + + fx.animate() // Recreate an animation since the other one was ended + + // With loops, absolute position should not go above loops + loops = 4 + absPos = 7.42 + fx.absPos = absPos + expect(fx.loop(loops).step(true).absPos).toBe(loops) + expect(fx.situation).toBeNull() + }) + + describe('when converting an absolute position to a position', function() { + it('should, when the absolute position is below the maximum number of loops, use the integer part of the absolute position to set the loop counter and use its fractional part to set the position', function(){ + var absPos, absPosFrac, absPosInt, loops + + // Without the reverse flag + loops = 12 + absPos = 4.52 + absPosInt = Math.floor(absPos) + absPosFrac = absPos - absPosInt + fx.absPos = absPos + fx.loop(loops).step(true) + expect(fx.pos).toBe(absPosFrac) + expect(fx.situation.loop).toBe(absPosInt) + + fx.stop().animate() + + loops = true + absPos = 2.57 + absPosInt = Math.floor(absPos) + absPosFrac = absPos - absPosInt + fx.absPos = absPos + fx.loop(loops).step(true) + expect(fx.pos).toBe(absPosFrac) + expect(fx.situation.loop).toBe(absPosInt) + + fx.stop().animate() + + // With the reverse flag, the position is reversed at each odd loop + loops = 412 + absPos = 6.14 + absPosInt = Math.floor(absPos) + absPosFrac = absPos - absPosInt + fx.absPos = absPos + fx.loop(loops, true).step(true) + expect(fx.pos).toBe(absPosFrac) + expect(fx.situation.loop).toBe(absPosInt) + expect(fx.situation.reversed).toBe(false) + + fx.stop().animate() + + loops = true + absPos = 5.12 + absPosInt = Math.floor(absPos) + absPosFrac = absPos - absPosInt + fx.absPos = absPos + fx.loop(loops, true).step(true) + expect(fx.pos).toBe(1-absPosFrac) // Odd loop, so it is reversed + expect(fx.situation.loop).toBe(absPosInt) + expect(fx.situation.reversed).toBe(true) + + fx.stop().animate() + + // When the animation is set to run backward, it is the opposite, the position is reversed at each even loop + loops = 14 + absPos = 8.46 + absPosInt = Math.floor(absPos) + absPosFrac = absPos - absPosInt + fx.absPos = absPos + fx.reverse(true).loop(loops, true).step(true) + expect(fx.pos).toBe(1-absPosFrac) // Even loop, so it is reversed + expect(fx.situation.loop).toBe(absPosInt) + expect(fx.situation.reversed).toBe(true) + + fx.stop().animate() + + loops = true + absPos = 3.12 + absPosInt = Math.floor(absPos) + absPosFrac = absPos - absPosInt + fx.absPos = absPos + fx.reverse(true).loop(loops, true).step(true) + expect(fx.pos).toBe(absPosFrac) + expect(fx.situation.loop).toBe(absPosInt) + expect(fx.situation.reversed).toBe(false) + }) + + it('should, when the absolute position is above or equal to the the maximum number of loops, set the position to its end value and end the animation', function() { + var absPos, loops + + // Without the reverse flag, the end value of position is 1 + loops = 6 + absPos = 13.52 + fx.absPos = absPos + fx.loop(loops).step(true) + expect(fx.pos).toBe(1) + expect(fx.situation).toBeNull() + + fx.animate() // Recreate an animation since the other one was ended + + loops = false + absPos = 146.22 + fx.absPos = absPos + fx.loop(loops).step(true) + expect(fx.pos).toBe(1) + expect(fx.situation).toBeNull() + + fx.animate() // Recreate an animation since the other one was ended + + // With the reverse flag, the end value of position is 0 when loops is even and 1 when loops is an odd number or false + loops = 6 + absPos = 6 + fx.absPos = absPos + fx.loop(loops, true).step(true) + expect(fx.pos).toBe(0) // Even loops + expect(fx.situation).toBeNull() + + fx.animate() // Recreate an animation since the other one was ended + + loops = false + absPos = 4.47 + fx.absPos = absPos + fx.loop(loops, true).step(true) + expect(fx.pos).toBe(1) // 1 since loops is false + expect(fx.situation).toBeNull() + + fx.animate() // Recreate an animation since the other one was ended + + // When the animation is set to run backward, it is the opposite, the end value of position is 1 when loops is even and 0 when loops is an odd number or false + loops = 8 + absPos = 12.65 + fx.absPos = absPos + fx.reverse(true).loop(loops, true).step(true) + expect(fx.pos).toBe(1) // Even loops + expect(fx.situation).toBeNull() + + fx.animate() // Recreate an animation since the other one was ended + + loops = 11 + absPos = 12.41 + fx.absPos = absPos + fx.reverse(true).loop(loops, true).step(true) + expect(fx.pos).toBe(0) // Odd loops + expect(fx.situation).toBeNull() + }) + + it('should set the position to its start value when the absolute position is below 0', function() { + var absPos + + // When the animation is not set to run backward the start value is 0 + absPos = -2.27 + fx.loop(7) + fx.situation.loop = 3 + fx.absPos = absPos + fx.step(true) + expect(fx.pos).toBe(0) + expect(fx.absPos).toBe(absPos) + expect(fx.situation.loop).toBe(0) + + fx.stop().animate() + + // When the animation is set to run backward the start value is 1 + absPos = -4.12 + fx.absPos = absPos + fx.reverse(true).step(true) + expect(fx.pos).toBe(1) + expect(fx.absPos).toBe(absPos) + }) + + it('should, when looping with the reverse flag, toggle reversed only when the difference between the new value of loop counter and its old value is odd', function() { + // The new value of the loop counter is the integer part of absPos + + fx.loop(9, true) + expect(fx.situation.loop).toBe(0) + expect(fx.pos).toBe(0) + expect(fx.situation.reversed).toBe(false) + + fx.absPos = 3 + fx.step(true) + expect(fx.situation.reversed).toBe(true) // (3-0) is odd + + fx.absPos = 1 + fx.step(true) + expect(fx.situation.reversed).toBe(true) // (1-3) is even + + fx.absPos = 6 + fx.step(true) + expect(fx.situation.reversed).toBe(false) // (6-1) is odd + + fx.absPos = 9 + fx.step(true) + expect(fx.situation).toBeNull() + expect(fx.pos).toBe(1) // It should end not reversed, which mean the position is expected to be 1 + // ((9-1)-6) is even, the -1 is because we do not want reversed to be toggled after the last loop + }) + }) + }) + + + it('animates the x/y-attr', function() { + var called = false fx.move(200,200).after(function(){ expect(rect.x()).toBe(200) expect(rect.y()).toBe(200) - done() + called = true - }); + }) - setTimeout(function(){ - expect(rect.x()).toBeGreaterThan(100) - expect(rect.y()).toBeGreaterThan(100) - }, 250) + jasmine.clock().tick(250) + fx.step() + expect(rect.x()).toBeGreaterThan(100) + expect(rect.y()).toBeGreaterThan(100) + jasmine.clock().tick(250) + fx.step() + expect(called).toBe(true) }) - it('animates matrix', function(done) { + + it('animates matrix', function() { + var ctm, called = false fx.transform({a:0.8, b:0.4, c:-0.15, d:0.7, e: 90.3, f: 27.07}).after(function(){ @@ -563,22 +1458,23 @@ describe('FX', function() { expect(ctm.d).toBeCloseTo(0.7) expect(ctm.e).toBeCloseTo(90.3) expect(ctm.f).toBeCloseTo(27.07) - - done() + called = true }) - setTimeout(function(){ - - var ctm = rect.ctm(); - expect(ctm.a).toBeLessThan(1) - expect(ctm.b).toBeGreaterThan(0) - expect(ctm.c).toBeLessThan(0) - expect(ctm.d).toBeGreaterThan(0) - expect(ctm.e).toBeGreaterThan(0) - expect(ctm.f).toBeGreaterThan(0) - }, 250) - + jasmine.clock().tick(250) + fx.step() + ctm = rect.ctm() + expect(ctm.a).toBeLessThan(1) + expect(ctm.b).toBeGreaterThan(0) + expect(ctm.c).toBeLessThan(0) + expect(ctm.d).toBeGreaterThan(0) + expect(ctm.e).toBeGreaterThan(0) + expect(ctm.f).toBeGreaterThan(0) + + jasmine.clock().tick(250) + fx.step() + expect(called).toBe(true) }) }) diff --git a/src/fx.js b/src/fx.js index 58272c4..0746ba1 100644 --- a/src/fx.js +++ b/src/fx.js @@ -25,7 +25,9 @@ SVG.Situation = SVG.invent({ this.finish = this.start + this.duration this.ease = o.ease - this.loop = false + // 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 = { @@ -71,6 +73,9 @@ SVG.FX = SVG.invent({ 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 } @@ -127,14 +132,14 @@ SVG.FX = SVG.invent({ return this._target } - // returns the position at a given time - , timeToPos: function(timestamp){ + // 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 positon - , posToTime: function(pos){ - return this.situation.duration/this._speed * pos + this.situation.start + // returns the timestamp from a given absolute positon + , absPosToTime: function(absPos){ + return this.situation.duration/this._speed * absPos + this.situation.start } // starts the animationloop @@ -152,7 +157,7 @@ SVG.FX = SVG.invent({ , start: function(){ // dont start if already started if(!this.active && this.situation){ - this.situation.start = +new Date + this.situation.delay + this.situation.start = +new Date + this.situation.delay/this._speed this.situation.finish = this.situation.start + this.situation.duration/this._speed this.initAnimations() @@ -192,7 +197,7 @@ SVG.FX = SVG.invent({ var fn = function(){ if(this.situation instanceof SVG.Situation) - this.initAnimations().at(0) + this.initAnimations().atStart() else if(this.situation instanceof SVG.Delay) this.dequeue() else @@ -283,15 +288,7 @@ SVG.FX = SVG.invent({ this.active = false if(jumpToEnd && this.situation){ - - this.situation.loop = false - - if(this.situation.loops % 2 == 0 && this.situation.reversing){ - this.situation.reversed = true - } - - this.at(1) - + this.atEnd() } this.stopAnimFrame() @@ -308,7 +305,7 @@ SVG.FX = SVG.invent({ var temp = this.situation this.stop() this.situation = temp - this.at(0) + this.atStart() } return this } @@ -325,13 +322,40 @@ SVG.FX = SVG.invent({ 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 + return this.at(this.situation.loop+1, true) + } else 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 - , at: function(pos){ + // if isAbsPos is true, pos is treated as an absolute position + , at: function(pos, isAbsPos){ var durDivSpd = this.situation.duration/this._speed - this.pos = pos - this.situation.start = +new Date - pos * durDivSpd + 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) } @@ -345,7 +369,8 @@ SVG.FX = SVG.invent({ if (speed) { this._speed = speed - return this.at(this.situation.reversed ? 1-this.pos : this.pos) + // 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 } @@ -353,8 +378,9 @@ SVG.FX = SVG.invent({ , loop: function(times, reverse) { var c = this.last() - // store current loop and total loops - c.loop = c.loops = times || true + // store total loops + c.loops = (times != null) ? times : true + c.loop = 0 if(reverse) c.reversing = true return this @@ -372,7 +398,8 @@ SVG.FX = SVG.invent({ , play: function(){ if(!this.paused) return this this.paused = false - return this.at(this.pos) + // We use an absolute position here so that the delay before the animation can be paused + return this.at(this.absPos, true) } /** @@ -477,22 +504,45 @@ SVG.FX = SVG.invent({ */ , step: function(ignoreTime){ - // convert current time to position - if(!ignoreTime) this.pos = this.timeToPos(+new Date) - - if(this.pos >= 1 && (this.situation.loop === true || (typeof this.situation.loop == 'number' && --this.situation.loop))){ + // 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){ - this.situation.reversed = !this.situation.reversed + 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) } - return this.at(this.pos-1) + + } 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 - // correct position - if(this.pos > 1)this.pos = 1 - if(this.pos < 0)this.pos = 0 // apply easing var eased = this.situation.ease(this.pos) -- cgit v1.2.3